diff --git a/README.md b/README.md index 03b9c44..f55ed25 100644 --- a/README.md +++ b/README.md @@ -11,8 +11,9 @@ support is a documented future addition — see [`agent/windows/README.md`](agen - `server/` — Express + TypeScript API, SQLite (via libSQL) storage, Authentik OIDC login. - `web/` — React + Vite single-page app, served as static files by the Express server. -- `agent/linux/` — `report-tasks.sh` (collects cron + systemd timer data) and - `install.sh` (installs it as a systemd timer on a target server). +- `agent/linux/` — `report-tasks.sh` (collects cron + systemd timer data), + `install.sh` (installs it as a systemd timer on a target server), and + `uninstall.sh` (removes it). Data model, API contract, and sync behavior are documented in code: [`server/src/db/schema.ts`](server/src/db/schema.ts), @@ -64,11 +65,13 @@ automatically on server start. 1. Sign in, go to **Servers**, and add a server. The generated API token is shown once — copy it. -2. On the target Linux server (as root): +2. On the target Linux server, as root. If you're not already root, put + `sudo` right after the pipe so it elevates `bash`, not `curl` — `sudo curl + ... | bash` would still fail the root check: ```sh curl -fsSL https://schedule.example.lan/agent/linux/install.sh | \ - API_URL=https://schedule.example.lan API_TOKEN=stm_xxx bash + sudo API_URL=https://schedule.example.lan API_TOKEN=stm_xxx bash ``` This installs `report-tasks.sh` to `/usr/local/bin`, writes the API @@ -76,7 +79,10 @@ automatically on server start. `schedule-task-manager-agent.timer` systemd timer that reports every 15 minutes (override with `INTERVAL_MINUTES=5` before the `bash` above). - Requires `curl`, `jq`, and `systemd` on the target server. + Requires `curl`, `jq`, and `systemd` on the target server — if either + `curl` or `jq` is missing, both this script and `report-tasks.sh` print + the right install command for the server's package manager (apt, dnf, + yum, apk, pacman, or zypper) and exit. 3. Tasks appear on the Dashboard, grouped by server and then by schedule type, within one reporting interval. @@ -88,7 +94,7 @@ hidden by default — toggle "Show stale/missing tasks" to see them. To remove the agent from a server, run: ```sh -curl -fsSL https://schedule.example.lan/agent/linux/uninstall.sh | bash +curl -fsSL https://schedule.example.lan/agent/linux/uninstall.sh | sudo bash ``` This stops and deletes the systemd timer/service, the installed script, and diff --git a/agent/linux/install.sh b/agent/linux/install.sh index 36aa887..d30f42b 100644 --- a/agent/linux/install.sh +++ b/agent/linux/install.sh @@ -1,9 +1,10 @@ #!/usr/bin/env bash # Installs the Schedule Task Manager agent as a systemd timer. # -# Usage: +# Usage (must run as root — if not already root, put sudo right after the +# pipe so it applies to bash, not to curl): # curl -fsSL https://schedule.example.lan/agent/linux/install.sh | \ -# API_URL=https://schedule.example.lan API_TOKEN=stm_xxx bash +# sudo API_URL=https://schedule.example.lan API_TOKEN=stm_xxx bash # set -euo pipefail @@ -17,16 +18,41 @@ INTERVAL_MINUTES="${INTERVAL_MINUTES:-15}" if [[ "$EUID" -ne 0 ]]; then echo "This installer must be run as root (it installs a systemd timer)." >&2 + echo "If you're piping from curl, put sudo right after the pipe so it elevates bash, not curl:" >&2 + echo " curl -fsSL ... | sudo API_URL=... API_TOKEN=... bash" >&2 exit 1 fi -for bin in curl jq systemctl; do +suggest_package_install() { + local pkg="$1" + if command -v apt-get >/dev/null 2>&1; then + echo " sudo apt-get update && sudo apt-get install -y $pkg" + elif command -v dnf >/dev/null 2>&1; then + echo " sudo dnf install -y $pkg" + elif command -v yum >/dev/null 2>&1; then + echo " sudo yum install -y $pkg" + elif command -v apk >/dev/null 2>&1; then + echo " sudo apk add $pkg" + elif command -v pacman >/dev/null 2>&1; then + echo " sudo pacman -S $pkg" + elif command -v zypper >/dev/null 2>&1; then + echo " sudo zypper install -y $pkg" + fi +} + +for bin in curl jq; do if ! command -v "$bin" >/dev/null 2>&1; then - echo "Required dependency '$bin' is not installed." >&2 + echo "Required dependency '$bin' is not installed. Try:" >&2 + suggest_package_install "$bin" >&2 exit 1 fi done +if ! command -v systemctl >/dev/null 2>&1; then + echo "systemctl was not found — this installer requires a systemd-based Linux distribution." >&2 + exit 1 +fi + echo "Installing Schedule Task Manager agent from $API_URL ..." curl -fsSL "$API_URL/agent/linux/report-tasks.sh" -o "$INSTALL_DIR/schedule-task-manager-agent.sh" @@ -69,4 +95,4 @@ echo "Installed. Running an initial report now..." "$INSTALL_DIR/schedule-task-manager-agent.sh" echo "Done. The agent reports every ${INTERVAL_MINUTES} minute(s) via the 'schedule-task-manager-agent.timer' systemd timer." -echo "To remove it later: curl -fsSL $API_URL/agent/linux/uninstall.sh | bash" +echo "To remove it later: curl -fsSL $API_URL/agent/linux/uninstall.sh | sudo bash" diff --git a/agent/linux/report-tasks.sh b/agent/linux/report-tasks.sh index 0b2c38a..8a94e31 100644 --- a/agent/linux/report-tasks.sh +++ b/agent/linux/report-tasks.sh @@ -23,9 +23,27 @@ if [[ -z "$API_URL" || -z "$API_TOKEN" ]]; then exit 1 fi +suggest_package_install() { + local pkg="$1" + if command -v apt-get >/dev/null 2>&1; then + echo " sudo apt-get update && sudo apt-get install -y $pkg" + elif command -v dnf >/dev/null 2>&1; then + echo " sudo dnf install -y $pkg" + elif command -v yum >/dev/null 2>&1; then + echo " sudo yum install -y $pkg" + elif command -v apk >/dev/null 2>&1; then + echo " sudo apk add $pkg" + elif command -v pacman >/dev/null 2>&1; then + echo " sudo pacman -S $pkg" + elif command -v zypper >/dev/null 2>&1; then + echo " sudo zypper install -y $pkg" + fi +} + for bin in curl jq; do if ! command -v "$bin" >/dev/null 2>&1; then - echo "Required dependency '$bin' is not installed." >&2 + echo "Required dependency '$bin' is not installed. Try:" >&2 + suggest_package_install "$bin" >&2 exit 1 fi done diff --git a/agent/linux/uninstall.sh b/agent/linux/uninstall.sh index 474aa51..7146d38 100644 --- a/agent/linux/uninstall.sh +++ b/agent/linux/uninstall.sh @@ -2,8 +2,9 @@ # Removes the Schedule Task Manager agent from this server: stops and deletes # its systemd timer/service, the installed script, and its credentials file. # -# Usage: -# curl -fsSL https://schedule.example.lan/agent/linux/uninstall.sh | bash +# Usage (must run as root — if not already root, put sudo right after the +# pipe so it applies to bash, not to curl): +# curl -fsSL https://schedule.example.lan/agent/linux/uninstall.sh | sudo bash # set -euo pipefail @@ -13,6 +14,8 @@ SYSTEMD_DIR="/etc/systemd/system" if [[ "$EUID" -ne 0 ]]; then echo "This uninstaller must be run as root." >&2 + echo "If you're piping from curl, put sudo right after the pipe so it elevates bash, not curl:" >&2 + echo " curl -fsSL ... | sudo bash" >&2 exit 1 fi diff --git a/web/src/pages/ServersAdmin.tsx b/web/src/pages/ServersAdmin.tsx index 554ebef..a228117 100644 --- a/web/src/pages/ServersAdmin.tsx +++ b/web/src/pages/ServersAdmin.tsx @@ -89,8 +89,8 @@ export default function ServersAdmin() {
This token is only shown once — copy it now.
{newToken.token}
- Install the agent on the server:
-{`curl -fsSL ${window.location.origin}/agent/linux/install.sh | API_URL=${window.location.origin} API_TOKEN=${newToken.token} bash`}
+ Install the agent on the server (run as root — put sudo right after the pipe, not before curl):
+{`curl -fsSL ${window.location.origin}/agent/linux/install.sh | sudo API_URL=${window.location.origin} API_TOKEN=${newToken.token} bash`}
@@ -100,8 +100,8 @@ export default function ServersAdmin() {
{uninstallFor && (
Run this on the server to stop and remove the agent (its entry here is kept):
-{`curl -fsSL ${window.location.origin}/agent/linux/uninstall.sh | bash`}
+ Run this on the server as root to stop and remove the agent (its entry here is kept):
+{`curl -fsSL ${window.location.origin}/agent/linux/uninstall.sh | sudo bash`}