Bittorrent seed (rtorrent)¶
Bittorrent is provided by two components - one is the tracker that brings peers together and the bittorent client that actually provides the files to other peers.
At TDF we’re using a custom build of the bnbt tracker software, and a stock version of rtorrent. This part describes the client, rtorrent.
Requirements¶
- Mirrorbrain/Master-mirror (needs access to all files that are served to the public)
- Bittorrent tracker (bnbt)
- Open Port(s) - configurable, at least one, currently uses 58185 (tcp & udp)
Installation¶
to install the client/seed (rtorrent):
sudo apt-get install rtorrent
Create directories for it to place it files:
sudo mkdir -p /srv/rtorrent sudo mkdir /srv/rtorrent/{config,download,session,watch}
Create configuration file:
sudo echo > /srv/rtorrent/config/rtorrent.rc <<EOF ## ## basic settings ## # change working directory of the process(chdir) - already done by initscript #cwd = /srv/rtorrent # default target-dir (where to save/look for the actual download) directory = /srv/rtorrent/download # session dir (one per instance!) session = /srv/rtorrent/session # port to use port_range = 58185-58185 ## ## misc features ## # we use our own tracker, and we want it to be used, so no dht and no peer-exchange dht = disable peer_exchange = no # allow encypted requests, but don't encrypt ourselves unless it fails without encryption = allow_incoming,enable_retry,prefer_plaintext # Watch a directory for new torrents, and stop those that have been deleted. # schedule = id, initial delay, schedule interval, command schedule = watch_directory,25,60,"load_start=/srv/rtorrent/watch/*.torrent" # watch another directory with different downlaod-directory #schedule = watch_another,15,60,"load_start=/srv/rtorrent/watch_extra/*.torrent,d.set_directory=/srv/rtorrent/watch_extra" # remove files from the client when the corresponding torrent file was deleted schedule = untied_directory,5,60,"remove_untied=" # Close torrents when diskspace is low. #schedule = low_diskspace,5,60,"close_low_diskspace=100M" ## ## throttle options ## # old way to throttle by ration (prior to 0.8.4) # Stop torrents when reaching upload ratio in percent, # when also reaching total upload in bytes, or when # reaching final upload ratio in percent. # example: stop at ratio 2.0 with at least 200 MB uploaded, or else ratio 20.0 #schedule = ratio,60,60,"stop_on_ratio=1000,1000M,10000" # current method - details http://libtorrent.rakshasa.no/wiki/RTorrentRatioHandling # Enable the default ratio group. #ratio.enable= # Change the limits #ratio.min.set=200 #ratio.upload.set=1000M #ratio.max.set=1000 # Changing the command triggered when the ratio is reached. (default is to close only) # system.method.set = group.seeding.ratio.command, d.close=, d.erase= # system.method.set = group.seeding.ratio.command, d.stop= # upload_rate = KB # Maximum and minimum number of peers to connect to per torrent. #min_peers = 40 #max_peers = 100 # Maximum number of simultaneous uploads per torrent. #max_uploads = 30 # max upload slots for all torrents #max_uploads_global = 200 ## ## performance options (handle with care) ## # sendbuffer, increase may reduse diskaccess, see "cat /proc/sys/net/ipv4/tcp_wmem" for min, default, max #send_buffer_size = value named network.send_buffer.size since 0.8.7) # 128K, default 16K network.send_buffer.size = 131072 # max memory is limited to ulimit -m or 1GB #max_memory_usage = bytes # for additional settings see man rtorrent EOF
Change ownership to the user under which ID rtorrent should be running:
sudo chown -R cloph: /srv/rtorrent/*
Create the init script to have it launch at boot:
sudo echo > /etc/init.d/rtorrent <<EOF #!/bin/bash ### BEGIN INIT INFO # Provides: torrent_client # Required-Start: $network $local_fs $syslog torrent_tracker # Required-Stop: $network $local_fs $syslog # Default-Start: 2 3 4 5 # Default-Stop: 0 1 6 # Short-Description: Start the BitTorrent client ### END INIT INFO ############# ###<Notes>### ############# # This script depends on screen. # For the stop function to work, you must set an # explicit session directory using absolute paths (no, ~ is not absolute) in your rtorrent.rc. # If you typically just start rtorrent with just "rtorrent" on the # command line, all you need to change is the "user" option. # Attach to the screen session as your user with # "screen -dr rtorrent". Change "rtorrent" with srnname option. # Licensed under the GPLv2 by lostnihilist: lostnihilist _at_ gmail _dot_ com ############## ###</Notes>### ############## ####################### ##Start Configuration## ####################### # You can specify your configuration in a different file # (so that it is saved with upgrades, saved in your home directory, # or whatever reason you want to) # by commenting out/deleting the configuration lines and placing them # in a text file (say /home/user/.rtorrent.init.conf) exactly as you would # have written them here (you can leave the comments if you desire # and then uncommenting the following line correcting the path/filename # for the one you used. note the space after the ".". # . /etc/rtorrent.init.conf #Do not put a space on either side of the equal signs e.g. # user = user # will not work # system user to run as (can only use one) user="cloph" # system user to run as # not implemented, see d_start for beginning implementation # group=$(id -ng "$user") # the full path to the filename where you store your rtorrent configuration # must keep parentheses around the entire statement, quotations around each config file #config=("$(su -c 'echo $HOME' $user)/.rtorrent.rc") config=("/srv/rtorrent/config/rtorrent.rc") # Examples: # config=("/home/user/.rtorrent.rc") # config=("/home/user/.rtorrent.rc" "/mnt/some/drive/.rtorrent2.rc") # config=("/home/user/.rtorrent.rc" # "/mnt/some/drive/.rtorrent2.rc" # "/mnt/another/drive/.rtorrent3.rc") # set of options to run with each instance, separated by a new line # must keep parentheses around the entire statement #if no special options, specify with: "" #options=("") options=("-n -o import=/srv/rtorrent/config/rtorrent.rc") # Examples: # starts one instance, sourcing both .rtorrent.rc and .rtorrent2.rc # options=("-o import=~/.rtorrent2.rc") # starts two instances, ignoring .rtorrent.rc for both, and using # .rtorrent2.rc for the first, and .rtorrent3.rc for the second # we do not check for valid options # options=("-n -o import=~/.rtorrent2.rc" "-n -o import=~/rtorrent3.rc") # default directory for screen, needs to be an absolute path #base=$(su -c 'echo $HOME' $user) base="/srv/rtorrent" # name of screen session srnname="rtorrent" # file to log to (makes for easier debugging if something goes wrong) logfile="/var/log/rtorrentInit.log" ####################### ###END CONFIGURATION### ####################### PATH=/usr/bin:/usr/local/bin:/usr/local/sbin:/sbin:/bin:/usr/sbin DESC="rtorrent" NAME=rtorrent DAEMON=$NAME SCRIPTNAME=/etc/init.d/$NAME checkcnfg() { exists=0 for i in `echo "$PATH" | tr ':' '\n'` ; do if [ -f $i/$NAME ] ; then exists=1 break fi done if [ $exists -eq 0 ] ; then echo "cannot find $NAME binary in PATH: $PATH" | tee -a "$logfile" >&2 exit 3 fi for (( i=0 ; i < ${#config[@]} ; i++ )) ; do if ! [ -r "${config[i]}" ] ; then echo "cannot find readable config ${config[i]}. check that it is there and permissions are appropriate" | tee -a "$logfile" >&2 exit 3 fi session=$(getsession "${config[i]}") if ! [ -d "${session}" ] ; then echo "cannot find readable session directory ${session} from config ${config[i]}. check permissions" | tee -a "$logfile" >&2 exit 3 fi done } d_start() { [ -d "${base}" ] && cd "${base}" stty stop undef && stty start undef #su -c "screen -S "${srnname}" -X screen rtorrent ${options} 2>&1 1>/dev/null" ${user} | tee -a "$logfile" >&2 su -c "screen -ls | grep -sq "\.${srnname}[[:space:]]" " ${user} || su -c "screen -dm -S ${srnname} 2>&1 1>/dev/null" ${user} | tee -a "$logfile" >&2 # this works for the screen command, but starting rtorrent below adopts screen session gid # even if it is not the screen session we started (e.g. running under an undesirable gid #su -c "screen -ls | grep -sq "\.${srnname}[[:space:]]" " ${user} || su -c "sg \"$group\" -c \"screen -fn -dm -S ${srnname} 2>&1 1>/dev/null\"" ${user} | tee -a "$logfile" >&2 for (( i=0 ; i < ${#options[@]} ; i++ )) ; do sleep 3 su -c "screen -S "${srnname}" -X screen rtorrent ${options[i]} 2>&1 1>/dev/null" ${user} | tee -a "$logfile" >&2 done } d_stop() { for (( i=0 ; i < ${#config[@]} ; i++ )) ; do session=$(getsession "${config[i]}") if ! [ -s ${session}/rtorrent.lock ] ; then return fi pid=$(cat ${session}/rtorrent.lock | awk -F: '{print($2)}' | sed "s/[^0-9]//g") # make sure the pid doesn't belong to another process if ps -A | grep -sq ${pid}.*rtorrent ; then kill -s INT ${pid} fi done } getsession() { session=$(cat "$1" | grep "^[[:space:]]*session[[:space:]]*=" | sed "s/^[[:space:]]*session[[:space:]]*=[[:space:]]*//" ) #session=${session/#~/`getent passwd ${user}|cut -d: -f6`} echo $session } checkcnfg case "$1" in start) echo -n "Starting $DESC: $NAME" d_start echo "." ;; stop) echo -n "Stopping $DESC: $NAME" d_stop echo "." ;; restart|force-reload) echo -n "Restarting $DESC: $NAME" d_stop sleep 1 d_start echo "." ;; *) echo "Usage: $SCRIPTNAME {start|stop|restart|force-reload}" >&2 exit 1 ;; esac exit 0 EOF sudo chmod +x /etc/init.d/rtorrent
Enable the initscript:
sudo update-rc.d rtorrent defaults
Add hooks to the
/usr/local/bin/stage2pub
script to add the torrents & symlink the files:# Torrent section - if anyone goes wrong here, blame cloph echo "setting up symlinks for rtorrent" su - cloph -c "find /srv/active/pub/libreoffice/ -type f -not -name \*md5 -not -name \*asc -not -name \*log -print0 | xargs -r -0 ln -sf --target-directory=/srv/rtorrent/download/" echo "updating torrents for the client" su - cloph -c "rsync -r --delete --include=\*torrent /srv/tracker/torrents_sync/ /srv/rtorrent/watch"
Add cronjob to remove the dangling symlinks that remain:
sudo echo > /etc/cron.d/rtorrent-cleanup <<EOF # torrent download files are symlinked to preserve space # but while the obsolete "*.torrent" files are delted when updating the mirrors # (in the stage2pub script), the symlinks for the downloads remain. # This cleans up weekly (-L = follow symlinks, -type l -> the target still is a symlink, # thus it doesn't point to a file, in other words: find all links no longer pointing to a file) # # the second find statement is a sanity check - all files rtorrent seeds should be symlinks, # otherwise space is wasted # # m h dom mon dow user command 43 4 * * Sun cloph find -L /srv/rtorrent/download -type l -print0 | xargs -r -0 rm ; find /srv/rtorrent/download -type f EOF
Start¶
sudo /etc/init.d/rtorrent start
Stop¶
Note
stopping and restarting is not advised, as on start it hashes the files to check whether it has “donwloaded” all files and whether they are intact. This causes both IO as well as CPU cycles. If you temporarily have rtorrent cease its activity, either throttle it or start/stop the torrents using the interactive interface (connect to the screen session). To stop all torrents at once, see http://libtorrent.rakshasa.no/wiki/RTorrentCommonTasks#Startorstopalltorrents
sudo /etc/init.d/rtorrent stop
Disable¶
sudo update-rc.d rtorrent disable
Enable¶
sudo update-rc.d rtorrent enable
Responsible¶
If something wrong or fishy, contact cloph.