Merge /users/git/public/mirror
authorDavid Bartley <dtbartle@csclub.uwaterloo.ca>
Tue, 29 Jun 2010 09:23:40 +0000 (05:23 -0400)
committerDavid Bartley <dtbartle@csclub.uwaterloo.ca>
Tue, 29 Jun 2010 09:23:40 +0000 (05:23 -0400)
37 files changed:
bin/csc-sync-debian [new file with mode: 0755]
bin/csc-sync-debian-cd [new file with mode: 0755]
bin/csc-sync-standard [new file with mode: 0755]
bin/make-torrents [new file with mode: 0755]
bin/report_mirror [new file with mode: 0755]
bin/ubuntu-releases-sync [new file with mode: 0755]
crontab [deleted file]
csc-sync-debian [deleted file]
csc-sync-debian-cd [deleted file]
csc-sync-standard [deleted file]
debian-check-md5sum [deleted file]
include/HEADER.shtml
make-torrents [deleted file]
mirror-index/config.yaml [new file with mode: 0644]
mirror-index/index.css [new file with mode: 0644]
mirror-index/index.mako [new file with mode: 0644]
mirror-index/make-index.py [new file with mode: 0755]
misc/debian-check-md5sum [new file with mode: 0755]
orionroutes.py [deleted file]
routing/csc-mirror [new file with mode: 0644]
routing/interfaces [new file with mode: 0644]
routing/orionroutes.py [new file with mode: 0755]
routing/setup-routing [new file with mode: 0644]
routing/update-orion-routes [new file with mode: 0755]
rtorrent-init.d [deleted file]
rtorrent.rc [deleted file]
snmp/.gitignore [new file with mode: 0644]
snmp/CSC-MIB.txt [new file with mode: 0644]
snmp/Makefile [new file with mode: 0644]
snmp/csc-snmp-subagent.c [new file with mode: 0644]
snmp/mirror-mib.c [new file with mode: 0644]
snmp/mirror-mib.h [new file with mode: 0644]
snmp/mirror-nl-glue.c [new file with mode: 0644]
snmp/mirror-nl-glue.h [new file with mode: 0644]
snmp/mirror-stats.c [new file with mode: 0644]
torrents/rtorrent-init.d [new file with mode: 0755]
torrents/rtorrent.rc [new file with mode: 0644]

diff --git a/bin/csc-sync-debian b/bin/csc-sync-debian
new file mode 100755 (executable)
index 0000000..50f06cd
--- /dev/null
@@ -0,0 +1,246 @@
+#! /bin/bash
+
+if [[ $# < 3 ]]; then
+  echo 'Usage: sync local_dir rsync_host rsync_dir [trace_host [trace_dir]]'
+  exit 1
+fi
+
+set -e
+
+TOP_DIR=/mirror
+TO=$TOP_DIR/root/$1
+RSYNC_HOST=$2
+RSYNC_DIR=$3
+if [ $# = 4 ]; then
+    TRACE_HOST=$4
+fi
+TRACE_DIR=project/trace
+if [ $# = 5 ]; then
+    TRACE_DIR=$5
+fi
+LOGDIR=/var/log/mirror/$1_$2
+ADDRESS=129.97.134.71
+
+mkdir -p $LOGDIR
+
+# This script originates from http://www.debian.org/mirror/anonftpsync
+
+# CVS: cvs.debian.org:/cvs/webwml - webwml/english/mirror/anonftpsync
+# Version: $Id: anonftpsync,v 1.33 2007/09/12 15:19:03 joy Exp $ 
+
+# Note: You MUST have rsync 2.6.4 or newer, which is available in sarge
+# and all newer Debian releases, or at http://rsync.samba.org/
+
+# Don't forget:
+# chmod u+x anonftpsync
+
+# Set the variables below to fit your site. You can then use cron to have
+# this script run daily to automatically update your copy of the archive.
+
+# TO is the destination for the base of the Debian mirror directory
+# (the dir that holds dists/ and ls-lR).
+# (mandatory)
+
+#TO=
+
+# RSYNC_HOST is the site you have chosen from the mirrors file.
+# (http://www.debian.org/mirror/list-full)
+# (mandatory)
+
+#RSYNC_HOST=
+
+# RSYNC_DIR is the directory given in the "Packages over rsync:" line of
+# the mirrors file for the site you have chosen to mirror.
+# (mandatory)
+
+#RSYNC_DIR=
+
+# LOGDIR is the directory where the logs will be written to
+# (mandatory)
+
+#LOGDIR=
+
+# ARCH_EXCLUDE can be used to exclude a complete architecture from
+# mirrorring. Please use as space seperated list.
+# Possible values are:
+# alpha, amd64, arm, hppa, hurd-i386, i386, ia64, m68k, mipsel, mips, powerpc, s390, sh and sparc
+#
+# There is one special value: source
+# This is not an architecture but will exclude all source code in /pool
+#
+# eg.
+# ARCH_EXCLUDE="alpha arm hppa hurd-i386 ia64 m68k mipsel mips s390 sparc"
+# 
+# With a blank ARCH_EXCLUDE you will mirror all available architectures
+# (optional)
+
+# EXCLUDE is a list of parameters listing patterns that rsync will exclude, in
+# addition to the architectures excluded by ARCH_EXCLUDE.
+#
+# Use ARCH_EXCLUDE to exclude specific architectures or all sources
+#
+# --exclude stable, testing, unstable options DON'T remove the packages of
+# the given distribution. If you want do so, use debmirror instead.
+#
+# The following example would exclude mostly everything:
+#EXCLUDE="\
+#  --exclude stable/ --exclude testing/ --exclude unstable/ \
+#  --exclude source/ \
+#  --exclude *.orig.tar.gz --exclude *.diff.gz --exclude *.dsc \
+#  --exclude /contrib/ --exclude /non-free/ \
+# "
+
+# With a blank EXCLUDE you will mirror the entire archive, except the
+# architectures excluded by ARCH_EXCLUDE.
+# (optional)
+
+EXCLUDE=
+
+# LOCK_TIMEOUT is a timeout in minutes.  Defaults to 360 (6 hours).
+# This program creates a lock to ensure that only one copy
+# of it is mirroring any one archive at any one time.
+# Locks held for longer than the timeout are broken, unless
+# a running rsync process appears to be connected to $RSYNC_HOST.
+
+LOCK_TIMEOUT=360
+
+# There should be no need to edit anything below this point, unless there
+# are problems.
+
+#-----------------------------------------------------------------------------#
+
+# If you are accessing a rsync server/module which is password-protected,
+# uncomment the following lines (and edit the other file).
+
+# . ftpsync.conf
+# export RSYNC_PASSWORD
+# RSYNC_HOST=$RSYNC_USER@$RSYNC_HOST
+
+#-----------------------------------------------------------------------------#
+
+# Check for some environment variables
+if [ -z $TO ] || [ -z $RSYNC_HOST ] || [ -z $RSYNC_DIR ] || [ -z $LOGDIR ]; then
+       echo "One of the following variables seems to be empty:"
+       echo "TO, RSYNC_HOST, RSYNC_DIR or LOGDIR"
+       exit 2
+fi
+
+if ! [ -d ${TO}/${TRACE_DIR} ]; then
+       # we are running mirror script for the first time
+       umask 002
+       mkdir -p ${TO}/${TRACE_DIR}
+fi
+
+# Note: on some non-Debian systems, hostname doesn't accept -f option.
+# If that's the case on your system, make sure hostname prints the full
+# hostname, and remove the -f option. If there's no hostname command,
+# explicitly replace `hostname -f` with the hostname.
+
+HOSTNAME=`hostname -f`
+
+# The hostname must match the "Site" field written in the list of mirrors.
+# If hostname doesn't returns the correct value, fill and uncomment below 
+HOSTNAME=mirror.csclub.uwaterloo.ca
+LOCK="${TO}/Archive-Update-in-Progress-${HOSTNAME}"
+
+# The temp directory used by rsync --delay-updates is not
+# world-readable remotely. It must be excluded to avoid errors. 
+TMP_EXCLUDE="--exclude .~tmp~/"
+
+# Exclude architectures defined in $ARCH_EXCLUDE
+for ARCH in $ARCH_EXCLUDE; do
+       EXCLUDE=$EXCLUDE"\
+               --exclude binary-$ARCH/ \
+               --exclude disks-$ARCH/ \
+               --exclude installer-$ARCH/ \
+               --exclude Contents-$ARCH.gz \
+               --exclude Contents-$ARCH.diff/ \
+               --exclude arch-$ARCH.files \
+               --exclude arch-$ARCH.list.gz \
+               --exclude *_$ARCH.deb \
+               --exclude *_$ARCH.udeb "
+       if [ "$ARCH" == "source" ]; then
+               SOURCE_EXCLUDE="\
+               --exclude source/ \
+               --exclude *.tar.gz \
+               --exclude *.diff.gz \
+               --exclude *.dsc "
+       fi
+done
+
+# Logfile
+LOGFILE=$LOGDIR/mirror.log
+
+# Get in the right directory and set the umask to be group writable
+# 
+cd $HOME
+umask 002
+
+# Check to see if another sync is in progress
+if [ -f "$LOCK" ]; then
+# Note: this requires the findutils find; for other finds, adjust as necessary
+  if [ "`find $LOCK -maxdepth 1 -amin -$LOCK_TIMEOUT`" = "" ]; then
+# Note: this requires the procps ps; for other ps', adjust as necessary
+    if ps ax | grep '[r]'sync | grep -q $RSYNC_HOST; then
+      echo "stale lock found, but a rsync is still running, aiee!" >&2
+      exit 1
+    else
+      echo "stale lock found (not accessed in the last $LOCK_TIMEOUT minutes), forcing update!"
+      rm -f $LOCK
+    fi
+  else
+    echo "current lock file exists, unable to start rsync!"
+    exit 1
+  fi
+fi
+
+touch $LOCK
+# Note: on some non-Debian systems, trap doesn't accept "exit" as signal
+# specification. If that's the case on your system, try using "0".
+trap "rm -f $LOCK" exit
+
+set +e
+
+# check if we need to sync
+if [[ "$TRACE_HOST" != "" ]]; then
+    TRACE_OLD_TIME=`stat -c%Y $TO/$TRACE_DIR/$TRACE_HOST 2> /dev/null`
+    TRACE_NEW_FILE=/tmp/$RSYNC_HOST_$RSYNC_DIR_$RANDOM
+    nice rsync -tv -4 --address=$ADDRESS \
+        $RSYNC_HOST::$RSYNC_DIR/$TRACE_DIR/$TRACE_HOST \
+        $TRACE_NEW_FILE >> $LOGFILE 2>&1
+    TRACE_NEW_TIME=`stat -c%Y $TRACE_NEW_FILE`
+    rm -f $TRACE_NEW_FILE
+    if [ "$TRACE_OLD_TIME" = "$TRACE_NEW_TIME" ]; then
+        echo 'Trace file for' $RSYNC_HOST::$RSYNC_DIR \
+            'unchanged, not rsyncing.' >> $LOGFILE
+        exit 0
+    fi
+fi
+
+# First sync /pool
+nice rsync -rlHtv \
+     $TMP_EXCLUDE $EXCLUDE $SOURCE_EXCLUDE \
+     --timeout=3600 -4 --address=$ADDRESS \
+     $RSYNC_HOST::$RSYNC_DIR/pool/ $TO/pool/ >> $LOGFILE 2>&1
+result=$?
+
+if [ 0 = $result ]; then
+       # Now sync the remaining stuff
+       nice rsync -rlHtv --delay-updates --delete-after \
+            --exclude "Archive-Update-in-Progress-${HOSTNAME}" \
+            --exclude "${TRACE_DIR}/${HOSTNAME}" \
+            --timeout=3600 -4 --address=$ADDRESS \
+            $TMP_EXCLUDE $EXCLUDE $SOURCE_EXCLUDE \
+            $RSYNC_HOST::$RSYNC_DIR $TO >> $LOGFILE 2>&1
+
+       mkdir -p ${TO}/${TRACE_DIR}
+       LANG=C date -u > "${TO}/${TRACE_DIR}/${HOSTNAME}"
+else
+       echo "ERROR: Help, something weird happened" | tee -a $LOGFILE
+       echo "mirroring /pool exited with exitcode" $result | tee -a $LOGFILE
+fi
+
+savelog $LOGFILE >/dev/null
+
+rm $LOCK
diff --git a/bin/csc-sync-debian-cd b/bin/csc-sync-debian-cd
new file mode 100755 (executable)
index 0000000..11abcbc
--- /dev/null
@@ -0,0 +1,112 @@
+#! /bin/bash
+
+set -e
+
+TOP_DIR=/mirror
+TO=$TOP_DIR/root/debian-cd
+RSYNC_HOST=cdimage.debian.org
+RSYNC_DIR=debian-cd
+LOGDIR=/var/log/mirror/debian-cd_cdimage.debian.org
+ADDRESS=129.97.134.71
+
+mkdir -p $LOGDIR
+
+# LOCK_TIMEOUT is a timeout in minutes.  Defaults to 360 (6 hours).
+# This program creates a lock to ensure that only one copy
+# of it is mirroring any one archive at any one time.
+# Locks held for longer than the timeout are broken, unless
+# a running rsync process appears to be connected to $RSYNC_HOST.
+
+LOCK_TIMEOUT=360
+
+# There should be no need to edit anything below this point, unless there
+# are problems.
+
+#-----------------------------------------------------------------------------#
+
+# If you are accessing a rsync server/module which is password-protected,
+# uncomment the following lines (and edit the other file).
+
+# . ftpsync.conf
+export RSYNC_PASSWORD
+if [[ "$RSYNC_USER" != "" ]]; then
+    RSYNC_HOST=$RSYNC_USER@$RSYNC_HOST
+fi
+
+#-----------------------------------------------------------------------------#
+
+# Check for some environment variables
+if [ -z $TO ] || [ -z $RSYNC_HOST ] || [ -z $RSYNC_DIR ] || [ -z $LOGDIR ]; then
+       echo "One of the following variables seems to be empty:"
+       echo "TO, RSYNC_HOST, RSYNC_DIR or LOGDIR"
+       exit 2
+fi
+
+# Note: on some non-Debian systems, hostname doesn't accept -f option.
+# If that's the case on your system, make sure hostname prints the full
+# hostname, and remove the -f option. If there's no hostname command,
+# explicitly replace `hostname -f` with the hostname.
+
+HOSTNAME=`hostname -f`
+
+# The hostname must match the "Site" field written in the list of mirrors.
+# If hostname doesn't returns the correct value, fill and uncomment below 
+HOSTNAME=mirror.csclub.uwaterloo.ca
+LOCK="${TO}/Archive-Update-in-Progress-${HOSTNAME}"
+
+# The temp directory used by rsync --delay-updates is not
+# world-readable remotely. It must be excluded to avoid errors. 
+TMP_EXCLUDE="--exclude .~tmp~/"
+
+# Logfile
+LOGFILE=$LOGDIR/mirror.log
+
+# Get in the right directory and set the umask to be group writable
+# 
+cd $HOME
+umask 002
+
+# Check to see if another sync is in progress
+if [ -f "$LOCK" ]; then
+# Note: this requires the findutils find; for other finds, adjust as necessary
+  if [ "`find $LOCK -maxdepth 1 -amin -$LOCK_TIMEOUT`" = "" ]; then
+# Note: this requires the procps ps; for other ps', adjust as necessary
+    if ps ax | grep '[r]'sync | grep -q $RSYNC_HOST; then
+      echo "stale lock found, but a rsync is still running, aiee!" >&2
+      exit 1
+    else
+      echo "stale lock found (not accessed in the last $LOCK_TIMEOUT minutes), forcing update!"
+      rm -f $LOCK
+    fi
+  else
+    echo "current lock file exists, unable to start rsync!"
+    exit 1
+  fi
+fi
+
+touch $LOCK
+# Note: on some non-Debian systems, trap doesn't accept "exit" as signal
+# specification. If that's the case on your system, try using "0".
+trap "rm -f $LOCK" exit
+
+set +e
+
+# Now sync the remaining stuff
+nice rsync -rlHtv --delete \
+     --include='*-businesscard.iso' \
+     --include='*-netinst.iso' \
+     --include='*-CD-1.iso' \
+     --include='*-amd64-DVD-1.iso' \
+     --include='*-i386-DVD-1.iso' \
+     --exclude='*.iso' --timeout=3600 -4 --address=$ADDRESS $TMP_EXCLUDE \
+     --exclude "Archive-Update-in-Progress-${HOSTNAME}" \
+     $RSYNC_HOST::$RSYNC_DIR $TO >> $LOGFILE 2>&1
+if [[ "$?" != "0" ]]; then
+    echo "ERROR: Help, something weird happened" | tee -a $LOGFILE
+    echo "mirroring /pool exited with exitcode" $result | tee -a $LOGFILE
+fi
+
+savelog $LOGFILE >/dev/null
+
+rm $LOCK
diff --git a/bin/csc-sync-standard b/bin/csc-sync-standard
new file mode 100755 (executable)
index 0000000..e1ad951
--- /dev/null
@@ -0,0 +1,21 @@
+#!/bin/bash -e
+
+if [ $# -lt 3 ]; then
+  echo 'Usage: sync local_dir rsync_host rsync_dir'
+  exit 1
+fi
+
+umask 002
+
+TO=/mirror/root/$1
+RSYNC_HOST=$2
+RSYNC_DIR=$3
+if test -n "$RSYNC_USER"; then
+  RSYNC_HOST=$RSYNC_USER@$RSYNC_HOST
+fi
+
+exec nice rsync -aH --no-owner --no-group --delete \
+     --timeout=3600 -4 --address=129.97.134.71 \
+     --exclude .~tmp~/ \
+     --quiet --stats --log-file=/home/mirror/merlin/logs/transfer.log \
+     $RSYNC_HOST::$RSYNC_DIR $TO
diff --git a/bin/make-torrents b/bin/make-torrents
new file mode 100755 (executable)
index 0000000..bc7a9ac
--- /dev/null
@@ -0,0 +1,31 @@
+#!/usr/bin/python
+import os.path
+
+base = '/mirror/root/csclub'
+htbase = 'http://csclub.uwaterloo.ca/media/files'
+announce = 'http://bittorrent.csclub.uwaterloo.ca/announce'
+scpto = 'bittorrent.csclub:/var/lib/bnbt/torrents/'
+comment = 'Produced by the University of Waterloo Computer Science Club (http://csclub.uwaterloo.ca/)'
+minsize = 10*1024*1024 # 10 MiB
+
+btmake = '/usr/bin/btmakemetafile.bittornado'
+scp = '/usr/bin/scp'
+
+mediafiles = [ file for file in os.listdir(base) if
+    not file.endswith('.torrent') and
+    not os.path.basename(file).startswith('.') and
+    not os.path.isdir(base + '/' + file) and
+    os.path.getsize(base + '/' + file) > minsize
+]
+
+for file in mediafiles:
+    path = base + '/' + file
+    torrentpath = path + '.torrent'
+    htpath = htbase + '/' + file
+    if not os.path.exists(torrentpath):
+        print "Making torrent for %s..." % torrentpath
+        os.spawnl(os.P_WAIT, btmake, btmake, announce, path,
+                '--comment', comment, '--target', torrentpath,
+                '--httpseeds', htpath)
+        os.spawnl(os.P_WAIT, scp, scp, torrentpath, scpto)
+print "The bittorrent tracker will begin tracking new torrents within five minutes."
diff --git a/bin/report_mirror b/bin/report_mirror
new file mode 100755 (executable)
index 0000000..2f1054f
--- /dev/null
@@ -0,0 +1,272 @@
+#!/usr/bin/python
+
+import os, sys
+from os.path import join
+import pickle
+import re
+import ConfigParser
+import pprint
+import time
+import xmlrpclib
+import base64
+import bz2
+import socket
+
+class HostConfig(object):
+    """Holder for config info from the configuration file"""
+    def __init__(self):
+        self.config = { 'version' : 0,
+                        'global': {},
+                        'site': {},
+                        'host': {},
+                        'stats': {},
+                        }
+
+_translation = [chr(_x) for _x in range(256)]
+def _translate(s, altchars):
+    translation = _translation[:]
+    for k, v in altchars.items():
+        translation[ord(k)] = v
+    return s.translate(''.join(translation))
+
+def urlsafe_b64encode(s):
+   import binascii
+   altchars = '-_'
+   encoded = binascii.b2a_base64(s)[:-1]
+   if altchars is not None:
+      return _translate(encoded, {'+': altchars[0], '/': altchars[1]})
+   return encoded
+
+def gen_dirtree(path):
+    # structure here is:
+    # dirtree is a dict
+    # {
+    #   dirpath :
+    #                {
+    #                   filename1 : size1,
+    #                   filename2 : size2,
+    #                   ...
+    #                 },
+    #   ...
+    # }
+    # 
+    # 2009-03-09: MM's web app ignores the statfiles dict.  So don't bother generating it.
+
+    dirtree = {}
+    for dirpath, dirnames, filenames in os.walk(path):
+        statfiles = {}
+        if path.endswith('/'):
+            short_path = dirpath[len(path):]
+        else:
+            short_path = dirpath[len(path)+1:]
+        if len(short_path) > 0:
+            dirtree[short_path] = statfiles
+        else:
+            dirtree[''] = statfiles
+
+    return dirtree
+
+def errorprint(error):
+    sys.stderr.write(error+'\n')
+
+class MissingOption(Exception):
+    pass
+
+def check_required_options(conf, section, required_options):
+    for o in required_options:
+        if not conf.has_option(section, o):
+            errorprint('missing required option %s in config [%s]' % (o, section))
+            raise MissingOption()
+    return True
+
+def parse_value(value):
+    """Split multi-line values into a list"""
+    if value.find('\n') > -1:
+        return value.split()
+    return value
+
+def parse_section(conf, section, item, required_options, optional_options=[]):
+    if conf.has_option(section, 'enabled'):
+        if conf.get(section, 'enabled') != '1' and section.lower() in item.config:
+            print 'removing disabled section %s' % (section)
+            del item.config[section.lower()]
+            return False
+
+    if not check_required_options(conf, section, required_options):
+        return False
+
+    if not section.lower() in item.config:
+        item.config[section.lower()] = {}
+
+    for o in required_options:
+        item.config[section.lower()][o] = parse_value(conf.get(section, o))
+    for o in optional_options:
+        if conf.has_option(section, o):
+            item.config[section.lower()][o] = parse_value(conf.get(section, o))
+
+    return True
+
+def parse_global(conf, section, item):
+    required_options = [ 'enabled', 'server' ]
+    if not parse_section(conf, section, item, required_options):
+        errorprint('missing required options (server AND enabled) in [%s] section' % (section))
+        return False
+    return True
+
+def parse_site(conf, section, item):
+    required_options = [ 'enabled', 'name', 'password' ]
+    return parse_section(conf, section, item, required_options)
+
+def parse_host(conf, section, item):
+    required_options = [ 'enabled', 'name' ]
+    optional_options = [ 'user_active' ]
+    return parse_section(conf, section, item, required_options, optional_options=optional_options)
+
+def get_stats(conf, section):
+    if conf.has_option(section, 'enabled'):
+        if conf.get(section, 'enabled') != '1':
+            return None
+    statsdata = {}
+    for name, value in conf.items(section):
+        if name == 'enabled':
+            continue
+        filenames = parse_value(conf.get(section, name))
+        if type(filenames) != list:
+            filenames = [ filenames ]
+        for fn in filenames:
+            try:
+                f = open(fn, 'r')
+                contents = contents + f.readlines()
+                statsdata[name] = pickle.dumps(contents, -1)
+                f.close()
+            except:
+                pass
+    return statsdata
+
+def parse_category(conf, section, item, crawl):
+    required_options = [ 'enabled', 'path' ]
+    if not parse_section(conf, section, item, required_options):
+        return False
+
+    if crawl:
+        dirtree = gen_dirtree(conf.get(section, 'path'))
+        item.config[section.lower()]['dirtree'] = dirtree
+    # database doesn't need to know the disk path
+    del item.config[section.lower()]['path']
+
+def config(cfg, item, crawl=True):
+    broken = False
+    conf = ConfigParser.ConfigParser()
+    files = conf.read(cfg)
+    if files == []:
+        errorprint('Configuration file %s not found' % (cfg))
+        return False
+    conf.read(cfg)
+
+    try:
+        # don't grab parse_stats here
+        for section, parsefunc in [ ('global', parse_global), ('site', parse_site),
+                                    ('host', parse_host)]:
+            if conf.has_section(section):
+                if not parsefunc(conf, section, item):
+                    return False
+            else:
+                errorprint('Invalid configuration - missing section [%s]' % (section))
+                sys.exit(1)
+
+        for section in conf.sections():
+            if section in [ 'global', 'site', 'host', 'stats']:
+                continue
+            parse_category(conf, section, item, crawl)
+
+    except MissingOption:
+        errorprint('Invalid configuration - Exiting')
+        sys.exit(1)
+
+    return True
+
+
+
+def main():
+    from optparse import OptionParser
+    parser = OptionParser(usage= sys.argv[0] + " [options]")
+    parser.add_option("-c", "--config",
+                      dest="config",
+                      default='/etc/mirrormanager-client/report_mirror.conf',
+                      help='Configuration filename (required)')
+    parser.add_option("-s", "--stats",
+                      action="store_true",
+                      dest="stats",
+                      default=False,
+                      help='Send stats')
+    parser.add_option("-i", "--input",
+                      dest="input",
+                      default=None,
+                      help="Input filename (for debugging)")
+    parser.add_option("-o", "--output",
+                      dest="output",
+                      default=None,
+                      help="Output filename (for debugging)")
+    parser.add_option("-n", "--no-send",
+                      action="store_true",
+                      dest="no_send",
+                      default=False,
+                      help="Don't send data to the server.")
+    parser.add_option("-d", "--debug",
+                      action="store_true",
+                      dest="debug",
+                      default=False,
+                      help='Enable debugging output')
+
+
+    (options, args) = parser.parse_args()
+    item = HostConfig()
+    if options.input:
+        infile = open(options.input, 'rb')
+        item.config = pickle.load(infile)
+        infile.close()
+        if not config(options.config, item, crawl=False):
+            sys.exit(1)
+    else:
+        if not config(options.config, item, crawl=True):
+            sys.exit(1)
+
+    p = pickle.dumps(item.config, -1)
+
+    if options.debug:
+        pp = pprint.PrettyPrinter(indent=4)
+        pp.pprint(item.config)
+        
+    if options.output is not None:
+        outfile = open(options.output, 'wb')
+        outfile.write(p)
+        outfile.close()
+
+    if options.stats:
+        statdata = get_stats(conf, 'stats')
+
+    # upload p and statsdata here
+    if not item.config.has_key('global') or not item.config['global'].has_key('enabled') or item.config['global']['enabled'] != '1':
+        sys.exit(1)
+
+    if not options.no_send:
+        #   print "Connecting to %s" % item.config['global']['server']
+        server = xmlrpclib.ServerProxy(item.config['global']['server'])
+        data = None
+        try:
+            data = base64.urlsafe_b64encode(bz2.compress(p))
+        except AttributeError:
+            data = urlsafe_b64encode(bz2.compress(p))
+
+        if data is not None:
+            try:
+                print server.checkin(data)
+            except socket.error, m:
+                print "Error checking in: %s.  Please try again later." % (m[1])
+            except xmlrpclib.Fault:
+                print "Error checking in.  Connection closed before checkin complete.  Please try again later."
+                sys.exit(1)                
+
+
+if __name__ == '__main__':
+    main()
diff --git a/bin/ubuntu-releases-sync b/bin/ubuntu-releases-sync
new file mode 100755 (executable)
index 0000000..078b559
--- /dev/null
@@ -0,0 +1,3 @@
+#!/bin/sh
+# This file is used for ubuntu push mirroring. Do not remove it!
+exec ~/bin/csc-sync-standard ubuntu-releases rsync.releases.ubuntu.com releases
diff --git a/crontab b/crontab
deleted file mode 100644 (file)
index 6196831..0000000
--- a/crontab
+++ /dev/null
@@ -1,14 +0,0 @@
-# m h    dom mon dow command
-  0 *    *   *   *   ~/bin/csc-sync-debian debian gulus.usherbrooke.ca debian ftp-master.debian.org
-  0 *    *   *   *   ~/bin/csc-sync-debian debian-multimedia mirror.home-dn.net debian-multimedia roxane.home-dn.net
-  0 *    *   *   *   ~/bin/csc-sync-debian debian-backports www.backports.org backports.org www.backports.org
-  0 *    *   *   *   ~/bin/csc-sync-debian debian-volatile volatile.debian.org debian-volatile volatile-master.debian.net
-  0 *    *   *   *   ~/bin/csc-sync-debian debian-security security.debian.org debian-security security-master.debian.org
-  0 *    *   *   *   ~/bin/csc-sync-debian debian-unofficial ftp.debian-unofficial.org debian ftp-master.debian-unofficial.org
-  0 0    *   *   *   ~/bin/csc-sync-debian emdebian www.emdebian.org emdebian
-  0 *    *   *   *   ~/bin/csc-sync-debian ubuntu archive.ubuntu.com ubuntu drescher.canonical.com
-  0 */12 *   *   *   ~/bin/csc-sync-standard gobuntu-releases cdimage.ubuntu.com cdimage/gobuntu/releases/
-  0 */12 *   *   *   ~/bin/csc-sync-standard ubuntu-releases releases.ubuntu.com releases
-  0 */12 *   *   *   ~/bin/csc-sync-standard xubuntu-releases cdimage.ubuntu.com cdimage/xubuntu/releases/
-  0 */12 *   *   *   ~/bin/csc-sync-standard CPAN rsync.nic.funet.fi CPAN
-  0 */12 *   *   *   ~/bin/csc-sync-standard openoffice ftp.ussg.iu.edu openoffice
diff --git a/csc-sync-debian b/csc-sync-debian
deleted file mode 100755 (executable)
index 50f06cd..0000000
+++ /dev/null
@@ -1,246 +0,0 @@
-#! /bin/bash
-
-if [[ $# < 3 ]]; then
-  echo 'Usage: sync local_dir rsync_host rsync_dir [trace_host [trace_dir]]'
-  exit 1
-fi
-
-set -e
-
-TOP_DIR=/mirror
-TO=$TOP_DIR/root/$1
-RSYNC_HOST=$2
-RSYNC_DIR=$3
-if [ $# = 4 ]; then
-    TRACE_HOST=$4
-fi
-TRACE_DIR=project/trace
-if [ $# = 5 ]; then
-    TRACE_DIR=$5
-fi
-LOGDIR=/var/log/mirror/$1_$2
-ADDRESS=129.97.134.71
-
-mkdir -p $LOGDIR
-
-# This script originates from http://www.debian.org/mirror/anonftpsync
-
-# CVS: cvs.debian.org:/cvs/webwml - webwml/english/mirror/anonftpsync
-# Version: $Id: anonftpsync,v 1.33 2007/09/12 15:19:03 joy Exp $ 
-
-# Note: You MUST have rsync 2.6.4 or newer, which is available in sarge
-# and all newer Debian releases, or at http://rsync.samba.org/
-
-# Don't forget:
-# chmod u+x anonftpsync
-
-# Set the variables below to fit your site. You can then use cron to have
-# this script run daily to automatically update your copy of the archive.
-
-# TO is the destination for the base of the Debian mirror directory
-# (the dir that holds dists/ and ls-lR).
-# (mandatory)
-
-#TO=
-
-# RSYNC_HOST is the site you have chosen from the mirrors file.
-# (http://www.debian.org/mirror/list-full)
-# (mandatory)
-
-#RSYNC_HOST=
-
-# RSYNC_DIR is the directory given in the "Packages over rsync:" line of
-# the mirrors file for the site you have chosen to mirror.
-# (mandatory)
-
-#RSYNC_DIR=
-
-# LOGDIR is the directory where the logs will be written to
-# (mandatory)
-
-#LOGDIR=
-
-# ARCH_EXCLUDE can be used to exclude a complete architecture from
-# mirrorring. Please use as space seperated list.
-# Possible values are:
-# alpha, amd64, arm, hppa, hurd-i386, i386, ia64, m68k, mipsel, mips, powerpc, s390, sh and sparc
-#
-# There is one special value: source
-# This is not an architecture but will exclude all source code in /pool
-#
-# eg.
-# ARCH_EXCLUDE="alpha arm hppa hurd-i386 ia64 m68k mipsel mips s390 sparc"
-# 
-# With a blank ARCH_EXCLUDE you will mirror all available architectures
-# (optional)
-
-# EXCLUDE is a list of parameters listing patterns that rsync will exclude, in
-# addition to the architectures excluded by ARCH_EXCLUDE.
-#
-# Use ARCH_EXCLUDE to exclude specific architectures or all sources
-#
-# --exclude stable, testing, unstable options DON'T remove the packages of
-# the given distribution. If you want do so, use debmirror instead.
-#
-# The following example would exclude mostly everything:
-#EXCLUDE="\
-#  --exclude stable/ --exclude testing/ --exclude unstable/ \
-#  --exclude source/ \
-#  --exclude *.orig.tar.gz --exclude *.diff.gz --exclude *.dsc \
-#  --exclude /contrib/ --exclude /non-free/ \
-# "
-
-# With a blank EXCLUDE you will mirror the entire archive, except the
-# architectures excluded by ARCH_EXCLUDE.
-# (optional)
-
-EXCLUDE=
-
-# LOCK_TIMEOUT is a timeout in minutes.  Defaults to 360 (6 hours).
-# This program creates a lock to ensure that only one copy
-# of it is mirroring any one archive at any one time.
-# Locks held for longer than the timeout are broken, unless
-# a running rsync process appears to be connected to $RSYNC_HOST.
-
-LOCK_TIMEOUT=360
-
-# There should be no need to edit anything below this point, unless there
-# are problems.
-
-#-----------------------------------------------------------------------------#
-
-# If you are accessing a rsync server/module which is password-protected,
-# uncomment the following lines (and edit the other file).
-
-# . ftpsync.conf
-# export RSYNC_PASSWORD
-# RSYNC_HOST=$RSYNC_USER@$RSYNC_HOST
-
-#-----------------------------------------------------------------------------#
-
-# Check for some environment variables
-if [ -z $TO ] || [ -z $RSYNC_HOST ] || [ -z $RSYNC_DIR ] || [ -z $LOGDIR ]; then
-       echo "One of the following variables seems to be empty:"
-       echo "TO, RSYNC_HOST, RSYNC_DIR or LOGDIR"
-       exit 2
-fi
-
-if ! [ -d ${TO}/${TRACE_DIR} ]; then
-       # we are running mirror script for the first time
-       umask 002
-       mkdir -p ${TO}/${TRACE_DIR}
-fi
-
-# Note: on some non-Debian systems, hostname doesn't accept -f option.
-# If that's the case on your system, make sure hostname prints the full
-# hostname, and remove the -f option. If there's no hostname command,
-# explicitly replace `hostname -f` with the hostname.
-
-HOSTNAME=`hostname -f`
-
-# The hostname must match the "Site" field written in the list of mirrors.
-# If hostname doesn't returns the correct value, fill and uncomment below 
-HOSTNAME=mirror.csclub.uwaterloo.ca
-LOCK="${TO}/Archive-Update-in-Progress-${HOSTNAME}"
-
-# The temp directory used by rsync --delay-updates is not
-# world-readable remotely. It must be excluded to avoid errors. 
-TMP_EXCLUDE="--exclude .~tmp~/"
-
-# Exclude architectures defined in $ARCH_EXCLUDE
-for ARCH in $ARCH_EXCLUDE; do
-       EXCLUDE=$EXCLUDE"\
-               --exclude binary-$ARCH/ \
-               --exclude disks-$ARCH/ \
-               --exclude installer-$ARCH/ \
-               --exclude Contents-$ARCH.gz \
-               --exclude Contents-$ARCH.diff/ \
-               --exclude arch-$ARCH.files \
-               --exclude arch-$ARCH.list.gz \
-               --exclude *_$ARCH.deb \
-               --exclude *_$ARCH.udeb "
-       if [ "$ARCH" == "source" ]; then
-               SOURCE_EXCLUDE="\
-               --exclude source/ \
-               --exclude *.tar.gz \
-               --exclude *.diff.gz \
-               --exclude *.dsc "
-       fi
-done
-
-# Logfile
-LOGFILE=$LOGDIR/mirror.log
-
-# Get in the right directory and set the umask to be group writable
-# 
-cd $HOME
-umask 002
-
-# Check to see if another sync is in progress
-if [ -f "$LOCK" ]; then
-# Note: this requires the findutils find; for other finds, adjust as necessary
-  if [ "`find $LOCK -maxdepth 1 -amin -$LOCK_TIMEOUT`" = "" ]; then
-# Note: this requires the procps ps; for other ps', adjust as necessary
-    if ps ax | grep '[r]'sync | grep -q $RSYNC_HOST; then
-      echo "stale lock found, but a rsync is still running, aiee!" >&2
-      exit 1
-    else
-      echo "stale lock found (not accessed in the last $LOCK_TIMEOUT minutes), forcing update!"
-      rm -f $LOCK
-    fi
-  else
-    echo "current lock file exists, unable to start rsync!"
-    exit 1
-  fi
-fi
-
-touch $LOCK
-# Note: on some non-Debian systems, trap doesn't accept "exit" as signal
-# specification. If that's the case on your system, try using "0".
-trap "rm -f $LOCK" exit
-
-set +e
-
-# check if we need to sync
-if [[ "$TRACE_HOST" != "" ]]; then
-    TRACE_OLD_TIME=`stat -c%Y $TO/$TRACE_DIR/$TRACE_HOST 2> /dev/null`
-    TRACE_NEW_FILE=/tmp/$RSYNC_HOST_$RSYNC_DIR_$RANDOM
-    nice rsync -tv -4 --address=$ADDRESS \
-        $RSYNC_HOST::$RSYNC_DIR/$TRACE_DIR/$TRACE_HOST \
-        $TRACE_NEW_FILE >> $LOGFILE 2>&1
-    TRACE_NEW_TIME=`stat -c%Y $TRACE_NEW_FILE`
-    rm -f $TRACE_NEW_FILE
-    if [ "$TRACE_OLD_TIME" = "$TRACE_NEW_TIME" ]; then
-        echo 'Trace file for' $RSYNC_HOST::$RSYNC_DIR \
-            'unchanged, not rsyncing.' >> $LOGFILE
-        exit 0
-    fi
-fi
-
-# First sync /pool
-nice rsync -rlHtv \
-     $TMP_EXCLUDE $EXCLUDE $SOURCE_EXCLUDE \
-     --timeout=3600 -4 --address=$ADDRESS \
-     $RSYNC_HOST::$RSYNC_DIR/pool/ $TO/pool/ >> $LOGFILE 2>&1
-result=$?
-
-if [ 0 = $result ]; then
-       # Now sync the remaining stuff
-       nice rsync -rlHtv --delay-updates --delete-after \
-            --exclude "Archive-Update-in-Progress-${HOSTNAME}" \
-            --exclude "${TRACE_DIR}/${HOSTNAME}" \
-            --timeout=3600 -4 --address=$ADDRESS \
-            $TMP_EXCLUDE $EXCLUDE $SOURCE_EXCLUDE \
-            $RSYNC_HOST::$RSYNC_DIR $TO >> $LOGFILE 2>&1
-
-       mkdir -p ${TO}/${TRACE_DIR}
-       LANG=C date -u > "${TO}/${TRACE_DIR}/${HOSTNAME}"
-else
-       echo "ERROR: Help, something weird happened" | tee -a $LOGFILE
-       echo "mirroring /pool exited with exitcode" $result | tee -a $LOGFILE
-fi
-
-savelog $LOGFILE >/dev/null
-
-rm $LOCK
diff --git a/csc-sync-debian-cd b/csc-sync-debian-cd
deleted file mode 100755 (executable)
index 5147832..0000000
+++ /dev/null
@@ -1,108 +0,0 @@
-#! /bin/bash
-
-set -e
-
-TOP_DIR=/mirror
-TO=$TOP_DIR/root/debian-cd
-RSYNC_HOST=cdimage.debian.org
-RSYNC_DIR=debian-cd
-LOGDIR=/var/log/mirror/debian-cd_cdimage.debian.org
-ADDRESS=129.97.134.71
-
-mkdir -p $LOGDIR
-
-# LOCK_TIMEOUT is a timeout in minutes.  Defaults to 360 (6 hours).
-# This program creates a lock to ensure that only one copy
-# of it is mirroring any one archive at any one time.
-# Locks held for longer than the timeout are broken, unless
-# a running rsync process appears to be connected to $RSYNC_HOST.
-
-LOCK_TIMEOUT=360
-
-# There should be no need to edit anything below this point, unless there
-# are problems.
-
-#-----------------------------------------------------------------------------#
-
-# If you are accessing a rsync server/module which is password-protected,
-# uncomment the following lines (and edit the other file).
-
-# . ftpsync.conf
-export RSYNC_PASSWORD
-if [[ "$RSYNC_USER" != "" ]]; then
-    RSYNC_HOST=$RSYNC_USER@$RSYNC_HOST
-fi
-
-#-----------------------------------------------------------------------------#
-
-# Check for some environment variables
-if [ -z $TO ] || [ -z $RSYNC_HOST ] || [ -z $RSYNC_DIR ] || [ -z $LOGDIR ]; then
-       echo "One of the following variables seems to be empty:"
-       echo "TO, RSYNC_HOST, RSYNC_DIR or LOGDIR"
-       exit 2
-fi
-
-# Note: on some non-Debian systems, hostname doesn't accept -f option.
-# If that's the case on your system, make sure hostname prints the full
-# hostname, and remove the -f option. If there's no hostname command,
-# explicitly replace `hostname -f` with the hostname.
-
-HOSTNAME=`hostname -f`
-
-# The hostname must match the "Site" field written in the list of mirrors.
-# If hostname doesn't returns the correct value, fill and uncomment below 
-HOSTNAME=mirror.csclub.uwaterloo.ca
-LOCK="${TO}/Archive-Update-in-Progress-${HOSTNAME}"
-
-# The temp directory used by rsync --delay-updates is not
-# world-readable remotely. It must be excluded to avoid errors. 
-TMP_EXCLUDE="--exclude .~tmp~/"
-
-# Logfile
-LOGFILE=$LOGDIR/mirror.log
-
-# Get in the right directory and set the umask to be group writable
-# 
-cd $HOME
-umask 002
-
-# Check to see if another sync is in progress
-if [ -f "$LOCK" ]; then
-# Note: this requires the findutils find; for other finds, adjust as necessary
-  if [ "`find $LOCK -maxdepth 1 -amin -$LOCK_TIMEOUT`" = "" ]; then
-# Note: this requires the procps ps; for other ps', adjust as necessary
-    if ps ax | grep '[r]'sync | grep -q $RSYNC_HOST; then
-      echo "stale lock found, but a rsync is still running, aiee!" >&2
-      exit 1
-    else
-      echo "stale lock found (not accessed in the last $LOCK_TIMEOUT minutes), forcing update!"
-      rm -f $LOCK
-    fi
-  else
-    echo "current lock file exists, unable to start rsync!"
-    exit 1
-  fi
-fi
-
-touch $LOCK
-# Note: on some non-Debian systems, trap doesn't accept "exit" as signal
-# specification. If that's the case on your system, try using "0".
-trap "rm -f $LOCK" exit
-
-set +e
-
-# Now sync the remaining stuff
-nice rsync -rlHtv --delete \
-     --include='*businesscard*.iso' --include='*netinst*.iso' \
-     --exclude='*.iso' --timeout=3600 -4 --address=$ADDRESS $TMP_EXCLUDE \
-     --exclude "Archive-Update-in-Progress-${HOSTNAME}" \
-     $RSYNC_HOST::$RSYNC_DIR $TO >> $LOGFILE 2>&1
-if [[ "$?" != "0" ]]; then
-    echo "ERROR: Help, something weird happened" | tee -a $LOGFILE
-    echo "mirroring /pool exited with exitcode" $result | tee -a $LOGFILE
-fi
-
-savelog $LOGFILE >/dev/null
-
-rm $LOCK
diff --git a/csc-sync-standard b/csc-sync-standard
deleted file mode 100755 (executable)
index 1aa2be5..0000000
+++ /dev/null
@@ -1,114 +0,0 @@
-#! /bin/bash
-
-if [ $# -lt 3 ]; then
-  echo 'Usage: sync local_dir rsync_host rsync_dir'
-  exit 1
-fi
-
-set -e
-
-TOP_DIR=/mirror
-TO=$TOP_DIR/root/$1
-RSYNC_HOST=$2
-RSYNC_DIR=$3
-LOGDIR=/var/log/mirror/$1_$2
-ADDRESS=129.97.134.71
-EXTRA=$4
-
-mkdir -p $LOGDIR
-
-# LOCK_TIMEOUT is a timeout in minutes.  Defaults to 360 (6 hours).
-# This program creates a lock to ensure that only one copy
-# of it is mirroring any one archive at any one time.
-# Locks held for longer than the timeout are broken, unless
-# a running rsync process appears to be connected to $RSYNC_HOST.
-
-LOCK_TIMEOUT=360
-
-# There should be no need to edit anything below this point, unless there
-# are problems.
-
-#-----------------------------------------------------------------------------#
-
-# If you are accessing a rsync server/module which is password-protected,
-# uncomment the following lines (and edit the other file).
-
-# . ftpsync.conf
-export RSYNC_PASSWORD
-if [[ "$RSYNC_USER" != "" ]]; then
-    RSYNC_HOST=$RSYNC_USER@$RSYNC_HOST
-fi
-
-#-----------------------------------------------------------------------------#
-
-# Check for some environment variables
-if [ -z $TO ] || [ -z $RSYNC_HOST ] || [ -z $RSYNC_DIR ] || [ -z $LOGDIR ]; then
-       echo "One of the following variables seems to be empty:"
-       echo "TO, RSYNC_HOST, RSYNC_DIR or LOGDIR"
-       exit 2
-fi
-
-# Note: on some non-Debian systems, hostname doesn't accept -f option.
-# If that's the case on your system, make sure hostname prints the full
-# hostname, and remove the -f option. If there's no hostname command,
-# explicitly replace `hostname -f` with the hostname.
-
-HOSTNAME=`hostname -f`
-
-# The hostname must match the "Site" field written in the list of mirrors.
-# If hostname doesn't returns the correct value, fill and uncomment below 
-HOSTNAME=mirror.csclub.uwaterloo.ca
-LOCK="${TO}/Archive-Update-in-Progress-${HOSTNAME}"
-
-# The temp directory used by rsync --delay-updates is not
-# world-readable remotely. It must be excluded to avoid errors. 
-TMP_EXCLUDE="--exclude .~tmp~/"
-
-# Logfile
-LOGFILE=$LOGDIR/mirror.log
-
-# Get in the right directory and set the umask to be group writable
-# 
-cd $HOME
-umask 002
-
-# Check to see if another sync is in progress
-if [ -f "$LOCK" ]; then
-# Note: this requires the findutils find; for other finds, adjust as necessary
-  if [ "`find $LOCK -maxdepth 1 -amin -$LOCK_TIMEOUT`" = "" ]; then
-# Note: this requires the procps ps; for other ps', adjust as necessary
-    if ps ax | grep '[r]'sync | grep -q $RSYNC_HOST; then
-      echo "stale lock found, but a rsync is still running, aiee!" >&2
-      exit 1
-    else
-      echo "stale lock found (not accessed in the last $LOCK_TIMEOUT minutes), forcing update!"
-      rm -f $LOCK
-    fi
-  else
-    echo "current lock file exists, unable to start rsync!"
-    exit 1
-  fi
-fi
-
-touch $LOCK
-# Note: on some non-Debian systems, trap doesn't accept "exit" as signal
-# specification. If that's the case on your system, try using "0".
-trap "rm -f $LOCK" exit
-
-set +e
-
-# Now sync the remaining stuff
-nice rsync -aHv --delete \
-     --exclude "Archive-Update-in-Progress-${HOSTNAME}" \
-     --timeout=3600 -4 --address=$ADDRESS $TMP_EXCLUDE $EXTRA \
-     $RSYNC_HOST::$RSYNC_DIR $TO >> $LOGFILE 2>&1
-if [[ "$?" != "0" ]]; then
-    echo "ERROR: Help, something weird happened" | tee -a $LOGFILE
-    echo "mirroring /pool exited with exitcode" $result | tee -a $LOGFILE
-fi
-chmod ug+rwx $TO
-
-savelog $LOGFILE >/dev/null
-
-rm $LOCK
diff --git a/debian-check-md5sum b/debian-check-md5sum
deleted file mode 100755 (executable)
index f52e72d..0000000
+++ /dev/null
@@ -1,119 +0,0 @@
-#!/usr/bin/python2.5
-import sys, os, re, gzip, bz2, hashlib
-
-package_file_map = {
-    'Packages' : file,
-    'Packages.gz' : gzip.GzipFile,
-    'Packages.bz2' : bz2.BZ2File,
-    'Sources' : file,
-    'Sources.gz' : gzip.GzipFile,
-    'Sources.bz2' : bz2.BZ2File,
-}
-
-def parse_packages_file(path):
-    try:
-        open_func = package_file_map[os.path.basename(path)]
-        file = open_func(path)
-    except IOError, e:
-        print "WARNING: failed to open %s: %s" % (path, e)
-        return {}
-    cur_dict = {}
-    key, value = None, ''
-    ret_list = []
-    while True:
-        try:
-            line = file.readline()
-        except IOError, e:
-            print "WARNING: failed to read %s: %s" % (path, e)
-            print "WARNING: %s" % e
-            return {}
-
-        # check if we are done with current value
-        if (line == '' or line[0] == '\n' or line[0] != ' ') and key != None:
-            cur_dict[key] = value
-
-        if line == '' or line == '\n': # done current block
-            if cur_dict != {}:
-                ret_list.append(cur_dict)
-                cur_dict = {}
-                key = None
-            if line == '': break
-        elif line[0] == ' ': # multi-line value
-            value += '\n' + line[1:-1]
-        else:
-            if line[-1] == '\n': line = line[:-1]
-            pos = line.find(':')
-            key = line[:pos]
-            if key == '': key = None
-            value = line[pos+2:]
-    return ret_list
-
-def find_packages_files(path):
-    files = []
-    for file in os.listdir(path):
-        file_path = "%s/%s" % (path, file)
-        if os.path.islink(file_path):
-            continue
-        elif os.path.isdir(file_path):
-            files += find_packages_files(file_path)
-        elif file in package_file_map:
-            files.append(file_path)
-    return files
-
-if len(sys.argv) != 2:
-    print "Usage: debian-check-md5sum.py base-dir"
-    sys.exit(1)
-base_dir = sys.argv[1]
-
-all = {}
-files_regex = re.compile('(\S+)\s+(\S+)\s+(\S+)')
-for file in find_packages_files(base_dir):
-    file_type = os.path.basename(file).split('.')[0]
-    a = parse_packages_file(file)
-    for package in parse_packages_file(file):
-        if file_type == 'Packages':
-            if 'Filename' in package:
-                all[package['Filename']] = package
-        elif file_type == 'Sources':
-            files = package['Files'].split('\n')
-            for file in files:
-                if file == '': continue
-                match = files_regex.match(file)
-                file_path = '%s/%s' % (package['Directory'], match.group(3))
-                all[file_path] = { 'MD5sum' : match.group(1) }
-print "NOTICE:  need to check %d files" % len(all)
-
-ret_val = 0
-block_size = 65536
-for (file, package) in all.iteritems():
-    path = '%s/%s' % (base_dir, file)
-    try:
-        file = open(path, 'rb')
-    except IOError:
-        print "WARNING: missing %s" % path
-        continue
-    if 'SHA256' in package:
-        md = hashlib.sha256()
-        hash = package['SHA256']
-    elif 'SHA1' in package:
-        md = hashlib.sha1()
-        hash = package['SHA1']
-    elif 'MD5sum' in package:
-        md = hashlib.md5()
-        hash = package['MD5sum']
-    else:
-        print "WARNING: no hash found for %s" % path
-        print package
-        exit(1)
-    while True:
-        data = file.read(block_size)
-        if data == '': break
-        md.update(data)
-    hash_calc = md.hexdigest()
-    if hash == hash_calc:
-        print "NOTICE:  hash ok for %s [hash = %s]" % (path, hash)
-    else:
-        print "ERROR:   hash mismatch for %s [hash = %s, hash_calc = %s]" % \
-            (path, hash, hash_calc)
-        ret_val = 1
-exit(ret_val)
index 65ce693..ab848a7 100644 (file)
@@ -1,5 +1,5 @@
 <div class="csclogo">
-  <a href="http://csclub.uwaterloo.ca">
+  <a href="http://mirror.csclub.uwaterloo.ca">
     <img src="/include/header.png" alt="Computer Science Club Mirror - The University of Waterloo - Funded by MEF" />
   </a>
 </div>
diff --git a/make-torrents b/make-torrents
deleted file mode 100755 (executable)
index 34a23bd..0000000
+++ /dev/null
@@ -1,31 +0,0 @@
-#!/usr/bin/python
-import os.path
-
-base = '/users/www/files'
-htbase = 'http://csclub.uwaterloo.ca/media/files'
-announce = 'http://bittorrent.csclub.uwaterloo.ca/announce'
-scpto = 'bittorrent.csclub:/var/lib/bnbt/torrents/'
-comment = 'Produced by the University of Waterloo Computer Science Club (http://csclub.uwaterloo.ca/)'
-minsize = 10*1024*1024 # 10 MiB
-
-btmake = '/usr/bin/btmakemetafile.bittornado'
-scp = '/usr/bin/scp'
-
-mediafiles = [ file for file in os.listdir(base) if
-    not file.endswith('.torrent') and
-    not os.path.basename(file).startswith('.') and
-    not os.path.isdir(base + '/' + file) and
-    os.path.getsize(base + '/' + file) > minsize
-]
-
-for file in mediafiles:
-    path = base + '/' + file
-    torrentpath = path + '.torrent'
-    htpath = htbase + '/' + file
-    if not os.path.exists(torrentpath):
-        print "Making torrent for %s..." % torrentpath
-        os.spawnl(os.P_WAIT, btmake, btmake, announce, path,
-                '--comment', comment, '--target', torrentpath,
-                '--httpseeds', htpath)
-        os.spawnl(os.P_WAIT, scp, scp, torrentpath, scpto)
-print "The bittorrent tracker will begin tracking new torrents within five minutes."
diff --git a/mirror-index/config.yaml b/mirror-index/config.yaml
new file mode 100644 (file)
index 0000000..74bb689
--- /dev/null
@@ -0,0 +1,172 @@
+docroot: /mirror/root
+duflags: --human-readable --max-depth=1
+output: /mirror/root/index.html
+
+directories:
+  apache:
+    site: apache.org
+    url: http://www.apache.org/
+
+  archlinux:
+    site: archlinux.org
+    url: http://www.archlinux.org/
+
+  blastwave:
+    site: blastwave.org
+    url: http://www.blastwave.org/
+
+  centos:
+    site: centos.org
+    url: http://www.centos.org/
+
+  CPAN:
+    site: cpan.org
+    url: http://www.cpan.org/
+
+  CRAN:
+    site: r-project.org
+    url: http://cran.r-project.org/
+
+  csclub:
+    site: csclub.uwaterloo.ca
+    url: http://csclub.uwaterloo.ca/media/
+
+  CTAN:
+    site: ctan.org
+    url: http://www.ctan.org/
+
+  cygwin:
+    site: cygwin.com
+    url: http://www.cygwin.com/
+
+  damnsmalllinux:
+    site: damnsmalllinux.org
+    url: http://www.damnsmalllinux.org/
+
+  debian:
+    site: debian.org
+    url: http://www.debian.org/
+
+  debian-backports:
+    site: backports.org
+    url: http://www.backports.org/
+
+  debian-cd:
+    site: debian.org
+    url: http://www.debian.org/CD/
+
+  debian-multimedia:
+    site: debian-multimedia.org
+    url: http://www.debian-multimedia.org/
+
+  debian-ports:
+    site: debian-ports.org
+    url: http://www.debian-ports.org/
+
+  debian-security:
+    site: debian.org
+    url: http://www.debian.org/security/
+
+  debian-unofficial:
+    site: debian-maintainers.org
+    url: http://unofficial.debian-maintainers.org/
+
+  debian-volatile:
+    site: debian.org
+    url: http://www.debian.org/volatile/
+
+  eclipse:
+    site: eclipse.org
+    url: http://www.eclipse.org/
+
+  emdebian:
+    site: emdebian.org
+    url: http://www.emdebian.org/
+
+  FreeBSD:
+    site: freebsd.org
+    url: http://www.freebsd.org/
+
+  gentoo-distfiles:
+    site: gentoo.org
+    url: http://www.gentoo.org/
+
+  gentoo-portage:
+    site: gentoo.org
+    url: http://www.gentoo.org/
+
+  gnome:
+    site: gnome.org
+    url: http://www.gnome.org/
+
+  gnu:
+    site: gnu.org
+    url: http://www.gnu.org/
+
+  kde:
+    site: kde.org
+    url: http://www.kde.org/
+
+  kernel.org:
+    site: kernel.org
+    url: http://www.kernel.org/
+
+  linuxmint:
+    site: linuxmint.com
+    url: http://www.linuxmint.com/
+
+  linuxmint-packages:
+    site: linuxmint.com
+    url: http://www.linuxmint.com/
+
+  mozdev:
+    site: mozdev.org
+    url: http://www.mozdev.org/
+
+  mozilla.org:
+    site: mozilla.org
+    url: http://www.mozilla.org/
+
+  mysql:
+    site: mysql.com
+    url: http://www.mysql.com/
+
+  nongnu:
+    site: nongnu.org
+    url: http://savannah.nongnu.org/
+
+  openoffice:
+    site: openoffice.org
+    url: http://www.openoffice.org/
+
+  opensuse:
+    site: opensuse.org
+    url: http://www.opensuse.org/
+
+  slackware:
+    site: slackware.com
+    url: http://www.slackware.com/
+
+  ubuntu:
+    site: ubuntu.com
+    url: http://www.ubuntu.com/
+
+  ubuntu-ports:
+    site: ports.ubuntu.com
+    url: http://ports.ubuntu.com/ubuntu-ports/
+
+  ubuntu-ports-releases:
+    site: ports.ubuntu.com
+    url: http://cdimage.ubuntu.com/ports/releases/
+
+  ubuntu-releases:
+    site: releases.ubuntu.com
+    url: http://releases.ubuntu.com/
+
+  x.org:
+    site: x.org
+    url: http://www.x.org/
+
+  xubuntu-releases:
+    site: xubuntu.org
+    url: http://www.xubuntu.org/
diff --git a/mirror-index/index.css b/mirror-index/index.css
new file mode 100644 (file)
index 0000000..ec62059
--- /dev/null
@@ -0,0 +1,43 @@
+img {
+ border: none;
+}
+html {
+ margin:0.5ex;
+ font-family: sans-serif;
+ font-size: 110%;
+}
+p {
+ margin: 1ex 0;
+}
+table {
+ border-collapse: collapse;
+ text-align: left;
+ width: 100%;
+}
+td {
+ border-top: 1px solid #aaa;
+}
+th, td {
+ padding: .4ex 2em .4ex 0;
+}
+h1 {
+ font-size: 110%;
+}
+#logo {
+ width: 100%;
+ text-align: center;
+ margin-bottom:1em;
+}
+#footer {
+ margin: 2em auto 0 auto;
+ width: 75%;
+ font-size: 70%;
+ text-align: center;
+}
+body {
+ max-width: 40em;
+ margin-top:0;
+ padding-top:0;
+}
+
+tr :last-child { text-align: right; }
diff --git a/mirror-index/index.mako b/mirror-index/index.mako
new file mode 100644 (file)
index 0000000..88f9dfb
--- /dev/null
@@ -0,0 +1,47 @@
+<!DOCTYPE html>
+<html lang="en">
+  <head>
+    <meta charset="UTF-8" />
+    <link rel="stylesheet" type="text/css" href="index.css" />
+    <title>Computer Science Club Mirror</title>
+  </head>
+  
+  <body>
+    <div id="logo">
+      <a href="/"><img src="/include/header.png" alt="Computer Science Club Mirror - The University of Waterloo - Funded by MEF" title="Computer Science Club Mirror - The University of Waterloo - Funded by MEF" /></a>
+    </div>
+    
+    <div id="listing">
+      <table>
+        <tr><th>Directory</th><th>Project Site</th><th>Size</th></tr>
+       
+        % for dir in directories:
+        <tr>
+          <td>
+            ${h.link_to(dir['dir']+'/', '/'+dir['dir']+'/')}
+          </td>
+
+          <td>
+           % if 'site' in dir:
+            ${h.link_to(dir['site'], dir['url'])}
+           % endif
+          </td>
+
+          <td>${dir['size'] | h}</td>
+        </tr>
+
+       % endfor \
+       
+       <tr class="total">
+         <td>Total</td>
+         <td></td>
+         <td>${total_size}</td>
+       </tr>
+      </table>
+    </div>
+
+    <div id="footer">
+      <p>This service is run by the <a href="http://csclub.uwaterloo.ca/">Computer Science Club of the University of Waterloo</a>.<br />It is made possible by funding from the <a href="http://www.student.math.uwaterloo.ca/~mefcom/home">Mathematics Endowment Fund</a><br />and support from the <a href="http://www.cs.uwaterloo.ca">David R. Cheriton School of Computer Science</a>.</p>
+    </div>
+  </body>
+</html>
diff --git a/mirror-index/make-index.py b/mirror-index/make-index.py
new file mode 100755 (executable)
index 0000000..204e61c
--- /dev/null
@@ -0,0 +1,166 @@
+#!/usr/bin/env python
+"""make-index.py
+
+Generates an nice index of the directories from a
+template.
+
+Original Author: Jeremy Roman <jbroman@csclub.uwaterloo.ca>
+
+So if you don't like how I did something,
+I'm the person you get to complain to.
+Please be gentle.
+"""
+
+import os, sys, time
+from subprocess import Popen, PIPE
+from optparse import OptionParser
+import yaml, mako.exceptions, webhelpers.html.tags
+from mako.template import Template
+
+def reformat_size(size):
+    """Reformats '124M' to '124 MB', et cetera."""
+    if size[-1].isalpha():
+        return size[:-1] + " " + size[-1] + "B"
+    else:
+        return size
+
+def atomic_write(filename, body):
+    """Atomically write to a file by writing a
+    temporary file and then moving it to replace
+    the desired output file.
+    
+    This ensures that partial files are never seen
+    by clients."""
+    
+    # generate an appropriate temporary filename
+    # in the same directory
+    tmp_filename = "%s.%d.tmp" % (filename, os.getpid())
+    
+    # open the directory so that we can fsync it
+    dir = os.open(os.path.realpath(os.path.dirname(filename)), \
+                      os.O_DIRECTORY | os.O_RDONLY)
+    
+    # write to the temporary file
+    tmp = open(tmp_filename, 'w')
+    print >>tmp, body
+    tmp.flush()
+    os.fsync(tmp.fileno())
+    tmp.close()
+    
+    # atomically replace the actual file
+    os.rename(tmp_filename, filename)
+    os.fsync(dir)
+    os.close(dir)
+
+def main():
+    # accept command-line arguments
+    parser = OptionParser()
+    parser.add_option("-c", "--config", dest="config", default="config.yaml",
+                      help="configuration file to be used", metavar="FILE")
+    parser.add_option("-D", "--docroot", dest="docroot",
+                      help="directory to be scanned", metavar="DIR")
+    parser.add_option("-F", "--duflags", dest="duflags",
+                      help="flags to be passed to du, replaces any in config")
+    parser.add_option("-o", "--output", dest="output", metavar="FILE",
+                      help="file to which index page will be written. "
+                      "Use /dev/stdout to send to standard out.")
+    parser.add_option("-t", "--template", dest="template",
+                      help="Mako template to render", metavar="FILE")
+    parser.add_option("--nonatomic", dest="nonatomic", action="store_true",
+                      default=False, help="write the output to the path "
+                      "given without creating a temporary file in between. "
+                      "This is automatically set if the output appears "
+                      "to be a character device, not a file.")
+    (options, args) = parser.parse_args()
+    
+    # load config file
+    try:
+        config = yaml.load(file(options.config,'r'))
+    except:
+        config = None
+    
+    if not config or type(config) != dict:
+        print >>sys.stderr, "Unable to load configuration '%s'." % options.config
+        sys.exit(-1)
+    
+    # determine important variables based on an appropriate order of
+    # precedence (command-line flags first, then the config file,
+    # then built-in fallbacks)
+    #
+    # fallback value for nonatomic is used so that character devices
+    # (e.g. /dev/stdout, /dev/null) are written to in the regular way
+    docroot   = options.docroot   or config.get('docroot')
+    duflags   = options.duflags   or config.get('duflags')   or "-h --max-depth=1"
+    output    = options.output    or config.get('output')
+    template  = options.template  or config.get("template")  or "index.mako"
+    nonatomic = options.nonatomic or config.get("nonatomic") or \
+        (os.path.exists(output) and not os.path.isfile(output))
+    
+    # sanity checks
+    if not docroot:
+        print >>sys.stderr, "docroot not specified."
+        print >>sys.stderr, "Define it in the config file or pass -D on the command line."
+        sys.exit(-1)
+    elif not output:
+        print >>sys.stderr, "output not specified."
+        print >>sys.stderr, "Define it in the config file or pass -o on the command line."
+    elif not config.get('directories'):
+        print >>sys.stderr, "directories not specified."
+        print >>sys.stderr, "Define it in the config file."
+        sys.exit(-1)
+    elif not os.path.isdir(docroot):
+        print >>sys.stderr, "docroot '%s' not found or not a directory." % docroot
+        sys.exit(-1)
+    elif not os.path.exists(template) or os.path.isdir(template):
+        print >>sys.stderr, "template '%s' not found or is a directory." % template
+        sys.exit(-1)
+    
+    # Call du to compute size
+    du = Popen(
+        "/usr/bin/du %s %s | /usr/bin/sort -fk2" % (docroot, duflags),
+        shell=True, stdout=PIPE, stderr=PIPE).communicate()
+    
+    # Check that du executed successfully
+    # If there's anything on stderr, send it
+    # out our own stderr and terminate.
+    if len(du[1].strip()) > 0:
+        sys.stderr.write(du[1])
+        print >>sys.stderr, "du terminated unsuccessfully. Not generating index."
+        sys.exit(-1)
+    
+    # first one should be total, grab its size and format
+    du = du[0].splitlines() # we only care about stdout now
+    total_size = reformat_size(du[0].split(None,2)[0])
+    
+    # the rest are the sizes we want
+    directories = []
+    for line in du[1:]:
+        (size, dir) = line.split(None, 2)
+        dir = os.path.basename(dir)
+        info = {'dir':dir, 'size':reformat_size(size)}
+        
+        # use info from config.yaml, if found
+        # otherwise, skip this directory
+        if dir in config['directories']:
+            info.update(config['directories'][dir])
+        else:
+            continue
+        
+        directories.append(info)
+    
+    # render the template to a string
+    body = Template(filename=template).render(
+        total_size=total_size,
+        directories=directories,
+        config=config,
+        h=webhelpers.html.tags)
+    
+    # write the rendered output
+    if nonatomic:
+        print >>file(output,'w'), body
+    else:
+        atomic_write(output, body)
+
+
+if __name__ == "__main__":
+    main()
diff --git a/misc/debian-check-md5sum b/misc/debian-check-md5sum
new file mode 100755 (executable)
index 0000000..f52e72d
--- /dev/null
@@ -0,0 +1,119 @@
+#!/usr/bin/python2.5
+import sys, os, re, gzip, bz2, hashlib
+
+package_file_map = {
+    'Packages' : file,
+    'Packages.gz' : gzip.GzipFile,
+    'Packages.bz2' : bz2.BZ2File,
+    'Sources' : file,
+    'Sources.gz' : gzip.GzipFile,
+    'Sources.bz2' : bz2.BZ2File,
+}
+
+def parse_packages_file(path):
+    try:
+        open_func = package_file_map[os.path.basename(path)]
+        file = open_func(path)
+    except IOError, e:
+        print "WARNING: failed to open %s: %s" % (path, e)
+        return {}
+    cur_dict = {}
+    key, value = None, ''
+    ret_list = []
+    while True:
+        try:
+            line = file.readline()
+        except IOError, e:
+            print "WARNING: failed to read %s: %s" % (path, e)
+            print "WARNING: %s" % e
+            return {}
+
+        # check if we are done with current value
+        if (line == '' or line[0] == '\n' or line[0] != ' ') and key != None:
+            cur_dict[key] = value
+
+        if line == '' or line == '\n': # done current block
+            if cur_dict != {}:
+                ret_list.append(cur_dict)
+                cur_dict = {}
+                key = None
+            if line == '': break
+        elif line[0] == ' ': # multi-line value
+            value += '\n' + line[1:-1]
+        else:
+            if line[-1] == '\n': line = line[:-1]
+            pos = line.find(':')
+            key = line[:pos]
+            if key == '': key = None
+            value = line[pos+2:]
+    return ret_list
+
+def find_packages_files(path):
+    files = []
+    for file in os.listdir(path):
+        file_path = "%s/%s" % (path, file)
+        if os.path.islink(file_path):
+            continue
+        elif os.path.isdir(file_path):
+            files += find_packages_files(file_path)
+        elif file in package_file_map:
+            files.append(file_path)
+    return files
+
+if len(sys.argv) != 2:
+    print "Usage: debian-check-md5sum.py base-dir"
+    sys.exit(1)
+base_dir = sys.argv[1]
+
+all = {}
+files_regex = re.compile('(\S+)\s+(\S+)\s+(\S+)')
+for file in find_packages_files(base_dir):
+    file_type = os.path.basename(file).split('.')[0]
+    a = parse_packages_file(file)
+    for package in parse_packages_file(file):
+        if file_type == 'Packages':
+            if 'Filename' in package:
+                all[package['Filename']] = package
+        elif file_type == 'Sources':
+            files = package['Files'].split('\n')
+            for file in files:
+                if file == '': continue
+                match = files_regex.match(file)
+                file_path = '%s/%s' % (package['Directory'], match.group(3))
+                all[file_path] = { 'MD5sum' : match.group(1) }
+print "NOTICE:  need to check %d files" % len(all)
+
+ret_val = 0
+block_size = 65536
+for (file, package) in all.iteritems():
+    path = '%s/%s' % (base_dir, file)
+    try:
+        file = open(path, 'rb')
+    except IOError:
+        print "WARNING: missing %s" % path
+        continue
+    if 'SHA256' in package:
+        md = hashlib.sha256()
+        hash = package['SHA256']
+    elif 'SHA1' in package:
+        md = hashlib.sha1()
+        hash = package['SHA1']
+    elif 'MD5sum' in package:
+        md = hashlib.md5()
+        hash = package['MD5sum']
+    else:
+        print "WARNING: no hash found for %s" % path
+        print package
+        exit(1)
+    while True:
+        data = file.read(block_size)
+        if data == '': break
+        md.update(data)
+    hash_calc = md.hexdigest()
+    if hash == hash_calc:
+        print "NOTICE:  hash ok for %s [hash = %s]" % (path, hash)
+    else:
+        print "ERROR:   hash mismatch for %s [hash = %s, hash_calc = %s]" % \
+            (path, hash, hash_calc)
+        ret_val = 1
+exit(ret_val)
diff --git a/orionroutes.py b/orionroutes.py
deleted file mode 100755 (executable)
index 1d377da..0000000
+++ /dev/null
@@ -1,183 +0,0 @@
-#!/usr/bin/python
-
-# Configuration
-ORION_TABLE =   1 # from /etc/iproute2/rt_tables
-ORION_REALMS =  1 # from /etc/iproute2/rt_realms
-ORION_VIAS =    [ "66.97.23.33", "66.97.28.65", "129.97.1.46" ]
-ORION_GW =      "129.97.134.1"
-ORION_SRC =     "129.97.134.42"
-ORION_IFACE =   "eth0"
-
-# Don't touch anything beyond here
-
-import sys, iplib, SubnetTree
-from ctypes import *
-
-NETLINK_ROUTE =         0
-AF_UNSPEC =             0
-RT_SCOPE_UNIVERSE =     0
-RTPROT_STATIC =         4
-NLM_F_REPLACE =         0x100
-
-def die(msg):
-    sys.stderr.write("orionroutes.py: %s\n" % msg)
-    sys.exit(1)
-
-try:
-    libnl = cdll.LoadLibrary("libnl.so.1")
-    nl_geterror = CFUNCTYPE(c_char_p) (("nl_geterror", libnl), None)
-    nl_handle_alloc = CFUNCTYPE(c_void_p) (("nl_handle_alloc", libnl), None)
-    nl_connect = CFUNCTYPE(c_int, c_void_p, c_int) \
-        (("nl_connect", libnl), ((1, "handle", None), (1, "type", NETLINK_ROUTE)))
-    rtnl_route_alloc = CFUNCTYPE(c_void_p) (("rtnl_route_alloc", libnl), None)
-    rtnl_link_alloc_cache = CFUNCTYPE(c_void_p, c_void_p) \
-        (("rtnl_link_alloc_cache", libnl), ((1, "handle", None), ))
-    rtnl_link_name2i = CFUNCTYPE(c_int, c_void_p, c_char_p) \
-        (("rtnl_link_name2i", libnl), ((1, "cache", None), (1, "iface", -1)))
-    rtnl_route_set_oif = CFUNCTYPE(c_void_p, c_void_p, c_int) \
-        (("rtnl_route_set_oif", libnl), ((1, "route", None), (1, "iface", -1)))
-    nl_cache_free = CFUNCTYPE(None, c_void_p) \
-        (("nl_cache_free", libnl), ((1, "cache", None), ))
-    nl_addr_parse = CFUNCTYPE(c_void_p, c_char_p, c_int) \
-        (("nl_addr_parse", libnl), ((1, "dst", None), (1, "family", AF_UNSPEC)))
-    rtnl_route_set_dst = CFUNCTYPE(c_int, c_void_p, c_void_p) \
-        (("rtnl_route_set_dst", libnl), ((1, "route", None), (1, "dst", None)))
-    rtnl_route_set_pref_src = CFUNCTYPE(c_int, c_void_p, c_void_p) \
-        (("rtnl_route_set_pref_src", libnl), ((1, "route", None), (1, "src", None)))
-    nl_addr_put = CFUNCTYPE(None, c_void_p) \
-        (("nl_addr_put", libnl), ((1, "addr", None), ))
-    rtnl_route_set_gateway = CFUNCTYPE(c_int, c_void_p, c_void_p) \
-        (("rtnl_route_set_gateway", libnl), ((1, "route", None), (1, "gw", None)))
-    rtnl_route_set_table = CFUNCTYPE(None, c_void_p, c_int) \
-        (("rtnl_route_set_table", libnl), ((1, "route", None), (1, "table", -1)))
-    rtnl_route_set_scope = CFUNCTYPE(None, c_void_p, c_int) \
-        (("rtnl_route_set_scope", libnl), ((1, "route", None), (1, "scope", -1)))
-    rtnl_route_set_protocol = CFUNCTYPE(None, c_void_p, c_int) \
-        (("rtnl_route_set_protocol", libnl), ((1, "route", None), (1, "proto", -1)))
-    rtnl_route_set_realms = CFUNCTYPE(None, c_void_p, c_int) \
-        (("rtnl_route_set_realms", libnl), ((1, "route", None), (1, "realms", -1)))
-    rtnl_route_add = CFUNCTYPE(c_int, c_void_p, c_void_p, c_int) \
-        (("rtnl_route_add", libnl), ((1, "handle", None), (1, "route", None), (1, "flags", 0)))
-    rtnl_route_put = CFUNCTYPE(None, c_void_p) \
-        (("rtnl_route_put", libnl), ((1, "route", None), ))
-    nl_handle_destroy = CFUNCTYPE(None, c_void_p) \
-        (("nl_handle_destroy", libnl), ((1, "handle", None), ))
-    rtnl_route_alloc_cache = CFUNCTYPE(c_void_p, c_void_p) \
-        (("rtnl_route_alloc_cache", libnl), ((1, "handle", None), ))
-    nl_cache_get_first = CFUNCTYPE(c_void_p, c_void_p) \
-        (("nl_cache_get_first", libnl), ((1, "cache", None), ))
-    rtnl_route_get_table = CFUNCTYPE(c_int, c_void_p) \
-        (("rtnl_route_get_table", libnl), ((1, "route", None), ))
-    rtnl_route_get_dst = CFUNCTYPE(c_void_p, c_void_p) \
-        (("rtnl_route_get_dst", libnl), ((1, "route", None), ))
-    nl_addr2str = CFUNCTYPE(c_char_p, c_void_p, c_char_p, c_int) \
-        (("nl_addr2str", libnl), ((1, "addr", None), (1, "buffer", None), (1, "size", 0)))
-    rtnl_route_del = CFUNCTYPE(c_int, c_void_p, c_void_p, c_int) \
-        (("rtnl_route_del", libnl), ((1, "handle", None), (1, "route", None), (1, "flags", 0)))
-    nl_cache_get_next = CFUNCTYPE(c_void_p, c_void_p) \
-        (("nl_cache_get_next", libnl), ((1, "object", None), ))
-except Exception,e:
-    die("Failed to load libnl: %s" % e)
-
-def nl_die(func):
-    die("%s: %s" % (func, nl_geterror()))
-
-ips = [[] for i in range(33)]
-for line in sys.stdin:
-    try:
-        ip, mask, via = line.strip().split(',')[0:3]
-    except ValueError:
-        die("Malformed line: %s" % line.strip())
-
-    if via not in ORION_VIAS:
-        continue
-    bits = int(iplib.IPv4NetMask(mask).get_bits())
-    ips[bits].append(int(iplib.IPv4Address(ip)))
-
-count = sum([len(ip_list) for ip_list in ips])
-if count < 10:
-    die("Not enough routes (got %d)" % count)
-
-cidrs = []
-for bits in range(32, 1, -1):
-    ips[bits].sort()
-    last_ip = 0
-    for ip in ips[bits]:
-        if ip != last_ip and (ip ^ last_ip) == (1 << (32 - bits)):
-            ips[bits - 1].append(ip & (((1 << (bits - 1)) - 1) << (32 - (bits - 1))))
-            last_ip = 0
-        elif last_ip != 0:
-            cidrs.append((iplib.IPv4Address(last_ip), bits))
-        last_ip = ip
-    if last_ip != 0:
-        cidrs.append((iplib.IPv4Address(last_ip), bits))
-
-nlh = nl_handle_alloc()
-if nlh == None: nl_die("nl_handle_alloc")
-if nl_connect(nlh, NETLINK_ROUTE) < 0: nl_die("nl_connect")
-
-link_cache = rtnl_link_alloc_cache(nlh)
-if link_cache == None: nl_die("rtnl_link_alloc")
-iface = rtnl_link_name2i(link_cache, ORION_IFACE)
-if iface < 0: nl_die("rtnl_link_name2i")
-nl_cache_free(link_cache)
-
-cidrs.sort(lambda (ip1, bits1), (ip2, bits2): cmp(ip1, ip2) if bits1 == bits2 else (bits1 - bits2))
-tree = SubnetTree.SubnetTree()
-for (ip, bits) in cidrs:
-    if str(ip) not in tree:
-        cidr = "%s/%s" % (ip, bits)
-        tree[cidr] = None
-
-        route = rtnl_route_alloc()
-        if route == None: nl_die("rtnl_route_alloc")
-
-        dstaddr = nl_addr_parse(cidr, AF_UNSPEC)
-        if dstaddr == None: nl_die("nl_addr_parse(%s)" % cidr)
-        if rtnl_route_set_dst(route, dstaddr) < 0: nl_die("rtnl_route_set_dst")
-        nl_addr_put(dstaddr)
-
-        srcaddr = nl_addr_parse(ORION_SRC, AF_UNSPEC)
-        if srcaddr == None: nl_die("nl_addr_parse(%s)" % ORION_SRC)
-        if rtnl_route_set_pref_src(route, srcaddr) < 0: nl_die("nl_route_set_pref_src")
-        nl_addr_put(srcaddr)
-
-        gwaddr = nl_addr_parse(ORION_GW, AF_UNSPEC)
-        if gwaddr == None: nl_die("nl_addr_parse(%s)" % ORION_GW)
-        if rtnl_route_set_gateway(route, gwaddr) < 0: nl_die("nl_route_set_gateway")
-        nl_addr_put(gwaddr)
-
-        rtnl_route_set_oif(route, iface)
-        rtnl_route_set_table(route, ORION_TABLE)
-        rtnl_route_set_scope(route, RT_SCOPE_UNIVERSE)
-        rtnl_route_set_protocol(route, RTPROT_STATIC)
-        rtnl_route_set_realms(route, ORION_REALMS)
-
-        if rtnl_route_add(nlh, route, NLM_F_REPLACE) < 0: nl_die("rtnl_route_add(dst=%s)" % cidr)
-        rtnl_route_put(route)
-
-route_cache = rtnl_route_alloc_cache(nlh)
-if route_cache == None: nl_die("rtnl_route_alloc_cache")
-dstaddr_s = create_string_buffer(100)
-
-route = nl_cache_get_first(route_cache)
-while route != None:
-    table = rtnl_route_get_table(route)
-    if table != ORION_TABLE:
-        route = nl_cache_get_next(route)
-        continue
-
-    dstaddr = rtnl_route_get_dst(route)
-    if dstaddr == None:
-        continue
-    if nl_addr2str(dstaddr, dstaddr_s, sizeof(dstaddr_s)) == None: nl_die("nl_addr2str")
-    dstaddr = str(repr(dstaddr_s.value)).strip('\'').split('/')[0]
-
-    if dstaddr not in tree:
-        rtnl_route_del(nlh, route, 0)
-
-    route = nl_cache_get_next(route)
-
-nl_cache_free(route_cache)
-
-nl_handle_destroy(nlh)
diff --git a/routing/csc-mirror b/routing/csc-mirror
new file mode 100644 (file)
index 0000000..26dd78c
--- /dev/null
@@ -0,0 +1,22 @@
+# /etc/cron.d/csc-mirror: mirror cron jobs
+
+# m h dom mon dow user command
+
+# update orion routes
+30 5 * * * root /usr/local/sbin/update-orion-routes
+
+# make torrents
+*/10 * * * * mirror /home/mirror/bin/make-torrents > /dev/null 2> /dev/null
+
+# The rsync cron jobs are now run by a small script a2brenna wrote
+# that works a bit more intelligently than cron. For one thing, it
+# won't kick off a sync when one's already running. Please see
+# ~mirror/merlin.
+#  -- mspang
+
+# regenerate mirror index at 5:40 am on 14th & 28th of every month
+# feel free to run this manually if you've added or removed an
+# archive or some such thing
+#
+# Documented here: http://wiki.csclub.uwaterloo.ca/Mirror#Index
+40 5 */14 * * mirror cd /home/mirror/mirror-index && /home/mirror/mirror-index/make-index.py
diff --git a/routing/interfaces b/routing/interfaces
new file mode 100644 (file)
index 0000000..9ae14b7
--- /dev/null
@@ -0,0 +1,32 @@
+# This file describes the network interfaces available on your system
+# and how to activate them. For more information, see interfaces(5).
+
+# The loopback network interface
+auto lo
+iface lo inet loopback
+
+# The routes added here will not be visible to the 'route' command; you
+# should use 'ip route show table foo' instead.
+
+auto eth0
+iface eth0 inet static
+       address 129.97.134.42
+       netmask 255.255.255.0
+       gateway 129.97.134.1
+       up   ip rule add from all lookup campus prio 1
+       down ip rule del from all lookup campus prio 1
+       up   ip rule add from all lookup orion prio 2
+       down ip rule del from all lookup orion prio 2
+       up   ip route add 129.97.0.0/16  via 129.97.134.1 dev eth0 table campus realm campus
+       down ip route del 129.97.0.0/16  via 129.97.134.1 dev eth0 table campus realm campus
+       up   ip route add 10.0.0.0/8     via 129.97.134.1 dev eth0 table campus realm campus
+       down ip route del 10.0.0.0/8     via 129.97.134.1 dev eth0 table campus realm campus
+       up   ip route add 172.16.0.0/20  via 129.97.134.1 dev eth0 table campus realm campus
+       down ip route del 172.16.0.0/20  via 129.97.134.1 dev eth0 table campus realm campus
+       up   ip route add 192.168.0.0/16 via 129.97.134.1 dev eth0 table campus realm campus
+       down ip route del 192.168.0.0/16 via 129.97.134.1 dev eth0 table campus realm campus
+
+auto eth0:mirror
+iface eth0:mirror inet static
+       address 129.97.134.71
+       netmask 255.255.255.0
diff --git a/routing/orionroutes.py b/routing/orionroutes.py
new file mode 100755 (executable)
index 0000000..829279f
--- /dev/null
@@ -0,0 +1,186 @@
+#!/usr/bin/python
+
+# This file updates the orion routing table.
+# Put it at /usr/local/sbin/orionroutes.py
+
+# Configuration
+ORION_TABLE =   1 # from /etc/iproute2/rt_tables
+ORION_REALMS =  1 # from /etc/iproute2/rt_realms
+ORION_VIAS =    [ "66.97.23.33", "66.97.28.65", "129.97.1.46" ]
+ORION_GW =      "129.97.134.1"
+ORION_SRC =     "129.97.134.42"
+ORION_IFACE =   "eth0"
+
+# Don't touch anything beyond here
+
+import sys, iplib, SubnetTree
+from ctypes import *
+
+NETLINK_ROUTE =         0
+AF_UNSPEC =             0
+RT_SCOPE_UNIVERSE =     0
+RTPROT_STATIC =         4
+NLM_F_REPLACE =         0x100
+
+def die(msg):
+    sys.stderr.write("orionroutes.py: %s\n" % msg)
+    sys.exit(1)
+
+try:
+    libnl = cdll.LoadLibrary("libnl.so.1")
+    nl_geterror = CFUNCTYPE(c_char_p) (("nl_geterror", libnl), None)
+    nl_handle_alloc = CFUNCTYPE(c_void_p) (("nl_handle_alloc", libnl), None)
+    nl_connect = CFUNCTYPE(c_int, c_void_p, c_int) \
+        (("nl_connect", libnl), ((1, "handle", None), (1, "type", NETLINK_ROUTE)))
+    rtnl_route_alloc = CFUNCTYPE(c_void_p) (("rtnl_route_alloc", libnl), None)
+    rtnl_link_alloc_cache = CFUNCTYPE(c_void_p, c_void_p) \
+        (("rtnl_link_alloc_cache", libnl), ((1, "handle", None), ))
+    rtnl_link_name2i = CFUNCTYPE(c_int, c_void_p, c_char_p) \
+        (("rtnl_link_name2i", libnl), ((1, "cache", None), (1, "iface", -1)))
+    rtnl_route_set_oif = CFUNCTYPE(c_void_p, c_void_p, c_int) \
+        (("rtnl_route_set_oif", libnl), ((1, "route", None), (1, "iface", -1)))
+    nl_cache_free = CFUNCTYPE(None, c_void_p) \
+        (("nl_cache_free", libnl), ((1, "cache", None), ))
+    nl_addr_parse = CFUNCTYPE(c_void_p, c_char_p, c_int) \
+        (("nl_addr_parse", libnl), ((1, "dst", None), (1, "family", AF_UNSPEC)))
+    rtnl_route_set_dst = CFUNCTYPE(c_int, c_void_p, c_void_p) \
+        (("rtnl_route_set_dst", libnl), ((1, "route", None), (1, "dst", None)))
+    rtnl_route_set_pref_src = CFUNCTYPE(c_int, c_void_p, c_void_p) \
+        (("rtnl_route_set_pref_src", libnl), ((1, "route", None), (1, "src", None)))
+    nl_addr_put = CFUNCTYPE(None, c_void_p) \
+        (("nl_addr_put", libnl), ((1, "addr", None), ))
+    rtnl_route_set_gateway = CFUNCTYPE(c_int, c_void_p, c_void_p) \
+        (("rtnl_route_set_gateway", libnl), ((1, "route", None), (1, "gw", None)))
+    rtnl_route_set_table = CFUNCTYPE(None, c_void_p, c_int) \
+        (("rtnl_route_set_table", libnl), ((1, "route", None), (1, "table", -1)))
+    rtnl_route_set_scope = CFUNCTYPE(None, c_void_p, c_int) \
+        (("rtnl_route_set_scope", libnl), ((1, "route", None), (1, "scope", -1)))
+    rtnl_route_set_protocol = CFUNCTYPE(None, c_void_p, c_int) \
+        (("rtnl_route_set_protocol", libnl), ((1, "route", None), (1, "proto", -1)))
+    rtnl_route_set_realms = CFUNCTYPE(None, c_void_p, c_int) \
+        (("rtnl_route_set_realms", libnl), ((1, "route", None), (1, "realms", -1)))
+    rtnl_route_add = CFUNCTYPE(c_int, c_void_p, c_void_p, c_int) \
+        (("rtnl_route_add", libnl), ((1, "handle", None), (1, "route", None), (1, "flags", 0)))
+    rtnl_route_put = CFUNCTYPE(None, c_void_p) \
+        (("rtnl_route_put", libnl), ((1, "route", None), ))
+    nl_handle_destroy = CFUNCTYPE(None, c_void_p) \
+        (("nl_handle_destroy", libnl), ((1, "handle", None), ))
+    rtnl_route_alloc_cache = CFUNCTYPE(c_void_p, c_void_p) \
+        (("rtnl_route_alloc_cache", libnl), ((1, "handle", None), ))
+    nl_cache_get_first = CFUNCTYPE(c_void_p, c_void_p) \
+        (("nl_cache_get_first", libnl), ((1, "cache", None), ))
+    rtnl_route_get_table = CFUNCTYPE(c_int, c_void_p) \
+        (("rtnl_route_get_table", libnl), ((1, "route", None), ))
+    rtnl_route_get_dst = CFUNCTYPE(c_void_p, c_void_p) \
+        (("rtnl_route_get_dst", libnl), ((1, "route", None), ))
+    nl_addr2str = CFUNCTYPE(c_char_p, c_void_p, c_char_p, c_int) \
+        (("nl_addr2str", libnl), ((1, "addr", None), (1, "buffer", None), (1, "size", 0)))
+    rtnl_route_del = CFUNCTYPE(c_int, c_void_p, c_void_p, c_int) \
+        (("rtnl_route_del", libnl), ((1, "handle", None), (1, "route", None), (1, "flags", 0)))
+    nl_cache_get_next = CFUNCTYPE(c_void_p, c_void_p) \
+        (("nl_cache_get_next", libnl), ((1, "object", None), ))
+except Exception,e:
+    die("Failed to load libnl: %s" % e)
+
+def nl_die(func):
+    die("%s: %s" % (func, nl_geterror()))
+
+ips = [[] for i in range(33)]
+for line in sys.stdin:
+    try:
+        ip, mask, via = line.strip().split(',')[0:3]
+    except ValueError:
+        die("Malformed line: %s" % line.strip())
+
+    if via not in ORION_VIAS:
+        continue
+    bits = int(iplib.IPv4NetMask(mask).get_bits())
+    ips[bits].append(int(iplib.IPv4Address(ip)))
+
+count = sum([len(ip_list) for ip_list in ips])
+if count < 10:
+    die("Not enough routes (got %d)" % count)
+
+cidrs = []
+for bits in range(32, 1, -1):
+    ips[bits].sort()
+    last_ip = 0
+    for ip in ips[bits]:
+        if ip != last_ip and (ip ^ last_ip) == (1 << (32 - bits)):
+            ips[bits - 1].append(ip & (((1 << (bits - 1)) - 1) << (32 - (bits - 1))))
+            last_ip = 0
+        elif last_ip != 0:
+            cidrs.append((iplib.IPv4Address(last_ip), bits))
+        last_ip = ip
+    if last_ip != 0:
+        cidrs.append((iplib.IPv4Address(last_ip), bits))
+
+nlh = nl_handle_alloc()
+if nlh == None: nl_die("nl_handle_alloc")
+if nl_connect(nlh, NETLINK_ROUTE) < 0: nl_die("nl_connect")
+
+link_cache = rtnl_link_alloc_cache(nlh)
+if link_cache == None: nl_die("rtnl_link_alloc")
+iface = rtnl_link_name2i(link_cache, ORION_IFACE)
+if iface < 0: nl_die("rtnl_link_name2i")
+nl_cache_free(link_cache)
+
+cidrs.sort(lambda (ip1, bits1), (ip2, bits2): cmp(ip1, ip2) if bits1 == bits2 else (bits1 - bits2))
+tree = SubnetTree.SubnetTree()
+for (ip, bits) in cidrs:
+    if str(ip) not in tree:
+        cidr = "%s/%s" % (ip, bits)
+        tree[cidr] = None
+
+        route = rtnl_route_alloc()
+        if route == None: nl_die("rtnl_route_alloc")
+
+        dstaddr = nl_addr_parse(cidr, AF_UNSPEC)
+        if dstaddr == None: nl_die("nl_addr_parse(%s)" % cidr)
+        if rtnl_route_set_dst(route, dstaddr) < 0: nl_die("rtnl_route_set_dst")
+        nl_addr_put(dstaddr)
+
+        srcaddr = nl_addr_parse(ORION_SRC, AF_UNSPEC)
+        if srcaddr == None: nl_die("nl_addr_parse(%s)" % ORION_SRC)
+        if rtnl_route_set_pref_src(route, srcaddr) < 0: nl_die("nl_route_set_pref_src")
+        nl_addr_put(srcaddr)
+
+        gwaddr = nl_addr_parse(ORION_GW, AF_UNSPEC)
+        if gwaddr == None: nl_die("nl_addr_parse(%s)" % ORION_GW)
+        if rtnl_route_set_gateway(route, gwaddr) < 0: nl_die("nl_route_set_gateway")
+        nl_addr_put(gwaddr)
+
+        rtnl_route_set_oif(route, iface)
+        rtnl_route_set_table(route, ORION_TABLE)
+        rtnl_route_set_scope(route, RT_SCOPE_UNIVERSE)
+        rtnl_route_set_protocol(route, RTPROT_STATIC)
+        rtnl_route_set_realms(route, ORION_REALMS)
+
+        if rtnl_route_add(nlh, route, NLM_F_REPLACE) < 0: nl_die("rtnl_route_add(dst=%s)" % cidr)
+        rtnl_route_put(route)
+
+route_cache = rtnl_route_alloc_cache(nlh)
+if route_cache == None: nl_die("rtnl_route_alloc_cache")
+dstaddr_s = create_string_buffer(100)
+
+route = nl_cache_get_first(route_cache)
+while route != None:
+    table = rtnl_route_get_table(route)
+    if table != ORION_TABLE:
+        route = nl_cache_get_next(route)
+        continue
+
+    dstaddr = rtnl_route_get_dst(route)
+    if dstaddr == None:
+        continue
+    if nl_addr2str(dstaddr, dstaddr_s, sizeof(dstaddr_s)) == None: nl_die("nl_addr2str")
+    dstaddr = str(repr(dstaddr_s.value)).strip('\'').split('/')[0]
+
+    if dstaddr not in tree:
+        rtnl_route_del(nlh, route, 0)
+
+    route = nl_cache_get_next(route)
+
+nl_cache_free(route_cache)
+
+nl_handle_destroy(nlh)
diff --git a/routing/setup-routing b/routing/setup-routing
new file mode 100644 (file)
index 0000000..199636d
--- /dev/null
@@ -0,0 +1,10 @@
+# This file configures the packet scheduler
+
+tc qdisc  del dev eth0 parent root
+tc qdisc  add dev eth0 parent root handle 1: htb default 2 r2q 10000
+tc class  add dev eth0 parent 1:   classid 1:1 htb rate 1000Mbit
+tc class  add dev eth0 parent 1:1  classid 1:2 htb rate 100Mbit
+tc class  add dev eth0 parent 1:1  classid 1:3 htb rate 200Mbit
+tc class  add dev eth0 parent 1:1  classid 1:4 htb rate 700Mbit ceil 1000Mbit
+tc filter add dev eth0 parent 1:   protocol ip pref 2 route to orion  flowid 1:3
+tc filter add dev eth0 parent 1:   protocol ip pref 1 route to campus flowid 1:4
diff --git a/routing/update-orion-routes b/routing/update-orion-routes
new file mode 100755 (executable)
index 0000000..394745e
--- /dev/null
@@ -0,0 +1,6 @@
+#!/bin/bash
+
+# This file updates the orion routing table.
+# Put it at /usr/local/sbin/update-orion-routes.
+
+wget --quiet -O - http://noc.uwaterloo.ca/~ns-owner/ext-routes.txt | /usr/local/sbin/orionroutes.py
diff --git a/rtorrent-init.d b/rtorrent-init.d
deleted file mode 100755 (executable)
index 69d398c..0000000
+++ /dev/null
@@ -1,56 +0,0 @@
-#!/bin/sh
-
-. /lib/lsb/init-functions
-
-PATH=$PATH:/bin:/usr/bin:/sbin:/usr/sbin
-NAME=rtorrent
-PIDFILE=/var/run/$NAME.screen
-CHUSER=$NAME
-DAEMON=/usr/bin/rtorrent
-DAEMON_ARGS="-n -o try_import=/etc/rtorrent.rc"
-
-do_start()
-{
-    if [ -s $PIDFILE ] && kill -0 $(cat $PIDFILE) >/dev/null 2>&1; then
-        exit 0
-    fi
-    log_daemon_msg "Starting" $NAME
-    start-stop-daemon --start --quiet --background --pidfile $PIDFILE \
-        --make-pidfile --exec /bin/su -- \
-        $CHUSER -c "/usr/bin/screen -D -m -- $DAEMON $DAEMON_ARGS"
-    log_end_msg 0
-}
-
-do_stop()
-{
-    log_daemon_msg "Stopping" $NAME
-    start-stop-daemon --stop --quiet --pidfile $PIDFILE --oknodo
-    log_end_msg 0
-}
-
-do_status()
-{
-    if [ -s $PIDFILE ] && kill -0 $(cat $PIDFILE) >/dev/null 2>&1; then
-        exit 0
-    else
-        exit 4
-    fi
-}
-
-case "$1" in
-    start)
-        do_start
-    ;;
-    stop)
-        do_stop
-    ;;
-    restart)
-        do_stop
-        sleep 4
-        do_start
-    ;;
-    status)
-        do_status
-esac
-
-exit 0
diff --git a/rtorrent.rc b/rtorrent.rc
deleted file mode 100644 (file)
index 9b90cf7..0000000
+++ /dev/null
@@ -1,100 +0,0 @@
-# This is an example resource file for rTorrent. Copy to
-# ~/.rtorrent.rc and enable/modify the options as needed. Remember to
-# uncomment the options you wish to enable.
-
-# Maximum and minimum number of peers to connect to per torrent.
-#min_peers = 40
-#max_peers = 100
-
-# Same as above but for seeding completed torrents (-1 = same as downloading)
-#min_peers_seed = 10
-#max_peers_seed = 50
-
-# Maximum number of simultanious uploads per torrent.
-#max_uploads = 15
-
-# Global upload and download rate in KiB. "0" for unlimited.
-#download_rate = 0
-#upload_rate = 0
-
-# Default directory to save the downloaded torrents.
-directory = /mirror/root/csclub
-
-# Default session directory. Make sure you don't run multiple instance
-# of rtorrent using the same session directory. Perhaps using a
-# relative path?
-session = /var/lib/rtorrent/session
-
-# Watch a directory for new torrents, and stop those that have been
-# deleted.
-schedule = watch_www_directory,1,5,load_start=/www/files/*.torrent
-schedule = untied_directory,5,5,remove_untied=
-
-# Close torrents when diskspace is low.
-#schedule = low_diskspace,5,60,close_low_diskspace=100M
-
-# 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=200,200M,2000
-
-# The ip address reported to the tracker.
-#ip = 127.0.0.1
-#ip = rakshasa.no
-
-# The ip address the listening socket and outgoing connections is
-# bound to.
-bind = mirror
-
-# Port range to use for listening.
-port_range = 6900-6999
-
-# Start opening ports at a random position within the port range.
-#port_random = no
-
-# Check hash for finished torrents. Might be usefull until the bug is
-# fixed that causes lack of diskspace not to be properly reported.
-#check_hash = no
-
-# Set whetever the client should try to connect to UDP trackers.
-#use_udp_trackers = yes
-
-# Alternative calls to bind and ip that should handle dynamic ip's.
-#schedule = ip_tick,0,1800,ip=rakshasa
-#schedule = bind_tick,0,1800,bind=rakshasa
-
-encryption = allow_incoming,prefer_plaintext
-
-#
-# Do not modify the following parameters unless you know what you're doing.
-#
-
-# Hash read-ahead controls how many MB to request the kernel to read
-# ahead. If the value is too low the disk may not be fully utilized,
-# while if too high the kernel might not be able to keep the read
-# pages in memory thus end up trashing.
-#hash_read_ahead = 10
-
-# Interval between attempts to check the hash, in milliseconds.
-#hash_interval = 100
-
-# Number of attempts to check the hash while using the mincore status,
-# before forcing. Overworked systems might need lower values to get a
-# decent hash checking rate.
-#hash_max_tries = 10
-
-# Max number of files to keep open simultaniously.
-#max_open_files = 128
-
-# Number of sockets to simultaneously keep open.
-#max_open_sockets = <no default>
-
-
-# Example of scheduling commands: Switch between two ip's every 5
-# seconds.
-#schedule = "ip_tick1,5,10,ip=torretta"
-#schedule = "ip_tick2,10,10,ip=lampedusa"
-
-# Remove a scheduled event.
-#schedule_remove = "ip_tick1"
diff --git a/snmp/.gitignore b/snmp/.gitignore
new file mode 100644 (file)
index 0000000..1792b04
--- /dev/null
@@ -0,0 +1,3 @@
+/csc-snmp-subagent
+/mirror-stats
+*.o
diff --git a/snmp/CSC-MIB.txt b/snmp/CSC-MIB.txt
new file mode 100644 (file)
index 0000000..97a314d
--- /dev/null
@@ -0,0 +1,46 @@
+CSC-MIB DEFINITIONS ::= BEGIN
+
+IMPORTS
+    MODULE-IDENTITY, OBJECT-TYPE, Counter32, Gauge32, Counter64,
+    Integer32, TimeTicks, mib-2, enterprises,
+    NOTIFICATION-TYPE                        FROM SNMPv2-SMI
+    TEXTUAL-CONVENTION, DisplayString,
+    PhysAddress, TruthValue, RowStatus,
+    TimeStamp, AutonomousType, TestAndIncr   FROM SNMPv2-TC
+    MODULE-COMPLIANCE, OBJECT-GROUP,
+    NOTIFICATION-GROUP                       FROM SNMPv2-CONF
+    snmpTraps                                FROM SNMPv2-MIB
+    IANAifType                               FROM IANAifType-MIB;
+
+csclub OBJECT IDENTIFIER ::= { enterprises 27934 }
+
+cscMIB MODULE-IDENTITY
+    LAST-UPDATED "200905080000Z"
+    ORGANIZATION "University of Waterloo Computer Science Club"
+    CONTACT-INFO "systems-committee@csclub.uwaterloo.ca"
+    DESCRIPTION  "Computer Science Club Local MIBs"
+    REVISION     "200905080000Z"
+    DESCRIPTION  "Initial revision"
+    ::= { csclub 2 }
+
+mirror OBJECT IDENTIFIER ::= { cscMIB 2 }
+
+cogentBytes OBJECT-TYPE
+    SYNTAX      Counter64
+    MAX-ACCESS  read-only
+    STATUS      current
+    ::= { mirror 1 }
+
+orionBytes OBJECT-TYPE
+    SYNTAX      Counter64
+    MAX-ACCESS  read-only
+    STATUS      current
+    ::= { mirror 2 }
+
+campusBytes OBJECT-TYPE
+    SYNTAX      Counter64
+    MAX-ACCESS  read-only
+    STATUS      current
+    ::= { mirror 3 }
+
+END
diff --git a/snmp/Makefile b/snmp/Makefile
new file mode 100644 (file)
index 0000000..ae0ec1d
--- /dev/null
@@ -0,0 +1,11 @@
+LDFLAGS := -lnl $(shell net-snmp-config --base-agent-libs)
+CFLAGS := -g3 -O2 -Wall
+
+all: mirror-stats csc-snmp-subagent
+
+mirror-stats: mirror-stats.o mirror-nl-glue.o
+
+csc-snmp-subagent: csc-snmp-subagent.o mirror-mib.o mirror-nl-glue.o
+
+clean:
+       rm -f *.o mirror-stats csc-snmp-subagent
diff --git a/snmp/csc-snmp-subagent.c b/snmp/csc-snmp-subagent.c
new file mode 100644 (file)
index 0000000..103760b
--- /dev/null
@@ -0,0 +1,221 @@
+/* generated from net-snmp-config */
+#include <net-snmp/net-snmp-config.h>
+#ifdef HAVE_SIGNAL
+#include <signal.h>
+#endif /* HAVE_SIGNAL */
+#include <net-snmp/net-snmp-includes.h>
+#include <net-snmp/agent/net-snmp-agent-includes.h>
+  #include "mirror-mib.h"
+const char *app_name = "cscMIB";
+
+extern int netsnmp_running;
+
+#ifdef __GNUC__
+#define UNUSED __attribute__((unused))
+#else
+#define UNUSED
+#endif
+
+RETSIGTYPE
+stop_server(UNUSED int a) {
+    netsnmp_running = 0;
+}
+
+static void
+usage(const char *prog)
+{
+    fprintf(stderr,
+            "USAGE: %s [OPTIONS]\n"
+            "\n"
+            "OPTIONS:\n", prog);
+
+    fprintf(stderr,
+            "  -d\t\t\tdump all traffic\n"
+            "  -D TOKEN[,...]\tturn on debugging output for the specified "
+            "TOKENs\n"
+            "\t\t\t   (ALL gives extremely verbose debugging output)\n"
+            "  -f\t\t\tDo not fork() from the calling shell.\n"
+            "  -h\t\t\tdisplay this help message\n"
+            "  -H\t\t\tdisplay a list of configuration file directives\n"
+            "  -L LOGOPTS\t\tToggle various defaults controlling logging:\n");
+    snmp_log_options_usage("\t\t\t  ", stderr);
+#ifndef DISABLE_MIB_LOADING
+    fprintf(stderr,
+            "  -m MIB[:...]\t\tload given list of MIBs (ALL loads "
+            "everything)\n"
+            "  -M DIR[:...]\t\tlook in given list of directories for MIBs\n");
+#endif /* DISABLE_MIB_LOADING */
+#ifndef DISABLE_MIB_LOADING
+    fprintf(stderr,
+            "  -P MIBOPTS\t\tToggle various defaults controlling mib "
+            "parsing:\n");
+    snmp_mib_toggle_options_usage("\t\t\t  ", stderr);
+#endif /* DISABLE_MIB_LOADING */
+    fprintf(stderr,
+            "  -v\t\t\tdisplay package version number\n"
+            "  -x TRANSPORT\tconnect to master agent using TRANSPORT\n");
+    exit(1);
+}
+
+static void
+version(void)
+{
+    fprintf(stderr, "NET-SNMP version: %s\n", netsnmp_get_version());
+    exit(0);
+}
+
+int
+main (int argc, char **argv)
+{
+  int arg;
+  char* cp = NULL;
+  int dont_fork = 0, do_help = 0;
+
+  while ((arg = getopt(argc, argv, "dD:fhHL:"
+#ifndef DISABLE_MIB_LOADING
+                       "m:M:"
+#endif /* DISABLE_MIB_LOADING */
+                       "n:"
+#ifndef DISABLE_MIB_LOADING
+                       "P:"
+#endif /* DISABLE_MIB_LOADING */
+                       "vx:")) != EOF) {
+    switch (arg) {
+    case 'd':
+      netsnmp_ds_set_boolean(NETSNMP_DS_LIBRARY_ID,
+                             NETSNMP_DS_LIB_DUMP_PACKET, 1);
+      break;
+
+    case 'D':
+      debug_register_tokens(optarg);
+      snmp_set_do_debugging(1);
+      break;
+
+    case 'f':
+      dont_fork = 1;
+      break;
+
+    case 'h':
+      usage(argv[0]);
+      break;
+
+    case 'H':
+      do_help = 1;
+      break;
+
+    case 'L':
+      if (snmp_log_options(optarg, argc, argv) < 0) {
+        exit(1);
+      }
+      break;
+
+#ifndef DISABLE_MIB_LOADING
+    case 'm':
+      if (optarg != NULL) {
+        setenv("MIBS", optarg, 1);
+      } else {
+        usage(argv[0]);
+      }
+      break;
+
+    case 'M':
+      if (optarg != NULL) {
+        setenv("MIBDIRS", optarg, 1);
+      } else {
+        usage(argv[0]);
+      }
+      break;
+#endif /* DISABLE_MIB_LOADING */
+
+    case 'n':
+      if (optarg != NULL) {
+        app_name = optarg;
+        netsnmp_ds_set_string(NETSNMP_DS_LIBRARY_ID,
+                              NETSNMP_DS_LIB_APPTYPE, app_name);
+      } else {
+        usage(argv[0]);
+      }
+      break;
+
+#ifndef DISABLE_MIB_LOADING
+    case 'P':
+      cp = snmp_mib_toggle_options(optarg);
+      if (cp != NULL) {
+        fprintf(stderr, "Unknown parser option to -P: %c.\n", *cp);
+        usage(argv[0]);
+      }
+      break;
+#endif /* DISABLE_MIB_LOADING */
+
+    case 'v':
+      version();
+      break;
+
+    case 'x':
+      if (optarg != NULL) {
+        netsnmp_ds_set_string(NETSNMP_DS_APPLICATION_ID,
+                              NETSNMP_DS_AGENT_X_SOCKET, optarg);
+      } else {
+        usage(argv[0]);
+      }
+      break;
+
+    default:
+      fprintf(stderr, "invalid option: -%c\n", arg);
+      usage(argv[0]);
+      break;
+    }
+  }
+
+  if (do_help) {
+    netsnmp_ds_set_boolean(NETSNMP_DS_APPLICATION_ID,
+                           NETSNMP_DS_AGENT_NO_ROOT_ACCESS, 1);
+  } else {
+    /* we are a subagent */
+    netsnmp_ds_set_boolean(NETSNMP_DS_APPLICATION_ID,
+                           NETSNMP_DS_AGENT_ROLE, 1);
+
+    if (!dont_fork) {
+      if (netsnmp_daemonize(1, snmp_stderrlog_status()) != 0)
+        exit(1);
+    }
+
+    /* initialize tcpip, if necessary */
+    SOCK_STARTUP;
+  }
+
+  /* initialize the agent library */
+  init_agent(app_name);
+
+  /* initialize your mib code here */
+  init_mirror_mib();
+
+  /* cscMIB will be used to read cscMIB.conf files. */
+  init_snmp("cscMIB");
+
+  if (do_help) {
+    fprintf(stderr, "Configuration directives understood:\n");
+    read_config_print_usage("  ");
+    exit(0);
+  }
+
+  /* In case we received a request to stop (kill -TERM or kill -INT) */
+  netsnmp_running = 1;
+#ifdef SIGTERM
+  signal(SIGTERM, stop_server);
+#endif
+#ifdef SIGINT
+  signal(SIGINT, stop_server);
+#endif
+
+  /* main loop here... */
+  while(netsnmp_running) {
+    agent_check_and_process(1);
+  }
+
+  /* at shutdown time */
+  snmp_shutdown(app_name);
+  SOCK_CLEANUP;
+  exit(0);
+}
+
diff --git a/snmp/mirror-mib.c b/snmp/mirror-mib.c
new file mode 100644 (file)
index 0000000..4cd7e87
--- /dev/null
@@ -0,0 +1,109 @@
+/*
+ * Note: this file originally auto-generated by mib2c using
+ *        : mib2c.scalar.conf 11805 2005-01-07 09:37:18Z dts12 $
+ */
+
+#include <net-snmp/net-snmp-config.h>
+#include <net-snmp/net-snmp-includes.h>
+#include <net-snmp/agent/net-snmp-agent-includes.h>
+#include "mirror-mib.h"
+#include "mirror-nl-glue.h"
+
+void
+init_mirror_mib(void)
+{
+    static oid      cogentBytes_oid[] =
+        { 1, 3, 6, 1, 4, 1, 27934, 2, 2, 1 };
+    static oid      orionBytes_oid[] =
+        { 1, 3, 6, 1, 4, 1, 27934, 2, 2, 2 };
+    static oid      campusBytes_oid[] =
+        { 1, 3, 6, 1, 4, 1, 27934, 2, 2, 3 };
+
+    DEBUGMSGTL(("mirror_mib", "Initializing\n"));
+
+    mirror_stats_initialize();
+
+    netsnmp_register_scalar(netsnmp_create_handler_registration
+                            ("cogentBytes", handle_cogentBytes,
+                             cogentBytes_oid, OID_LENGTH(cogentBytes_oid),
+                             HANDLER_CAN_RONLY));
+    netsnmp_register_scalar(netsnmp_create_handler_registration
+                            ("orionBytes", handle_orionBytes,
+                             orionBytes_oid, OID_LENGTH(orionBytes_oid),
+                             HANDLER_CAN_RONLY));
+    netsnmp_register_scalar(netsnmp_create_handler_registration
+                            ("campusBytes", handle_campusBytes,
+                             campusBytes_oid, OID_LENGTH(campusBytes_oid),
+                             HANDLER_CAN_RONLY));
+}
+
+void explode_counter64(uint64_t num, struct counter64 *counter) {
+    counter->low = num & 0xFFFFFFFF;
+    counter->high = (num >> 32) & 0xFFFFFFFF;
+}
+
+int
+handle_cogentBytes(netsnmp_mib_handler *handler,
+                   netsnmp_handler_registration *reginfo,
+                   netsnmp_agent_request_info *reqinfo,
+                   netsnmp_request_info *requests)
+{
+    struct counter64 counter;
+    mirror_stats_refresh();
+    explode_counter64(get_class_byte_count(&cogent_class), &counter);
+
+    switch (reqinfo->mode) {
+        case MODE_GET:
+            snmp_set_var_typed_value(requests->requestvb, ASN_COUNTER64,
+                    (u_char *)&counter, sizeof(counter));
+            break;
+        default:
+            die("unknown mode");
+    }
+
+    return SNMP_ERR_NOERROR;
+}
+
+int
+handle_orionBytes(netsnmp_mib_handler *handler,
+                  netsnmp_handler_registration *reginfo,
+                  netsnmp_agent_request_info *reqinfo,
+                  netsnmp_request_info *requests)
+{
+    struct counter64 counter;
+    mirror_stats_refresh();
+    explode_counter64(get_class_byte_count(&orion_class), &counter);
+
+    switch (reqinfo->mode) {
+        case MODE_GET:
+            snmp_set_var_typed_value(requests->requestvb, ASN_COUNTER64,
+                    (u_char *)&counter, sizeof(counter));
+            break;
+        default:
+            die("unknown mode");
+    }
+
+    return SNMP_ERR_NOERROR;
+}
+
+int
+handle_campusBytes(netsnmp_mib_handler *handler,
+                   netsnmp_handler_registration *reginfo,
+                   netsnmp_agent_request_info *reqinfo,
+                   netsnmp_request_info *requests)
+{
+    struct counter64 counter;
+    mirror_stats_refresh();
+    explode_counter64(get_class_byte_count(&campus_class), &counter);
+
+    switch (reqinfo->mode) {
+        case MODE_GET:
+            snmp_set_var_typed_value(requests->requestvb, ASN_COUNTER64,
+                    (u_char *)&counter, sizeof(counter));
+            break;
+        default:
+            die("unknown mode");
+    }
+
+    return SNMP_ERR_NOERROR;
+}
diff --git a/snmp/mirror-mib.h b/snmp/mirror-mib.h
new file mode 100644 (file)
index 0000000..f707f97
--- /dev/null
@@ -0,0 +1,9 @@
+#ifndef MIRRORMIB_H
+#define MIRRORMIB_H
+
+void            init_mirror_mib(void);
+Netsnmp_Node_Handler handle_cogentBytes;
+Netsnmp_Node_Handler handle_orionBytes;
+Netsnmp_Node_Handler handle_campusBytes;
+
+#endif
diff --git a/snmp/mirror-nl-glue.c b/snmp/mirror-nl-glue.c
new file mode 100644 (file)
index 0000000..720ac6a
--- /dev/null
@@ -0,0 +1,102 @@
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <inttypes.h>
+#include <libgen.h>
+#include <netlink/route/class.h>
+#include <netlink/route/link.h>
+#include <netlink/cache-api.h>
+#include <netlink/object.h>
+#include "mirror-nl-glue.h"
+
+static struct nl_cache *link_cache, *class_cache;
+static struct rtnl_link *eth;
+static int ifindex;
+
+struct class_info cogent_class = { "cogent", "01:02", };
+struct class_info orion_class  = { "orion",  "01:03", };
+struct class_info campus_class = { "campus", "01:04", };
+
+static struct nl_handle *nl_handle;
+
+void die(const char *message) {
+    fprintf(stderr, "fatal: %s\n", message);
+    exit(1);
+}
+
+static void match_obj(struct nl_object *obj, void *arg) {
+    struct nl_object *needle = *(struct nl_object **)arg;
+    struct nl_object **ret = (struct nl_object **)arg + 1;
+
+    if (!*ret && nl_object_identical(obj, needle)) {
+        nl_object_get(obj);
+        *ret = obj;
+    }
+}
+
+static struct rtnl_class *get_class_by_id(char *id, int ifindex) {
+    uint32_t handle;
+    struct rtnl_class *needle;
+    struct nl_object *magic[2];
+
+    if (rtnl_tc_str2handle(id, &handle))
+        die("invalid id");
+
+    needle = rtnl_class_alloc();
+    rtnl_class_set_ifindex(needle, ifindex);
+    rtnl_class_set_handle(needle, handle);
+
+    magic[0] = (struct nl_object *)needle;
+    magic[1] = (struct nl_object *)NULL;
+
+    nl_cache_foreach(class_cache, match_obj, magic);
+
+    rtnl_class_put(needle);
+    return (struct rtnl_class *)magic[1];
+}
+
+uint64_t get_class_byte_count(struct class_info *info) {
+    struct rtnl_class *class = get_class_by_id(info->id, ifindex);
+    uint64_t bytes;
+    if (!class)
+        die("class not found");
+    bytes = rtnl_class_get_stat(class, RTNL_TC_BYTES);
+    rtnl_class_put(class);
+    return bytes;
+}
+
+void mirror_stats_refresh(void) {
+    nl_cache_refill(nl_handle, class_cache);
+}
+
+void mirror_stats_initialize(void) {
+    nl_handle = nl_handle_alloc();
+    if (!nl_handle)
+        die("unable to allocate handle");
+
+    if (nl_connect(nl_handle, NETLINK_ROUTE) < 0)
+        die("unable to connect to netlink");
+
+    link_cache = rtnl_link_alloc_cache(nl_handle);
+    if (!link_cache)
+        die("unable to allocate link cache");
+
+    eth = rtnl_link_get_by_name(link_cache, "eth0");
+    if (!eth)
+        die("unable to acquire eth0");
+    ifindex = rtnl_link_get_ifindex(eth);
+
+    class_cache = rtnl_class_alloc_cache(nl_handle, ifindex);
+    if (!class_cache)
+        die("unable to allocate class cache");
+}
+
+void mirror_stats_cleanup(void) {
+    rtnl_link_put(eth);
+    nl_cache_free(class_cache);
+    nl_cache_free(link_cache);
+    nl_close(nl_handle);
+    nl_handle_destroy(nl_handle);
+}
+
diff --git a/snmp/mirror-nl-glue.h b/snmp/mirror-nl-glue.h
new file mode 100644 (file)
index 0000000..53add1a
--- /dev/null
@@ -0,0 +1,25 @@
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <inttypes.h>
+#include <libgen.h>
+#include <netlink/route/class.h>
+#include <netlink/route/link.h>
+#include <netlink/cache-api.h>
+#include <netlink/object.h>
+
+struct class_info {
+    char *name;
+    char *id;
+};
+
+extern struct class_info cogent_class;
+extern struct class_info orion_class;
+extern struct class_info campus_class;
+
+void mirror_stats_refresh(void);
+void mirror_stats_initialize(void);
+void mirror_stats_cleanup(void);
+void die(const char *);
+uint64_t get_class_byte_count(struct class_info *);
diff --git a/snmp/mirror-stats.c b/snmp/mirror-stats.c
new file mode 100644 (file)
index 0000000..44bade4
--- /dev/null
@@ -0,0 +1,12 @@
+#include "mirror-nl-glue.h"
+
+int main(int argc, char *argv[]) {
+    mirror_stats_initialize();
+    for (;;) {
+        printf("%s %"PRIu64"\n", cogent_class.id, get_class_byte_count(&cogent_class));
+        printf("%s %"PRIu64"\n", orion_class.id, get_class_byte_count(&orion_class));
+        printf("%s %"PRIu64"\n", campus_class.id, get_class_byte_count(&campus_class));
+        sleep(1);
+        mirror_stats_refresh();
+    }
+}
diff --git a/torrents/rtorrent-init.d b/torrents/rtorrent-init.d
new file mode 100755 (executable)
index 0000000..69d398c
--- /dev/null
@@ -0,0 +1,56 @@
+#!/bin/sh
+
+. /lib/lsb/init-functions
+
+PATH=$PATH:/bin:/usr/bin:/sbin:/usr/sbin
+NAME=rtorrent
+PIDFILE=/var/run/$NAME.screen
+CHUSER=$NAME
+DAEMON=/usr/bin/rtorrent
+DAEMON_ARGS="-n -o try_import=/etc/rtorrent.rc"
+
+do_start()
+{
+    if [ -s $PIDFILE ] && kill -0 $(cat $PIDFILE) >/dev/null 2>&1; then
+        exit 0
+    fi
+    log_daemon_msg "Starting" $NAME
+    start-stop-daemon --start --quiet --background --pidfile $PIDFILE \
+        --make-pidfile --exec /bin/su -- \
+        $CHUSER -c "/usr/bin/screen -D -m -- $DAEMON $DAEMON_ARGS"
+    log_end_msg 0
+}
+
+do_stop()
+{
+    log_daemon_msg "Stopping" $NAME
+    start-stop-daemon --stop --quiet --pidfile $PIDFILE --oknodo
+    log_end_msg 0
+}
+
+do_status()
+{
+    if [ -s $PIDFILE ] && kill -0 $(cat $PIDFILE) >/dev/null 2>&1; then
+        exit 0
+    else
+        exit 4
+    fi
+}
+
+case "$1" in
+    start)
+        do_start
+    ;;
+    stop)
+        do_stop
+    ;;
+    restart)
+        do_stop
+        sleep 4
+        do_start
+    ;;
+    status)
+        do_status
+esac
+
+exit 0
diff --git a/torrents/rtorrent.rc b/torrents/rtorrent.rc
new file mode 100644 (file)
index 0000000..84dffa1
--- /dev/null
@@ -0,0 +1,100 @@
+# This is an example resource file for rTorrent. Copy to
+# ~/.rtorrent.rc and enable/modify the options as needed. Remember to
+# uncomment the options you wish to enable.
+
+# Maximum and minimum number of peers to connect to per torrent.
+#min_peers = 40
+#max_peers = 100
+
+# Same as above but for seeding completed torrents (-1 = same as downloading)
+#min_peers_seed = 10
+#max_peers_seed = 50
+
+# Maximum number of simultanious uploads per torrent.
+#max_uploads = 15
+
+# Global upload and download rate in KiB. "0" for unlimited.
+#download_rate = 0
+#upload_rate = 0
+
+# Default directory to save the downloaded torrents.
+directory = /mirror/root/csclub
+
+# Default session directory. Make sure you don't run multiple instance
+# of rtorrent using the same session directory. Perhaps using a
+# relative path?
+session = /var/lib/rtorrent/session
+
+# Watch a directory for new torrents, and stop those that have been
+# deleted.
+schedule = watch_www_directory,1,5,load_start=/mirror/root/csclub/*.torrent
+schedule = untied_directory,5,5,remove_untied=
+
+# Close torrents when diskspace is low.
+#schedule = low_diskspace,5,60,close_low_diskspace=100M
+
+# 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=200,200M,2000
+
+# The ip address reported to the tracker.
+#ip = 127.0.0.1
+#ip = rakshasa.no
+
+# The ip address the listening socket and outgoing connections is
+# bound to.
+bind = mirror
+
+# Port range to use for listening.
+port_range = 6900-6999
+
+# Start opening ports at a random position within the port range.
+#port_random = no
+
+# Check hash for finished torrents. Might be usefull until the bug is
+# fixed that causes lack of diskspace not to be properly reported.
+#check_hash = no
+
+# Set whetever the client should try to connect to UDP trackers.
+#use_udp_trackers = yes
+
+# Alternative calls to bind and ip that should handle dynamic ip's.
+#schedule = ip_tick,0,1800,ip=rakshasa
+#schedule = bind_tick,0,1800,bind=rakshasa
+
+encryption = allow_incoming,prefer_plaintext
+
+#
+# Do not modify the following parameters unless you know what you're doing.
+#
+
+# Hash read-ahead controls how many MB to request the kernel to read
+# ahead. If the value is too low the disk may not be fully utilized,
+# while if too high the kernel might not be able to keep the read
+# pages in memory thus end up trashing.
+#hash_read_ahead = 10
+
+# Interval between attempts to check the hash, in milliseconds.
+#hash_interval = 100
+
+# Number of attempts to check the hash while using the mincore status,
+# before forcing. Overworked systems might need lower values to get a
+# decent hash checking rate.
+#hash_max_tries = 10
+
+# Max number of files to keep open simultaniously.
+#max_open_files = 128
+
+# Number of sockets to simultaneously keep open.
+#max_open_sockets = <no default>
+
+
+# Example of scheduling commands: Switch between two ip's every 5
+# seconds.
+#schedule = "ip_tick1,5,10,ip=torretta"
+#schedule = "ip_tick2,10,10,ip=lampedusa"
+
+# Remove a scheduled event.
+#schedule_remove = "ip_tick1"