From da4147a8ca2ed92556eb56061a63e0cccb1984ce Mon Sep 17 00:00:00 2001 From: salmon Date: Sun, 9 Aug 2026 23:28:12 +0200 Subject: Add FreeBSD rc.d service, install completions only for installed shells --- README.md | 14 +++++++-- install.sh | 103 +++++++++++++++++++++++++++++++++++++----------------------- rsplayer.rc | 72 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 147 insertions(+), 42 deletions(-) create mode 100755 rsplayer.rc diff --git a/README.md b/README.md index 9cc85fb..ef8ebaf 100644 --- a/README.md +++ b/README.md @@ -31,9 +31,19 @@ cargo build --release install -m 755 target/release/rsp /usr/local/bin/rsp ``` +`install.sh` does all of the above on FreeBSD and additionally installs an rc.d +service. Enable it with: +``` +sudo sysrc rsplayer_enable=YES +sudo sysrc rsplayer_user=salmon # the user that runs the rsp CLI +sudo service rsplayer start +``` + FreeBSD notes: -- There is no systemd: run `rsp start-daemon` manually, from your login shell, - or via an rc.d script. `start-daemon`/`stop-daemon`/`restart` behave the same. +- There is no systemd: the rc.d service (rsplayer.rc) runs `rsp start-daemon` + as rsplayer_user via daemon(8); `service rsplayer status`/`stop`/`restart` + work as expected. You can also run `rsp start-daemon` manually from your + login shell — `start-daemon`/`stop-daemon`/`restart` behave the same. - XDG_RUNTIME_DIR is not set by default on FreeBSD, so the daemon socket and PID file fall back to /tmp/rsplayer/. Set XDG_RUNTIME_DIR in your rc.d script or shell profile if you prefer a private runtime dir. diff --git a/install.sh b/install.sh index 25a1f52..9280507 100755 --- a/install.sh +++ b/install.sh @@ -19,52 +19,75 @@ echo "Stopping existing daemon..." systemctl --user stop rsplayer 2>/dev/null || true rsp stop-daemon 2>/dev/null || true -echo "Installing to /usr/bin/rsp..." -sudo install target/release/rsp /usr/bin/rsp +if [ "$(uname -s)" = "FreeBSD" ]; then + BIN="/usr/local/bin/rsp" + echo "Installing to ${BIN}..." + sudo install target/release/rsp "${BIN}" -echo "" -echo "Installing systemd service..." -mkdir -p ~/.config/systemd/user -cp rsplayer.service ~/.config/systemd/user/ - -if systemctl --user is-active default.target &>/dev/null; then - systemctl --user daemon-reload - echo "Activating the daemon…" - systemctl --user restart --now rsplayer.service + echo "" + echo "Installing rc.d service..." + sudo install -m 755 rsplayer.rc /usr/local/etc/rc.d/rsplayer + echo "Service installed. Enable it with:" + echo " sudo sysrc rsplayer_enable=YES" + echo " sudo sysrc rsplayer_user=$USER # the user that runs the rsp CLI" + echo " sudo service rsplayer start" else - echo "Service installed but not enabled (no user session running)" - echo "To enable later when logged in: systemctl --user enable rsplayer" + echo "Installing to /usr/bin/rsp..." + sudo install target/release/rsp /usr/bin/rsp + + echo "" + echo "Installing systemd service..." + mkdir -p ~/.config/systemd/user + cp rsplayer.service ~/.config/systemd/user/ + + if systemctl --user is-active default.target &>/dev/null; then + systemctl --user daemon-reload + echo "Activating the daemon…" + systemctl --user restart --now rsplayer.service + else + echo "Service installed but not enabled (no user session running)" + echo "To enable later when logged in: systemctl --user enable rsplayer" + fi fi echo "" -echo "Installing shell completions..." -shell_name=$(basename "${SHELL:-}") -case "$shell_name" in - bash) - dest="${XDG_DATA_HOME:-$HOME/.local/share}/bash-completion/completions" - mkdir -p "$dest" - cp completions/rsp.bash "$dest/rsp" - echo " → Bash completions: $dest/rsp" - ;; - zsh) +echo "Installing shell completions (only for shells that are installed)..." +found=0 + +if command -v bash >/dev/null 2>&1; then + dest="${XDG_DATA_HOME:-$HOME/.local/share}/bash-completion/completions" + mkdir -p "$dest" + "${BIN}" completions bash > "$dest/rsp" + echo " → Bash completions: $dest/rsp" + found=1 +fi + +if command -v zsh >/dev/null 2>&1; then + if [ "$(uname -s)" = "FreeBSD" ]; then + dest="/usr/local/share/zsh/site-functions/_rsp" + else dest="/usr/share/zsh/site-functions/_rsp" - tmpfile=$(mktemp) - /usr/bin/rsp completions zsh > "$tmpfile" - cat completions/_rsp >> "$tmpfile" - sudo cp "$tmpfile" "$dest" - rm -f "$tmpfile" - echo " → Zsh completions: $dest" - ;; - fish) - dest="$HOME/.config/fish/completions/rsp.fish" - mkdir -p "$(dirname "$dest")" - /usr/bin/rsp completions fish > "$dest" - echo " → Fish completions: $dest" - ;; - *) - echo " → Unrecognized shell '$shell_name'; generate with: rsp completions " - ;; -esac + fi + tmpfile=$(mktemp) + "${BIN}" completions zsh > "$tmpfile" + cat completions/_rsp >> "$tmpfile" + sudo cp "$tmpfile" "$dest" + rm -f "$tmpfile" + echo " → Zsh completions: $dest" + found=1 +fi + +if command -v fish >/dev/null 2>&1; then + dest="$HOME/.config/fish/completions/rsp.fish" + mkdir -p "$(dirname "$dest")" + "${BIN}" completions fish > "$dest" + echo " → Fish completions: $dest" + found=1 +fi + +if [ "$found" -eq 0 ]; then + echo " → No supported shell (bash/zsh/fish) found; generate manually with: rsp completions " +fi echo "" echo "Done!" diff --git a/rsplayer.rc b/rsplayer.rc new file mode 100755 index 0000000..33a46b9 --- /dev/null +++ b/rsplayer.rc @@ -0,0 +1,72 @@ +#!/bin/sh + +# This file is a part of RSPlayer +# Licensed under the GPL-3.0 License +# Copyright (C) 2026 Salmonik @ salmonik@tuta.io +# https://codeberg.org/salmonik/RSPlayer +# +# FreeBSD rc.d service for RSPlayer. +# +# Add the following to /etc/rc.conf to enable the service: +# +# rsplayer_enable="YES" +# rsplayer_user="salmon" # user to run the daemon as (NOT root) +# rsplayer_group="wheel" # group of that user +# +# rsplayer_user must be a regular user: the daemon socket and PID file live +# in rsplayer_runtime_dir (default /tmp/rsplayer, matching the daemon's +# fallback when XDG_RUNTIME_DIR is unset) and the `rsp` CLI connects to that +# socket, so the daemon and the CLI must run as the same user. +# +# Optional: rsplayer_runtime_dir="/tmp/rsplayer" + +# PROVIDE: rsplayer +# REQUIRE: LOGIN dbus +# KEYWORD: shutdown + +. /etc/rc.subr + +name="rsplayer" +rcvar=rsplayer_enable + +load_rc_config $name + +: ${rsplayer_user:="root"} +: ${rsplayer_group:="wheel"} +: ${rsplayer_runtime_dir:="/tmp/rsplayer"} + +command="/usr/local/bin/rsp" +procname="${command}" +pidfile="${rsplayer_runtime_dir}/rsplayer.pid" + +start_cmd="rsplayer_start" +stop_cmd="rsplayer_stop" + +rsplayer_start() +{ + if [ -s "${pidfile}" ] && kill -0 "$(cat "${pidfile}")" 2>/dev/null; then + echo "rsplayer is already running (PID $(cat "${pidfile}"))." + return 0 + fi + + if [ "${rsplayer_user}" = "root" ]; then + echo "WARNING: rsplayer_user is \"root\"; the rsp CLI will not be able to" + echo " connect to the daemon socket. Set rsplayer_user in rc.conf." + fi + + install -d -o "${rsplayer_user}" -g "${rsplayer_group}" -m 700 "${rsplayer_runtime_dir}" + echo "Starting rsplayer as ${rsplayer_user}." + /usr/sbin/daemon -u "${rsplayer_user}" -o "${rsplayer_runtime_dir}/rsplayer.log" \ + "${command}" start-daemon +} + +rsplayer_stop() +{ + echo "Stopping rsplayer." + if ! su -m "${rsplayer_user}" -c "${command} stop-daemon"; then + echo "rsplayer is not running; cleaning up stale files." + rm -f "${pidfile}" "${rsplayer_runtime_dir}/daemon.sock" + fi +} + +run_rc_command "$1" -- cgit v1.2.3