#!/bin/bash # Version: 0.3.0-seeder # Copyright (c) 2005-2022: # Darren 'Tadgy' Austin # Licensed under the terms of the GNU General Public License version 3. SCREEN_EXEC="/usr/bin/screen" RTORRENTWRAPPER_EXEC='$HOME/bin/rtorrent-wrapper' DAEMON_EXEC="/usr/bin/daemon" SCREEN_ARGS=('-c' '~/.screenrc-seeding' '-S' 'seeding') RUNUSER="seeder" # Allow configuration in /etc/default to override. # Additional available variables: # ENVIRONMENT=() # Extra environment passed to $SCREEN_EXEC. Must be an array. # RUNUSER="" # The username of the seeding user account. Default: seeder. # SEEDS=() # The seeding sessions to start. Must be an array. # SLAY_DELAY="" # Delay between the SIGTERM and SIGKILL on a 'stop'. Default: 2s. # RESTART_DELAY="" # Delay between stopping and starting on a 'restart'. Default: 2s. [[ -e "/etc/default/${0##*rc.}" ]] && { source "/etc/default/${0##*rc.}" || return 1 2>/dev/null || exit 1; } error() { printf "%s: %s\\n" "${BASH_SOURCE[0]##*/}" "$*" >&2 } checkconfigured() { # This function can be used to perform any pre-start tests; hopfully to insure the daemon # can start correctly, before actually trying to start it. A return value of 0 means the # tests were passed and the daemon should be started. Any other value prevents the # daemon from being started, and an error message will be emitted. id "${RUNUSER:-seeder}" >/dev/null 2>&1 || return 1 return 0 } checkstatus() { # $@ = Name(s) of a seeding session(s) to check. local SEED RET=0 if [[ ! -z "$(su - "${RUNUSER:-seeder}" -c "\"$DAEMON_EXEC\" --list 2>/dev/null")" ]]; then for SEED in "$@"; do if su - "${RUNUSER:-seeder}" -c "\"$DAEMON_EXEC\" --running -n \"seeder-$SEED\""; then printf "%s: %s: %s\\n" "${BASH_SOURCE[0]##*/}" "seed '$SEED'" "running" RET=${RET:+2} else printf "%s: %s: %s\\n" "${BASH_SOURCE[0]##*/}" "seed '$SEED'" "stopped" RET=${RET:-1} fi done else printf "%s: %s\\n" "${BASH_SOURCE[0]##*/}" "all seeds stopped" RET=1 fi return $RET } startdaemon() { # $@ = Name(s) of a seeding session(s) to start. local EXEC SEED ERR RET checkconfigured || { error "seeding not started - pre-start checks failed" return 2 } for EXEC in "$SCREEN_EXEC" "$(su - "${RUNUSER:-seeder}" -c "printf \"%s\" \"$RTORRENTWRAPPER_EXEC\"")"; do [[ ! -e "$EXEC" ]] && { error "$EXEC:" "not found" return 2 } [[ ! -x "$EXEC" ]] && { error "$EXEC:" "not executable" return 2 } done su - "${RUNUSER:-seeder}" -c "${ENVIRONMENT:+declare ${ENVIRONMENT[*]};} \"$SCREEN_EXEC\" ${SCREEN_ARGS[*]} -ls" | grep -iv "dead" >/dev/null || { su - "${RUNUSER:-seeder}" -c "${ENVIRONMENT:+declare ${ENVIRONMENT[*]};} \"$SCREEN_EXEC\" ${SCREEN_ARGS[*]} -d -m" ERR=$? (( ERR != 0 )) && { error "failed to start background screen:" "Error code = $ERR" return 2 } sleep 2 } for SEED in "$@"; do su - "${RUNUSER:-seeder}" -c "${ENVIRONMENT:+declare ${ENVIRONMENT[*]};} \"$SCREEN_EXEC\" ${SCREEN_ARGS[*]} -X screen -t \"$SEED\" \"$RTORRENTWRAPPER_EXEC\" \"$SEED\"" ERR=$? (( ERR != 0 )) && { error "failed to add screen for '$SEED':" "Error code = $ERR" RET=2 continue } sleep 1 done return ${RET:-0} } stopdaemon() { # $@ = Optional name(s) of a seeding session(s) to stop. local SEED for SEED in "$@"; do su - "${RUNUSER:-seeder}" -c "\"$DAEMON_EXEC\" -n \"seeder-$SEED\" --stop 2>/dev/null" done sleep "${SLAY_DELAY:-2}" for SEED in "$@"; do checkstatus >/dev/null && { error "seed '$SEED':" "failed to stop gracefully - slaying" su - "${RUNUSER:-seeder}" -c "\"$DAEMON_EXEC\" -n \"seeder-$SEED\" --signal=kill 2>/dev/null" } done return 0 } for EXEC in "$SCREEN_EXEC" "$DAEMON_EXEC"; do [[ ! -e "$EXEC" ]] && { error "$EXEC:" "not found" return 1 2>/dev/null || exit 1 } [[ ! -x "$EXEC" ]] && { error "$EXEC:" "not executable" return 1 2>/dev/null || exit 1 } done OPT="$1" shift [[ -z "${*:-${SEEDS[@]}}" ]] && { error "no seeding sessions specified in /etc/default/${0##*rc.} or on command line" return 1 2>/dev/null || exit 1 } case "$OPT" in 'start') if checkstatus "${@:-${SEEDS[@]}}" >/dev/null; then error "seeds already running" printf " %s\\n" "Try: ${BASH_SOURCE[0]} status $@" >&2 RET=1 else startdaemon "${@:-${SEEDS[@]}}" RET=$? fi ;; 'stop') checkstatus "${@:-${SEEDS[@]}}" >/dev/null ERR=$? if (( ERR == 2 )) || (( ERR == 0 )); then stopdaemon "${@:-${SEEDS[@]}}" RET=$? elif (( ERR == 1 )); then error "seeds already stopped" printf " %s\\n" "Try: ${BASH_SOURCE[0]} status" >&2 RET=1 else error "unhandled status: $ERR" RET=1 break fi ;; 'restart') checkstatus "${@:-${SEEDS[@]}}" >/dev/null ERR=$? if (( ERR == 2 )) || (( ERR == 0 )); then stopdaemon "${@:-${SEEDS[@]}}" sleep "${RESTART_DELAY:-2}" startdaemon "${@:-${SEEDS[@]}}" RET=$? elif (( ERR == 1 )); then startdaemon "${@:-${SEEDS[@]}}" RET=$? else error "unhandled status: $ERR" RET=1 break fi ;; 'status') checkstatus "${@:-${SEEDS[@]}}" RET=$? ;; *) printf "%s\\n" "Usage: ${BASH_SOURCE[0]} [seeding session] ..." >&2 RET=1 ;; esac return $RET 2>/dev/null || exit $RET