#!/sbin/runscript # ====================[ rtorrent-screen ]==================== # [ Time-stamp: "2008-12-01 15:36:24 leycec" ] # # A Gentoo-specific "/etc/init.d"-style daemonization script for rtorrent. # # rtorrent is "...a BitTorrent client for ncurses, using the libtorrent library. # The client and library is written in C++ with emphasis on speed and # efficiency, while delivering equivalent features to those found in GUI-based # clients in an ncurses client." # # rtorrent has, unfortunately, no formal support for daemonization; that is, # rtorrent cannot be backgrounded or foregrounded to and fro the command-line. # Once you start it, it's always there: consuming one virtual terminal # (console) with its murky glimmerings. # # rtorrent, therefore, cannot be run as a background daemon on system startup. # Unless we apply some external trickery! The external applications "screen" and # "dtach" are two such external trickeries. Both allow applications to be # detached from the terminal from which they were run and then later # reattached to any terminal elsewhere. "dtach" is significantly smaller and # simpler than "screen"; however, "screen" is significantly more commonplace # and tends to come installed (and pre-configured) on more machines. In the # pragmatic interest of minimizing script prerequisites, therefore, this script # leverages "screen". # # --------------------( DEPENDENCIES )-------------------- # This script depends, of course, on Gentoo, "rtorrent", and "screen". The last # two dependencies may (as this is a Gentoo-specific script) be downloaded with # the Gentoo package manager, Portage, like so: # # # Install "rtorrent" and "screen". # emerge rtorrent screen # for emerge users; or # paludis -i rtorrent screen # for Paludis users # # --------------------( INSTALLATION )-------------------- # This script behaves like any Gentoo-specific, "/etc/init.d/"-style script; # that is, it depends upon the Gentoo-specific "start-stop-daemon" executable # and service-oriented architecture to properly daemonize rtorrent. # # # Download and move this script to "/etc/init.d/". # sudo mv rtorrent-screen /etc/init.d/ # chmod ugo+rx /etc/init.d/rtorrent-screen # # # Make this script executable. # sudo chmod ugo+rx /etc/init.d/rtorrent-screen # # # !!!! IMPORTANT !!!! # # Edit this script, changing the CONFIGURATION variables in this script to # # suite the account-specific needs of your local system. In particular, you # # will want to change the "USER" variable to that of the non-root user under # # which you intend to run the rtorrent daemon. # sudo emacs /etc/init.d/rtorrent-screen # or, # sudo nano /etc/init.d/rtorrent-screen # or, # sudo vi /etc/init.d/rtorrent-screen # # !!!! IMPORTANT !!!! end # # # Optionally make the phrase "rtorrent" a shell alias for foregrounding # # the currently backgrounded "rtorrent" process. This permits you to open # # and view your current rtorrent by typing "rtorrent" at the command line # # when logged in as the user specified by the "USER" variable, below. # echo 'alias rtorrent="screen -r rtorrent"' >> ~/.bashrc # for Bash; or, # echo 'alias rtorrent="screen -r rtorrent"' >> ~/.zshrc # for ZSH # # # Optionally add this script to the default Gentoo runlevel. This starts # # rtorrent on system startup, automatically. # sudo rc-update add rtorrent-screen default # # # Optionally add this script to the system crontab. This starts and stops # # rtorrent on a time-based schedule, which could be helpful for users # # having limited (or expensive!) bandwidth during certain hours of the day. # # # # In this example, we schedule rtorrent to run from 2:00AM to 8:00AM daily. # sudo emacs /etc/crontab # or, # sudo nano /etc/crontab # or, # sudo vi /etc/crontab # # # Now add or edit the following lines to the above file. (Naturally, # # remove the prefixing comment character '#' from each line!) # minute (0-59, where "*/n" means every n minutes), # | hour (0-23, where 0 is midnight and 13 is 1:00PM), # | | day of the month (1-31, where "*" means every day), # | | | month of the year (1-12, where "*" means every month), # | | | | day of the week (0-6, where 0 is Sunday and "*" means every day). # | | | | | user # | | | | | | command # | | | | | | | # 0 2 * * * root /etc/init.d/rtorrent-screen restart 1>> /var/log/rtorrent/current 2>&1 # 0 8 * * * root /etc/init.d/rtorrent-screen stop 1>> /var/log/rtorrent/current 2>&1 # # # Now close the file. Great! Lastly, ensure the above logging directory # # exists, and restart cron. # sudo mkdir -p /var/log/rtorrent/ # sudo /etc/init.d/vixie-cron restart # # --------------------( USAGE )-------------------- # This script behaves like any Gentoo-specific, "/etc/init.d/"-style script. # Let's examine a few examples. # # # Start rtorrent as a backgrounded "daemon." Note we call "restart" rather # # than "start," since the former implicitly stops any running rtorrent # # before it starts. # # # # This is especially helpful when attempting to restart rtorrent after # # having manually stopping rtorrent from within rtorrent. In this case, the # # services subsystem in Gentoo (i.e., the "start-stop-daemon" command) will # # be oddly unaware that rtorrent has actually been stopped and will thus # # probably issue an error when calling "start" rather than "restart." # # # # (Trust us on this one.) # sudo /etc/init.d/rtorrent-screen restart # # # Foreground rtorrent. This must be done as the user specified by the "USER" # # variable in the CONFIGURATION section of this script, below. If you type # # this often (and you probably will), you might want to make a shell alias. # # (See "INSTALLATION," above.) # screen -r rtorrent # # # Re-background rtorrent after foreground-ing it by typing in the # # foreground-ed rtorrent window where "" is the specific # # command for detaching (backgrounding) the current screen session # # (rtorrent). Simple, no? (No - not really. We know.) # # # # Stop rtorrent. (Alternatively, type or in the # # foreground-ed rtorrent window.) # sudo /etc/init.d/rtorrent-screen stop # # --------------------( HISTORY )-------------------- # This script is nominally inspired by several others that, for one # justification or another, were found severely wanting; these are: # # http://cowpowered.wordpress.com/2007/09/17/etcinitdrtorrent/ # http://libtorrent.rakshasa.no/attachment/wiki/RTorrentCommonTasks/rtorrentInit.sh # http://libtorrent.rakshasa.no/attachment/wiki/RTorrentCommonTasks/rtorrentInit.bash # # This script depends on "screen". If, on the other hand, you'd like a similar # script that depends on "dtach", you might like: # # http://forums.gentoo.org/viewtopic-p-4544759.html?sid=2b7c29fe6fa3ab163ccf824b69956d7b # # --------------------( TODO )-------------------- # * Provide dynamic support for both screen and dtach, according to whichever # is installed at runtime. This in turn suggests this file should be renamed # "rtorrent-daemon" or perhaps simply "rtorrentd". # * Extract the "CONFIGURATION" setting into a proper # "/etc/conf.d/rtorrentd" file. # * Conditionally enable "einfo" logging only when some configuration variable # (say, "VERBOSE") is also enabled. # * Perform automatic logging to a file when operating as a cronjob. (See # "hg-cohere" for assistance, eh?) # # --------------------( CHANGELOG )-------------------- # 2010-01-22 Cecil Curry # * Substantially improved all facets of this script, from documentation to # implementation. I'm afraid I can't enumerate what exactly changed; so let us # simply say: "Why, everything!" # * Documented the configuration of this script as a crontab-scheduled cronjob # in the "INSTALLATION" section, above. # * Improved logging particularly when run as such a cronjob. # * Improved the "stop" function so as to gracefully recover from failure: # particularly, the failure of "start-stop-daemon" to stop rtorrent. It's # particularly crucial that this be done for those of us relegated to the # second world ("I'm glaring at you, New Zealand."), who pay for bandwidth and # thus must ensure rtorrent be stopped when we schedule it to be stopped via a # cronjob. ("O-h, unlimited bandwidth: how I miss thee.") # * Removed the spurious "restart" function. (It did nothing, before. Uhps.) # * Added the incidental "echo_rtorrent_start_error" function for emitting # rtorrent startup errors in a fairly robust fashion. # # 2010-01-04 Cecil Curry # * Improved the "depend" function so as to properly depend on networking and # logging services in OpenRC-compliant fashion. # * Improved documentation and comments, slightly. # # 2009-08-07 Cecil Curry # * Improved rtorrent shutdown by sending a running rtorrent session the # standard INT (i.e., interrupt) signal on performing the "stop" action. This # insists rtorrent shutdown cleanly, and should circumvent hash checking on # the next rtorrent session. # # 2007-12-01 Cecil Curry # * Revised. This script now uses screen's built-in flow control mechanism, via # the "-fa" option, to manage rtorrent's conflicting key bindings, rather than # an external script. (This is substantially simpler, of course!) # # 2007-03-01 Cecil Curry # * Created. # ....................{ CONFIGURATION }.................... # The user who will own the rtorrent process, when run. (Ideally, this should be # the user who intends to foreground and view rtorrent.) # # The default is, probably, not fine. You should change this! USER=leycec # The absolute path to the rtorrent binary. (The default is probably fine.) RTORRENT=$(which rtorrent 2>/dev/null) # The absolute path to the screen binary. (The default is probably fine.) SCREEN=$(which screen 2>/dev/null) # The name of the screen session under which rtorrent will be run. (The default # is probably fine.) SCREEN_SESSION=rtorrent # The file persisting the current PID for the current rtorrent process. (The # default is probably fine.) PIDFILE="/var/run/rtorrent.$USER.pid" # ....................{ CRON }.................... # Ascertain whether we are running as a cronjob and, if so, alter output # appropriately. In particular, print the current date as a timestamp prior to # printing all other output. # # Adapted from this itworld.com article, "Am I being run by cron?": # http://www.itworld.com/Comp/3380/nls_unixcron041209/index.html CRON_PIDS=$(pgrep -x cron) GRANDPARENT_PID=$(ps -eo ppid,pid= | grep " ${PPID}$" | awk '{print $1}') if echo "$CRON_PIDS" | grep "$GRANDPARENT_PID" 1>/dev/null 2>&1; then IS_CRONJOB="1" echo "["$(date +'%F %T')"] rtorrent-screen says:" else IS_CRONJOB="" fi # ....................{ DEPENDENCIES }.................... if [ -z "$RTORRENT" ]; then eerror "rtorrent not installed in system PATH!" einfo "PATH=\"$PATH\"" return 1 fi if [ -z "$SCREEN" ]; then eerror "screen not installed in system PATH!" einfo "PATH=\"$PATH\"" return 1 fi # ....................{ IMPLEMENTATION }.................... depend() { need net use dns logger } start() { # Wipe all outstanding "dead screens" for the target user. su -c "$SCREEN -wipe" "$USER" 1>/dev/null 2>&1 # If there are other rtorrent screen sessions running, warn but continue on! SCREEN_SESSION_COUNT=$(su -c \ "$SCREEN -list | grep --count '\.${SCREEN_SESSION}[[:space:]]'" "$USER") [ "$SCREEN_SESSION_COUNT" -eq 0 ] || ewarn "$SCREEN_SESSION_COUNT rtorrent screen session(s) already started." # Start rtorrent as a "start-stop-daemon"-managed screen session. # # Note: screen's argument passing engine is... erroneous. Order is important # in the list of arguments, below. ebegin "Starting rtorrent" start-stop-daemon --start --user "$USER" --chuid "$USER" --pidfile "$PIDFILE" \ --env HOME="/home/$USER" \ --exec "$SCREEN" -- -d -m -fa -U -S "$SCREEN_SESSION" "$RTORRENT" # If rtorrent fails to start, fail and emit the cause thereof. RTORRENT_RETURN_CODE=$? if [ $RTORRENT_RETURN_CODE -ne 0 ]; then eend $RTORRENT_RETURN_CODE "rtorrent could not be started!" echo_rtorrent_start_error return $RTORRENT_RETURN_CODE fi # "start-stop-daemon" makes a PID file having the (blatantly) wrong screen PID, # when passing the "--make-pidfile" option. So we (cleverly) grep for the # right screen PID and make a PID file having that PID, instead. SCREEN_PID=$(su -c "$SCREEN -list" "$USER" | \ grep --max-count=1 "\.${SCREEN_SESSION}[[:space:]]" | \ sed "s/^[[:space:]]*\([0-9][0-9]*\)\.${SCREEN_SESSION}[[:space:]].*$/\1/" -) # If "screen" lists no rtorrent session for the user under which # "start-stop-daemon" should have started such an rtorrent session, fail. if echo "$SCREEN_PID" | grep --quiet "^[[:space:]]*$"; then eend 1 "screen lists no rtorrent sessions!" echo_rtorrent_start_error return 1 fi # Unfortunately, "$SCREEN_PID" gives the process ID of the screen process # hosting this rtorrent process rather than that rtorrent process itself. As # halting this rtorrent process requires we have its ID rather than screen's # ID, we now grep for that. # # Note, however, that this rtorrent process may not yet have actually started # up. Therefore, we wait a reasonable (!) number of seconds for it to start # up; if it fails to do so, fail. START_TIME=$(date +"%s") # current time in seconds since the Epoch CLOSE_TIME=$(expr $START_TIME + 8) # current time plus eight seconds while [ $(date +"%s") -le $CLOSE_TIME ]; do RTORRENT_PID=$(pgrep -P "$SCREEN_PID" -fx "$RTORRENT") # If an rtorrent process is found, break out of this while loop; # otherwise, sleep for one second and try again. [ $? -eq 0 ] && break sleep 1s done # If rtorrent is not running under the above user and screen process, then # something went ghastly wrong! We fail with error. if [ -z "$RTORRENT_PID" ]; then eend 1 "screen not running any rtorrent processes!" echo_rtorrent_start_error return 1 fi # Store the rtorrent process ID grepped above into an external "$PIDFILE" for # later use in shutting down the process. echo "$RTORRENT_PID" > "$PIDFILE" eend $? } stop() { ebegin "Stopping rtorrent" # Is rtorrent running? RTORRENT_PIDS=$(pgrep -d ' ' -u "$USER" -fx "$RTORRENT") RTORRENT_RUNNING=$? # Is the rtorrent PID file found? If so, what are its contents? $(test -f "$PIDFILE") RTORRENT_PIDFILE_FOUND=$? if [ $RTORRENT_PIDFILE_FOUND -eq 0 ]; then RTORRENT_PID=$(cat "$PIDFILE") #FIXME: Should "start-stop-daemon" handle this, or should I? # Is the rtorrent PID stored in the above file actually the PID of a running # rtorrent process? (If not, fail!) # if ! echo "$RTORRENT_PIDS" | grep "$RTORRENT_PID" 1>/dev/null 2>&1; then # eend 1 "rtorrent (PID $RTORRENT_PID) not running for user $USER!" # return 1 # fi else RTORRENT_PID="???" fi # Send the rtorrent session started above signal 2 (i.e., INT: interrupt) to # persuade rtorrent to shutdown cleanly. Calling this same command without # passing "--signal 2" causes rtorrent to shutdown non-cleanly, which # customarily causes the next rtorrent session to hash-check the integrity of # all incomplete torrents. That, in turn, can be a very time consuming # process: one hour for each torrent gigabyte is a very long time indeed! # # Lastly note that this waits 10 seconds (e.g., "--retry 10") for the rtorrent # session to shutdown cleanly before sending a definitive KILL signal. Since # rtorrent documentation insists that rtorrent always shuts down cleanly # within 5 seconds of an initial request, this should be more than long enough. SIGINT=2 WAIT_SECONDS=10 start-stop-daemon --stop --signal $SIGINT --pidfile "$PIDFILE" \ --retry $WAIT_SECONDS RETURN_CODE=$? # If rtorrent is not currently running, fail with a more specific error. if [ $RTORRENT_RUNNING -ne 0 ]; then eerror "rtorrent not currently running!" # Otherwise, it is currently running. else # "start-stop-daemon" returns success when rtorrent is currently running # but "$PIDFILE" does not exist; it should return failure, since it will # have been unable to shutdown rtorrent in this case. # # We force failure in this case so as to gracefully recover later. if [ $RTORRENT_PIDFILE_FOUND -ne 0 ]; then RETURN_CODE=1; fi # If "start-stop-daemon" returned failure, attempt to gracefully recover. if [ $RETURN_CODE -ne 0 ]; then # Print an appropriate error message. if [ $RTORRENT_PIDFILE_FOUND -ne 0 ]; then eend $RETURN_CODE "rtorrent could not be stopped, since "\ "PID file '$PIDFILE' not found!" else eend $RETURN_CODE "rtorrent (PID $RTORRENT_PID) could not be stopped!" fi # If the above "start-stop-daemon" command failed it was probably due to the # $PIDFILE having the PID for the previously started rtorrent process having # since been deleted, renamed, or otherwise moved. In that case, killing all # rtorrent processes owned by the target user should have the same effect. ebegin "Stopping rtorrent forcefully..." pkill -signal "$SIGINT" -u "$USER" -fx "$RTORRENT" # Wait several seconds to ensure rtorrent shutdown cleanly. sleep ${WAIT_SECONDS}s # Grep the process list to check rtorrent shutdown cleanly. pgrep -u "$USER" -fx "$RTORRENT" RETURN_CODE=$? fi fi eend $RETURN_CODE return $RETURN_CODE } # ....................{ UTILITIES }.................... # If rtorrent failed to run for any reason other than it is already running # under an existing screen session, print that reason by re-running rtorrent as # the target user. As we're (mostly) guaranteed of its failing with error, this # prints the cause of the error to stdout. echo_rtorrent_start_error() { if [ -n "$SCREEN_PID" ]; then einfo "rtorrent (for user ${USER} and screen PID ${SCREEN_PID}) fails with:" else einfo "rtorrent (for user ${USER}) fails with:" fi [ -z "$SCREEN_SESSION_COUNT" -o "$SCREEN_SESSION_COUNT" -eq 0 ] && \ su -c "$RTORRENT" "$USER" } # --------------------( COPYRIGHT AND LICENSE )-------------------- # The information below applies to everything in this distribution, # except where noted. # # Copyleft 2007, 2008, 2009 by Cecil Curry. # # http://www.raiazome.com # # This file is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This file is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this file; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.