= Various stuff used personally or at work = These might be out of date, no warranty, etc. <> == adduser.local == Goes in /usr/local/sbin/ {{{ #!/bin/sh echo "username is $1" echo "uid is $2" echo "gid is $3" echo "homedir is $4" # Give new users an initial quota of 1gig quotatool -v -u $1 -b -l '1024 MB' / }}} == check-aacraid.py == {{{#!python #!/usr/bin/python # # THIS FILE IS DISTRIBUTED BY CFENGINE! LOCAL CHANGES WILL BE DESTROYED! # # check-aacraid.py # # Grabs the output from "/usr/local/sbin/arcconf GETCONFIG 1 AL" then # determines the health of various status indicators from the card # and drives. # # v0.1 - only checks card information so far, not drives yet import sys, os, re, string c_status_re = re.compile('^\s*Controller Status\s*:\s*(.*)$') c_defunct_re = re.compile('^\s*Defunct disk drive count\s:\s*([0-9]+).*$') c_degraded_re = re.compile('^\s*Logical devices/Failed/Degraded\s*:\s*([0-9]+)/([0-9]+)/([0-9]+).*$') b_status_re = re.compile('^\s*Status\s*:\s*(.*)$') b_temp_re = re.compile('^\s*Over temperature\s*:\s*(.*)$') b_capacity_re = re.compile('\s*Capacity remaining\s*:\s*([0-9]+)\s*percent.*$') b_time_re = re.compile('\s*Time remaining \(at current draw\)\s*:\s*([0-9]+) days, ([0-9]+) hours, ([0-9]+) minutes.*$') cstatus = cdefunct = cdegraded = bstatus = btemp = bcapacity = btime = "" check_status = 0 result = "" for line in os.popen4("/usr/bin/sudo /usr/local/sbin/arcconf GETCONFIG 1 AD")[1].readlines(): # Match the regexs cstatus = c_status_re.match(line) if cstatus: if cstatus.group(1) != "Optimal": check_status = 2 result += "Controller " + cstatus.group(1) + "," cdefunct = c_defunct_re.match(line) if cdefunct: if int(cdefunct.group(1)) > 0: check_status = 2 result += "Defunct drives " + cdefunct_group(1) + "," cdegraded = c_degraded_re.match(line) if cdegraded: if int(cdegraded.group(2)) > 0: check_status = 2 result += "Failed drives " + cdegraded.group(2) + "," if int(cdegraded.group(3)) > 0: check_status = 2 result += "Degraded drives " + cdegraded.group(3) + "," bstatus = b_status_re.match(line) if bstatus: if bstatus.group(1) == "Charging": if check_status < 2: check_status = 1 elif bstatus.group(1) != "Optimal": check_status = 2 result += "Battery Status " + bstatus.group(1) + "," btemp = b_temp_re.match(line) if btemp: if btemp.group(1) != "No": check_status = 2 result += "Battery Overtemp " + btemp.group(1) + "," bcapacity = b_capacity_re.match(line) if bcapacity: result += "Battery Capacity " + bcapacity.group(1) + "%," if bcapacity.group(1) < 50: if check_status < 2: check_status = 1 if bcapacity.group(1) < 25: check_status = 2 btime = b_time_re.match(line) if btime: timemins = int(btime.group(1)) * 1440 + int(btime.group(2)) * 60 + int(btime.group(3)) if timemins < 1440: if check_status < 2: check_status = 1 if timemins < 720: check_status = 2 result += "Battery Time " if timemins < 60: result += str(timemins) + "mins," else: result += str(timemins/60) + "hours," if result == "": result = "No output from arcconf!" check_status = 3 print result sys.exit(check_status) }}} == check-for-orphans == {{{ #!/bin/sh # # THIS FILE IS ROLLED OUT BY CFENGINE! ALL LOCAL MODIFICATIONS WILL BE DESTROYED! # # use favourite package tool and see if there are orphaned packages tmpfile=`mktemp ${TMPDIR:-/tmp}/check-for-updates.XXXXXX` || exit 2 mailguy () { ( cat < $tmpfile # We only care about packages listed below the Extra Packages line LINE=`cat $tmpfile | grep -n "Extra Packages" | cut -f1 -d:` if [ "x$LINE" != "x" ] then cat $tmpfile | sed -i -e "1,${LINE}d" $tmpfile if [ `cat $tmpfile | wc -l` -gt 0 ] then mailguy fi fi elif test -x /usr/bin/apt-cache ; then # APT system. # Use set theory to determine orphans. Unfortunately this doesn't take into account package versions yet. ((apt-cache pkgnames; COLUMNS=200 dpkg -l | sed -e '1,5d' | awk '{print $2}') | sort | uniq -d; COLUMNS=200 dpkg -l | sed -e '1,5d' | awk '{print $2}') | sort | uniq -u > $tmpfile if [ `cat $tmpfile | wc -l` -gt 0 ] then mailguy fi else echo "Can't work out the OS on $HOSTNAME" fi rm -f $tmpfile }}} == check-for-updates == {{{ #!/bin/sh # use favourite package tool and see if there are updates available for a machine. tmpfile=`mktemp ${TMPDIR:-/tmp}/check-for-updates.XXXXXX` || exit 2 #export http_proxy=http://foo.proxy.com:3128/ mailguy () { ( cat < $tmpfile r=$? if test "$r" -eq 100 ; then mailguy fi elif test -f /etc/redhat-release ; then # RHEL/CentOS if egrep -q "(Red Hat Enterprise Linux Server release 5|ES release [234]|CentOS)" /etc/redhat-release ; then # Yum-based RHEL/CentOS yum check-update > $tmpfile r=$? if test "$r" -eq 100 ; then mailguy fi else echo "Can't work out RHEL/CentOS OS on this machine $HOSTNAME, or unsupported release." fi elif test -f /etc/debian_version ; then # Debian apt-get update > $tmpfile apt-get -s upgrade >> $tmpfile if grep -q "The following packages will be upgraded" $tmpfile ; then mailguy fi else echo "Can't work out the OS on $HOSTNAME" fi rm -f $tmpfile }}} == check-iptables-firewall == {{{ #!/bin/sh # at the moment, the status of the firewall is unknown output="FIREWALL UNKNOWN" r=3 if [ -f /etc/redhat-release ]; then IPTABLESSAVE="/etc/sysconfig/iptables" elif [ -f /etc/debian_version ]; then IPTABLESSAVE="/etc/network/iptables" fi if /usr/bin/sudo /sbin/iptables -L -n | grep ^Chain | grep -q "policy ACCEPT"; then # there's chains with a default policy of ACCEPT # which suggests that there isn't a firewall running output="FIREWALL CRITICAL: evidence of no firewall running" r=2 elif [ -f $IPTABLESSAVE ]; then # there's a saved iptables ruleset if [ -f /etc/pyfi/ruleset.py ]; then files=`find /etc/pyfi -type f -name '*.py'` flag=0 d=`/usr/bin/sudo /bin/date +%s -r $IPTABLESSAVE` ood="" for f in $files; do fdate=`date +%s -r $f` if [ "$d" -lt "$fdate" ]; then ood="$ood $f" flag=1 fi done if [ "$flag" -eq 1 ]; then output="FIREWALL WARNING: running firewall is out of date wrt configuration: $ood" r=1 else saved=`/usr/bin/sudo /usr/bin/head -n 1 $IPTABLESSAVE | sed 's/^.*on //'` output="FIREWALL OK: last saved at $saved" r=0 fi else # no filtergen... output="FIREWALL UNKNOWN: no barrier jacket configuration found" fi fi echo $output exit $r }}} == check_kernel == This is kinda arse, and not robust against minor output format changes. {{{ #!/bin/sh kver=`uname -a | awk '{print $3}'` if test -f /etc/fedora-release || test -f /etc/redhat-release ; then case $kver in *smp) kernel="kernel-smp" ;; *xen) kernel="kernel-xen" ;; *PAE) kernel="kernel-PAE" ;; *) kernel="kernel" ;; esac kver=`echo $kver | sed 's/smp$//;s/xen$//;s/PAE$//'` buildtime=0 for item in `rpm -q $kernel --qf '%{VERSION}-%{RELEASE}#%{BUILDTIME}\n'` do bt=`echo $item | cut -d# -f2` if [ "$buildtime" -lt "$bt" ] then buildtime=$bt lver=`echo $item | cut -d# -f1` fi done krunning="$kernel-$kver" lver="$kernel-$lver" elif test -f /etc/debian_version ; then # FIXME: still need to test sub-version of kernels on debian/ubuntu kernel="kernel" lver=`(COLUMNS=200 dpkg -l 'kernel-image*' 2>/dev/null|| COLUMNS=200 dpkg -l 'linux-image*' 2>/dev/null) | grep ^ii | awk '{print $2}' | grep '[0-9]' | tail -n 1` #echo $lver if dpkg -l 'kernel-image*' 2>/dev/null >/dev/null then krunning="kernel-image-$kver" else krunning="linux-image-$kver" fi fi if test "x$krunning" != "x$lver" ; then echo "KERNEL WARNING: running version $krunning doesn't match latest installed version $lver" r=1 else echo "KERNEL OK - $kernel-$kver" r=0 fi exit $r }}} == db-export-mysql == Compressed SQL dumps ftw. {{{ #! /bin/bash # # Backup the entire MySQL # # MY_BACKUP=/var/lib/mysqlbackup if [ ! -d $MY_BACKUP ] then mkdir -m 0700 $MY_BACKUP fi if [ ! -d $MY_BACKUP ] then echo "ERROR: database backup directory '$MY_BACKUP' does not exist." 1>&2 exit 1 fi # Compress data in a manner that will enable rsync # to sync efficiently. if gzip --help | grep -q -- --rsyncable then # gzip reads this environment variable automatically export GZIP="--rsyncable" fi DATE=`date -I` /usr/bin/mysqldump -q -c -A -Q | gzip -9 > $MY_BACKUP/$DATE.gz if [ -f /etc/redhat-release ] then /usr/sbin/tmpwatch --mtime 72 $MY_BACKUP elif [ -f /etc/debian_version ] then /usr/sbin/tmpreaper --mtime 72h $MY_BACKUP fi # Purge the binary log if /usr/bin/mysqladmin variables | grep -q log_bin.*ON then /usr/bin/mysql -e 'RESET MASTER;' fi }}} == db-export-pgsql == Compressed SQL dumps ftw. {{{ #! /bin/bash # # Backup the entire PostgreSQL cluster # # NB: password set in $PGUSER startup files PG_BACKUP=/var/lib/postgresql/8.3/main/backups PGUSER=postgres case `whoami` in root) if [ ! -d $PG_BACKUP ] then mkdir -m 0700 $PG_BACKUP chown $PGUSER $PG_BACKUP fi exec su - $PGUSER $0 ;; $PGUSER) ;; *) echo "$0 must be run as root" 1>&2 exit 1 ;; esac if [ ! -d $PG_BACKUP ] then echo "ERROR: database backup directory '$PG_BACKUP' does not exist." 1>&2 exit 1 fi # Compress data in a manner that will enable rsync # to sync efficiently. if gzip --help | grep -q -- --rsyncable then # gzip reads this environment variable automatically export GZIP="--rsyncable" fi DATE=`date -I` /usr/bin/pg_dumpall | gzip -9 > $PG_BACKUP/$DATE.gz if [ -f /etc/redhat-release ] then /usr/sbin/tmpwatch --mtime 72 $PG_BACKUP elif [ -f /etc/debian_version ] then /usr/sbin/tmpreaper --mtime 72h $PG_BACKUP fi # Run clustering script if it exists if [ -x /usr/local/sbin/pg-cluster ] then /usr/local/sbin/pg-cluster fi }}} == inflate.sh == {{{ #!/bin/bash # Barney Desmond 2008-08-07 # For simpler filesystem growth, performing an lvextend and resize2fs usage() { echo "Expected two arguments, LV name and no. of gb to increase by. Eg." echo " $0 LVNAME NUMGIGS" exit 2 } if [ $# -ne 2 ] then usage fi LVNAME="$1" MOREGIGS="$2" HOSTNAME=`hostname --short` FQDN=`hostname --fqdn` echo -n "Looking for LVM mounts in /dev... " if [ -d "/dev/${HOSTNAME}" ] then echo "found /dev/${HOSTNAME}" LVDIR="/dev/${HOSTNAME}/" elif [ -d "/dev/${FQDN}" ] then echo "found /dev/${FQDN}" LVDIR="/dev/${FQDN}/" else echo "can't find /dev/${HOSTNAME} or /dev/${FQDN}, bailing..." exit 1 fi if [ ! -L "${LVDIR}${LVNAME}" ] then echo "Can't find an LV at ${LVDIR}${LVNAME}, please check the name of the volume. Bailing..." exit 2 fi # test if the no. of gigs is numeric, probably imperfect test... if ! expr "${MOREGIGS}" + 0 > /dev/null 2>/dev/null then echo "No. of gigs \"${MOREGIGS}\" was not numeric, bailing..." exit 2 fi echo "Ready to go, will lvextend ${LVDIR}${LVNAME} by ${MOREGIGS}gb" echo echo "Starting lvextend..." lvextend -L "+${MOREGIGS}G" "${LVDIR}${LVNAME}" if [ $? -ne 0 ] then echo "Error during lvextend, bailing..." exit 1 fi echo "Starting resize2fs..." resize2fs -p "${LVDIR}${LVNAME}" echo "All done!" df -h | grep "${LVNAME}" }}} == new_all.sh == Make a user, mysql db, vhost. {{{ #!/bin/sh # Creates a whole new account on the system, as calling all the scripts is kinda tiring already, checking if the domain is already on the system, etc. # Takes the suggested username and domain, does all sanity checking, then calls the separate scripts. # Barney Desmond, Anchor Systems # 2008-06-23 - rev1 - initial hack-up ACCOUNTS_LOGFILE=/root/newaccounts DATABASES_LOGFILE=/root/newdatabases usage() { echo "Usage: $0 expects two arguments: system username, domain name" } if [ $# -ne 2 ] then usage exit 1 fi USERNAME=$1 CLEAN_USERNAME=`echo $1 | tr -cd a-zA-Z0-9` DOMAIN=`echo $2 | tr 'A-Z' 'a-z' | sed -r 's,.*http://,,' | awk '{ print $1 }' | sed -r 's/^www\.//'` ## LOGGING CHECKS # Must be able to touch the newaccounts output file if ! /bin/touch "$ACCOUNTS_LOGFILE" then echo "Could not touch ${ACCOUNTS_LOGFILE}, are you root?. Exiting..." exit 1 fi # Must be able to touch the newdatabases output file if ! /bin/touch "$DATABASES_LOGFILE" then echo "Could not touch ${DATABASES_LOGFILE}, are you root?. Exiting..." exit 1 fi ## USERNAME CHECKS # Sanity-check the username if [ ${#USERNAME} -gt 16 ] # mysql doesn't like long usernames then echo "New username must be no longer than 16 characters. Exiting..." exit 2 fi if [ x$USERNAME != x$CLEAN_USERNAME ] then echo "New username is not sane, must be alphanumeric. Exiting..." exit 2 fi if [ x$USERNAME = "xroot" ] # root is not okay then echo "Username of root is not acceptable. Exiting..." exit 2 fi # Username must not already exist if /usr/bin/id "$USERNAME" 2> /dev/null > /dev/null then echo "Username already exists. Exiting..." exit 2 fi ## DATABASE CHECKS # DB of same name must NOT already exist if /usr/bin/mysql -D "$USERNAME" -e '\q' 2>/dev/null then echo "Database $USERNAME already exists. Exiting..." exit 2 fi # .my.cnf file for user must not already exist if [ -f "/home/${USERNAME}/.my.cnf" ] then echo "~/.my.cnf already exists, don't want to clobber it. Exiting..." exit 1 fi ## VHOST CHECKS # vhost file must not already exist if [ -f "/etc/httpd/conf/users/${USERNAME}.conf" ] then echo "vhost ${USERNAME}.conf already exists. Exiting..." exit 1 fi # domain must not already be configured #free4lifeclub.com if /bin/grep -q "[ ]$DOMAIN" /etc/httpd/conf/users/*.conf then echo "The domain is already configured on the system. Exiting..." exit 2 fi echo "GOOD TO GO" /root/new_user.sh "${USERNAME}" echo "1. Created new user" /root/new_db.sh "${USERNAME}" echo "2. Created new database" /root/new_vhost.sh "${USERNAME}" "${DOMAIN}" echo "3. Created new vhost" /root/rowan_reply.sh "${USERNAME}" }}} == new_db.sh == {{{ #!/bin/sh # Create a new MySQL db for a user, setting a random password and granting all privs # New database will have name == system-username == db-username # Sets up ~/.my.cnf for the user so they can use mysql shell immediately # # Should be improved to handle adding secondary databases, # appending a number (1,2,3,etc) as needed # Barney Desmond, Anchor Systems # 2008-06-12 - v1 - initial hack-up LOGFILE=/root/newdatabases usage() { #echo "Usage: $0 expects two arguments: system username, name for new db" echo "Usage: $0 expects one argument: system username, which will be used as the name for the new DB" } if [ $# -ne 1 ] then usage exit 1 fi USERNAME=$1 DBNAME=$1 CLEAN_DBNAME=`echo $DBNAME | tr -cd a-zA-Z0-9` # DB for root is not okay if [ x$USERNAME = "xroot" ] then echo "Username of root is not acceptable. Exiting..." exit 2 fi # DBname must be alphanumeric if [ x$DBNAME != x$CLEAN_DBNAME ] then echo "New dbname is not sane, must be alphanumeric. Exiting..." exit 2 fi # The username must exist if ! /usr/bin/id "$USERNAME" 2> /dev/null > /dev/null then echo "System username doesn't exist. Exiting..." exit 2 else echo -n "" #echo "username exists, cool" fi # DB of same name must NOT already exist if /usr/bin/mysql -D "$DBNAME" -e '\q' 2>/dev/null then echo "Database $DBNAME already exists. Exiting..." exit 2 fi #echo "database $DBNAME can be created" # Setup .my.cnf file for user, must not already exist if [ -f "/home/${USERNAME}/.my.cnf" ] then echo "~/.my.cnf already exists, don't want to clobber it. Exiting..." exit 1 fi if ! /usr/bin/sudo -u $USERNAME touch "/home/${USERNAME}/.my.cnf" then echo "Count not create blank .my.cnf in user's homdir. Exiting..." exit 1 fi if ! /bin/chmod 600 "/home/${USERNAME}/.my.cnf" then echo "Count not chmod .my.cnf to 600 in user's homdir. Exiting..." exit 1 fi # Must be able to touch the output file if ! /bin/touch "$LOGFILE" then echo "Count not touch ${LOGFILE}, are you root?. Exiting..." exit 1 fi # Make up a password PASSWORD=`openssl rand -base64 12 | tr -cd a-zA-Z0-9` # Write out .my.cnf for user echo "[client]" >> "/home/${USERNAME}/.my.cnf" echo "user=${USERNAME}" >> "/home/${USERNAME}/.my.cnf" echo "password=${PASSWORD}" >> "/home/${USERNAME}/.my.cnf" echo "" >> "/home/${USERNAME}/.my.cnf" echo "[mysql]" >> "/home/${USERNAME}/.my.cnf" echo "database=${DBNAME}" >> "/home/${USERNAME}/.my.cnf" # Create the DB echo "CREATE DATABASE $DBNAME; GRANT ALL PRIVILEGES ON ${DBNAME}.* TO '${USERNAME}'@'localhost' IDENTIFIED BY '${PASSWORD}' WITH GRANT OPTION; FLUSH PRIVILEGES;" | mysql # Give visual feedback echo "DATABASE l:p is: $DBNAME // $PASSWORD" # Log information to file chmod 600 "$LOGFILE" echo "dbname: $DBNAME" >> "$LOGFILE" echo "dbpass: $PASSWORD" >> "$LOGFILE" echo "" >> "$LOGFILE" }}} == new_user.sh == {{{ #!/bin/sh # Adds a new user to the system (RHEL), setting a random password # Adds entries to ssh_users and ftp_users, may not always be desired. # Also sets the shell to the default, allowing ssh login. May not be desired. # Barney Desmond, Anchor Systems # 2008-06-12 - v1 - initial hack-up # 2008-06-23 - v2 - bugfix for checking if username already exists; corrected full path to openssl; adds ftp_users and ssh_users entries LOGFILE=/root/newaccounts usage() { echo "Usage: $0 expects one argument, the name for the new user" } if [ $# -ne 1 ] then usage exit 1 fi NEW_USERNAME=$1 CLEAN_NEW_USERNAME=`echo $1 | tr -cd a-zA-Z0-9` # Sanity-check the username if [ x$NEW_USERNAME != x$CLEAN_NEW_USERNAME ] then echo "New username is not sane, must be alphanumeric. Exiting..." exit 2 fi # Username must not already exist if /usr/bin/id "$NEW_USERNAME" 2> /dev/null > /dev/null then echo "Username already exists. Exiting..." exit 2 fi # Generate a password NEWPASSWD=`/usr/bin/openssl rand -base64 20 | tr -cd a-zA-Z0-9` # Must be able to touch the output file if ! /bin/touch "$LOGFILE" then echo "Count not touch ${LOGFILE}, are you root?. Exiting..." exit 1 fi # Add the user and set password /usr/sbin/adduser --shell /bin/bash --disabled-password --gecos "$NEW_USERNAME" $NEW_USERNAME /usr/sbin/usermod -p "$NEWPASSWD" $NEW_USERNAME # Give the user SSH and FTP access /bin/sed -i "1s/^/+\:${NEW_USERNAME}\:ALL\n/" /etc/security/ssh_users #/bin/sed -i "1s/^/+\:${NEW_USERNAME}\:ALL\n/" /etc/security/ftp_users # Log the new details chmod 600 "$LOGFILE" echo "username: ${NEW_USERNAME}" >> "$LOGFILE" echo "password: ${NEWPASSWD}" >> "$LOGFILE" echo "" >> "$LOGFILE" # Visual output, could add a facility to silence it echo "SYSTEM l:p is ${NEW_USERNAME} // ${NEWPASSWD}" }}} == new_vhost.sh == {{{ #!/bin/sh # Adds a vhost to the system in standard Anchor location (/etc/httpd/conf/users/username.conf) # vhost file takes the name of the user # Does not attempt to touch the running apache, you have to "bounce" it yourself # Barney Desmond, Anchor Systems # 2008-06-12 - rev1 - initial hack-up # 2008-06-12 - rev2 - chown and chgrp the placeholder index.html, apparently sudo may not create it as the user # 2008-06-19 - rev3 - added some parsing for the domain name to strip http://, www., etc. allows us to be smarter about pumping in domain names from a list # 2008-06-24 - rev4 - added vhost_template # 2009-02-11 - rev5 - Debian-ised VHOST_TEMPLATE=/etc/apache2/sites-available/template usage() { echo "Usage: $0 expects two arguments: system username, domain name" } if [ $# -ne 2 ] then usage exit 1 fi # Switch to the config dir cd /etc/apache2/sites-enabled USERNAME=$1 DOMAIN=`echo $2 | tr 'A-Z' 'a-z' | sed -r 's,.*http://,,' | awk '{ print $1 }' | sed -r 's/^www\.//'` # root is not okay if [ x$USERNAME = "xroot" ] then echo "Username of root is not acceptable. Exiting..." exit 2 fi # The user muxt exist if ! /usr/bin/id "$USERNAME" 2> /dev/null > /dev/null then echo "System username doesn't exist. Exiting..." exit 2 else echo -n "" #echo "username exists, cool" fi # vhost file must not already exist if [ -f "/etc/apache2/sites-available/${USERNAME}" ] then echo "vhost ${USERNAME}.conf already exists. Exiting..." exit 1 fi # vhost_template must exist if [ ! -f "${VHOST_TEMPLATE}" ] then echo "Couldn't find template vhost file at ${VHOST_TEMPLATE}. Exiting..." exit 1 fi # Copy across the template vhost .conf if ! /bin/cp "${VHOST_TEMPLATE}" "/etc/apache2/sites-available/${USERNAME}" then echo "Couldn't not copy vhost template to /etc/apache2/sites-available/${USERNAME}. Exiting..." exit 1 fi if ! /bin/ln -s "../sites-available/${USERNAME}" "${USERNAME}" then echo "Couldn't create vhost symlink in /etc/apache2/sites-enabled/. Exiting..." exit 1 fi # Search/replace the username and domain name in the vhost file /bin/sed -r -i "s,USER,${USERNAME},g" /etc/apache2/sites-available/${USERNAME} /bin/sed -r -i "s,DOMAIN,${DOMAIN},g" /etc/apache2/sites-available/${USERNAME} # Fix up the user's homedir so apache can get in /bin/chmod o+x "/home/${USERNAME}" # Create a placeholder page to show it's working, must NOT already exist if [ -f "/home/${USERNAME}/public_html/index.html" ] then echo "/home/${USERNAME}/public_html/index.html already exists. Exiting..." exit 1 fi /usr/bin/sudo -u $USERNAME echo "$USERNAME ok" > "/home/${USERNAME}/public_html/index.html" /bin/chown $USERNAME "/home/${USERNAME}/public_html/index.html" /bin/chgrp $USERNAME "/home/${USERNAME}/public_html/index.html" }}} == snmp-diskio-collector == {{{ #!/bin/sh # by Jamie Wilkinson # this code is in the public domain # based on passtest from the net-snmp distribution examples # WARNING there is shitloads of IO required to get this information :) debug_flag=0 debug () { if [ $debug_flag -eq 1 ]; then echo $* >> /tmp/snmp-diskio-collector-debug.log fi } PLACE=".1.3.6.1.4.1.2021.13.15" REQ="$2" debug debug "new run" debug "args $*" if [ "$1" = "-s" ]; then exit 0 fi # the 'tail' of the oid, everything after $PLACE oidtail=`echo $REQ | sed "s/^$PLACE//"` debug "oidtail=$oidtail" # number of devices we can export if [ -f /proc/diskstats ]; then devcount=`wc -l /proc/diskstats | sed 's/^ *//' | cut -f1 -d' '` else devcount=`wc -l /proc/partitions | sed 's/^ *//' | cut -f1 -d' '` devcount=`expr $devcount - 2` fi debug "devcount=$devcount" item=`echo $oidtail | cut -f4 -d.` index=`echo $oidtail | cut -f5 -d.` debug "oidtail=$oidtail, item=$item, index=$index" if [ "$1" = "-n" ]; then if [ -z "$item" ]; then item=1 index=1 elif [ -z "$index" ]; then index=1 else index=`expr $index + 1` if [ "$index" -gt "$devcount" ]; then index=1 item=`expr $item + 1` if [ "$item" -gt 6 ]; then # break out of the loop exit 0; fi fi fi RET=$PLACE.1.1.$item.$index else case "$REQ" in $PLACE) exit 0;; *) RET=$REQ ;; esac fi debug "after -n, item=$item, index=$index" debug "RET is now $RET" echo "$RET" debug "oidtail=$oidtail, item=$item, index=$index" # awk uses this variable in the environment below export index # see linux kernel Documentation/iostats.txt for format if [ -n "$index" ]; then case "$item" in 1) # diskIOIndex debug "result: diskIOIndex $index" echo "integer" echo $index exit 0 ;; 2) # diskIODevice debug "result: diskIODevice $index" echo "string" if [ -f /proc/diskstats ]; then awk 'FNR == ENVIRON["index"] { print $3 }' /proc/diskstats else awk 'FNR == ENVIRON["index"] + 2 { print $4 }' /proc/partitions fi exit 0 ;; 3) # diskIONRead debug "result: diskIONRead $index" echo "counter" if [ -f /proc/diskstats ]; then awk 'FNR == ENVIRON["index"] { if (NR==14) print $6; else print $5 }' /proc/diskstats else awk 'FNR == ENVIRON["index"] + 2 { print $7 }' /proc/partitions fi exit 0 ;; 4) # diskIONWritten debug "result: diskIONWritten $index" echo "counter" if [ -f /proc/diskstats ]; then awk 'FNR == ENVIRON["index"] { if (NR==14) print $10; else print $7 }' /proc/diskstats else awk 'FNR == ENVIRON["index"] + 2 { print $11 }' /proc/partitions fi exit 0 ;; 5) # diskIOReads debug "result: diskIOReads $index" echo "counter" if [ -f /proc/diskstats ]; then awk 'FNR == ENVIRON["index"] { if (NR==14) print $4; else print $4 }' /proc/diskstats else awk 'FNR == ENVIRON["index"] + 2 { print $5 }' /proc/partitions fi exit 0 ;; 6) # diskIOWrites debug "result: diskIOWrites $index" echo "counter" if [ -f /proc/diskstats ]; then awk 'FNR == ENVIRON["index"] { if (NR==14) print $8; else print $6 }' /proc/diskstats else awk 'FNR == ENVIRON["index"] + 2 { print $9 }' /proc/partitions fi exit 0 ;; *) exit 0; #echo "string"; echo "debug... $RET $REQ"; exit 0 ;; esac else exit 0 fi }}} == vhost_template == {{{ # -*- apache -*- vim: syntax=apache # # %s,DOMAIN,X,g # %s,USER,Y,g ServerName DOMAIN ServerAlias www.DOMAIN ServerAdmin webmaster@DOMAIN DocumentRoot /home/USER/public_html CustomLog /var/log/apache2/access_logs/DOMAIN_log combined ErrorLog /var/log/apache2/error_logs/USER_log SuexecUserGroup USER USER AddOutputFilterByType DEFLATE text/html text/plain text/xml text/css ## HOWTO: ## * correct the IP address on the line ## * put your cursor on _THIS_ line ## * :.,$s/^#// ## # # ServerName DOMAIN # ServerAlias www.DOMAIN # # ServerAdmin webmaster@DOMAIN # # DocumentRoot /home/USER/public_html # CustomLog /var/log/apache2/access_logs/DOMAIN_log combined # ErrorLog /var/log/apache2/error_logs/USER_log # # # SuexecUserGroup USER USER # # # # AddOutputFilterByType DEFLATE text/html text/plain text/xml text/css # # # SSLEngine on # # # :.,+3s,DOMAIN,THE_DOMAIN, # SSLCertificateFile /etc/ssl/www.DOMAIN_crt # SSLCertificateKeyFile /etc/ssl/www.DOMAIN_key # # SSLCACertificateFile /etc/ssl/foo.crt # # }}} == webalize == This shit is ''ballin'!'' {{{#!perl #!/usr/bin/perl # webalize version 4 use strict; use warnings; use User::pwent; use Getopt::Long; use Tie::File; # this module rocks use File::Basename; use File::Copy; # virtualhosting wrapper for webalizer and awstats # The canonical source of information for log files is the virtualhost # configuration, in /etc/httpd/conf/users/ . # Each file in that directory, named after the user account responsible, # contains a list of virtualhosts; in each virtual host block there may be # several domain names served, and one access log defined. # The sources of information for processing logs, then, is: # - user account name comes from the filename # - domain name comes from one of ServerName or ServerAlias inside the file # - access log path comes from the CustomLog directive in the file. # based on the original webalize-users by Jamie Wilkinson and the # process-logs script by Emmanuel Galanos # # updated by Barney Desmond - 2009-04-08 # minor cleanup, enhancements and documentation, I want to run it on Ubuntu as well #my $root = "/home/jaq/kayak"; my $root = ""; #my $awstats = '/var/www/awstats/awstats.pl'; my $awstats = '/usr/lib/cgi-bin/awstats.pl'; my $access_logs; my $conffiledir; my $logfiledir; # nastily redhat-specific in later usage if (-e "$root/etc/debian_version") { $access_logs = "$root/var/log/apache2/access_logs"; $conffiledir = "$root/etc/apache2/sites-enabled"; $logfiledir = "$root/var/log/apache2"; } else { $access_logs = "$root/var/log/httpd/access_logs"; $conffiledir = "$root/etc/httpd/conf/users"; $logfiledir = "$root/etc/httpd"; } my $outputdir; if (-d "$root/var/www/html/statistics") { $outputdir = "$root/var/www/html/statistics"; } else { $outputdir = "$root/var/www/html/usage"; if ( ! -d $outputdir ) { mkdir $outputdir or die "couldn't create $outputdir"; } } sub usage () { print <&1 >/dev/null |" or die "awstats update exec pipe failed: $!"; while () { if (! -t STDOUT) { next if (m/^Update for config /); next if (m/^With data in log file /); next if (m/^Searching new records from beginning/); next if (m/^Phase 1 :/); next if (m/^Direct access to last /); next if (m/^So searching/); next if (m/^Phase 2 :/); next if (m/^Jumped lines/); next if (m/^Parsed lines/); next if (m/^ Found /); next if (m/^Direct access after last parsed record/); } print $_; } close AWSTATSUPDATE; debug "exec done\n"; } sub process_with_webalizer($$$) { my $user = shift; my $domain = shift; my $log = shift; my $target = $outputdir . "/" . $domain; debug "processing " . $log . "\n"; if (! -d $target) { debug "making directory $target\n"; mkdir $target, 0755; } my $dnscache = $target . "/dns_cache.db"; if (-f $dnscache) { debug "removing old dns cache\n"; unlink $dnscache; } my $cmd = "/usr/bin/webalizer -o $target -D $dnscache -n $domain $log"; debug "exec'ing $cmd\n"; open WEBALIZER, $cmd . " 2>&1 |" or die "webalizer exec pipe failed: $!"; while () { if (! -t STDOUT) { next if (m/Truncating oversized \w+ field/); next if (m/Truncating oversized hostname/); next if (m/String exceeds storage size/); next if (m/No valid records found/); next if (m/Skipping oversized log record/); next if (m/Skipping bad record/); } print $_; } close WEBALIZER; debug "exec done\n"; } sub process($$$) { my $user = shift; my $domain = shift; my $log = shift; my $target = $outputdir . '/' . $domain; if (-f $target . '/webalizer.hist') { debug "found existing webalizer database\n"; process_with_webalizer($user, $domain, $log) } elsif (-f $awstats) { debug "awstats is installed, using\n"; process_with_awstats($user, $domain, $log) } else { debug "defaulting to webalizer\n"; process_with_webalizer($user, $domain, $log) } } sub filelist ($$) { # returns a list of files in directory who match regex my $directory = shift; my $regex = shift; opendir DIR, $directory or die "can't opendir $directory: $!"; my @files = grep { /$regex/ && -f "$directory/$_" } readdir DIR; closedir DIR; return @files; } sub bylrc { # logrotate's chronological order: highest number first, ignore the .gz ($b =~ /_log\.(\d+)/)[0] <=> ($a =~ /_log\.(\d+)/)[0] # then by case-insensitive compare || uc($a) cmp uc($b) } sub processbydomain($) { my $domain = shift; # find the config file that handles this domain # take the user from that # work out the logs for this domain (watch out for vhosts) # process logs in order } sub processbyuser($) { my $user = shift; my @conffiles; if (-e "$root/etc/debian_version") { @conffiles = filelist($conffiledir, '^' . $user . '$'); } else { @conffiles = filelist($conffiledir, '^' . $user . '\.conf$'); } # should only be one of them... print @conffiles; if ($#conffiles > 1) { print "warning: more than one config file found for $user\n"; } # take the access logs for this user my %access_logs = (); foreach (@conffiles) { debug "processing $_ for $user\n"; my @file; tie @file, 'Tie::File', $conffiledir . "/" . $_; my $access_log; my $domain; foreach (@file) { if (m/CustomLog\s+([^ ]*)\s+/) { # This relies on $access_log being relative to something - on redhat that's /etc/httpd, where 'logs' is symlinked. # Not the case on debian, the log path has to be absolute, which is caught by the "does it start with /" check $access_log = $1; $access_log = $logfiledir . "/" . $access_log unless $access_log =~ q{^/}; debug "found log $access_log\n"; } if (m/ServerName\s+([^ :]+)/) { $domain = $1; debug "found domain $domain\n"; } if (defined($access_log) && -f $access_log && defined($domain)) { # only the first domain assigned to the access log, # this saves us from using the wrong ServerName for an access log # when there's an SSL only vhost with www. in the name # typically the SSL logs are shared with the non-ssl logs. if (!defined($access_logs{$access_log})) { debug "using $access_log for $domain\n"; $access_logs{$access_log} = $domain; } # unset them both undef $access_log; undef $domain; } } if (defined($domain) && ! defined($access_log)) { # if we get here it's because *both* werent unset, which means we fell through without finding a CustomLog # This means we just *assume* where the Customlog is..? debug "fell through without a CustomLog\n"; if (-e "$root/etc/debian_version") { $access_log = $logfiledir . "/access_logs/" . $domain . "_log"; } else { $access_log = $logfiledir . "/logs/access_logs/" . $domain . "_log"; } if (!defined($access_logs{$access_log})) { debug "using $access_log for $domain\n"; $access_logs{$access_log} = $domain; } # unset them both undef $access_log; undef $domain; } untie @file; } # expand to all logs for these domains foreach (keys %access_logs) { debug "snuh $_\n"; my $dir = dirname $_; my $log = basename $_; my $domain = $access_logs{$_}; debug "dir is $dir\nlog is $log\ndomain is $domain\n"; # find the log files in that directory that logrotate will have made # we now ignore the one we found in apache's config file because # apache will still be writing to it, so let's leave it alone my @dom_logs = filelist($dir, '^' . $log . '\.\d+(.gz)?'); # sort the logs in logrotate-chronological order foreach (sort bylrc @dom_logs) { debug "found log $_\n"; # now we have a log file, a domain, and a user process($user, $domain, $dir . "/" . $_); } } } sub processbylog($) { my $log = shift; # find which domain and user are responsible for this log # process this log } my $domain; my $log; my $user; if (!GetOptions('domain=s' => \$domain, 'log=s' => \$log, 'user=s' => \$user, 'help' => sub { usage(); exit 0; } )) { exit 1; } my @logs; if (defined($domain)) { processbydomain($domain); } elsif (defined($log)) { processbylog($log); } elsif (defined($user)) { processbyuser($user); } else { # nothing defined, work it out my $arg = shift; # if no args, then process *_log.1 if (!defined($arg)) { # FIXME: go by domains in the apache config, becauase a log file may # exist for a recently removed domain. @logs = `ls $access_logs/*_log.1` or die "couldn't find any access logs"; foreach $log (@logs) { chomp $log; $log =~ m,$access_logs/(.*)_log,; $domain = $1; my $conffile; my $breaker = 0; my $target = "$outputdir/$domain"; if (-e "$root/etc/debian_version") { $conffile = `egrep -liH "ServerName[[:space:]]+$domain(:|\$|[[:space:]])" $conffiledir/* | head -1` or $breaker = 1; } else { $conffile = `egrep -liH "ServerName[[:space:]]+$domain(:|\$|[[:space:]])" $conffiledir/*.conf | head -1` or $breaker = 1; } if ($breaker) { # Don't care, too much noise #debug "couldn't find the virtualhost that creates access log for $domain\n"; next; }; chomp $conffile; if (-e "$root/etc/debian_version") { $conffile =~ m,$conffiledir/(.*), or $breaker = 1; $user = $1; } else { $conffile =~ m,$conffiledir/(.*)\.conf, or $breaker = 1; $user = $1; } if ($breaker) { print $conffile . "'s name doesn't look right\n"; next; }; # check that this user exists, if we're root my $pw = getpwnam $user or $breaker = 1; if ($breaker) { # Don't really care about listing these, too much noise #print "user $user doesn't exist\n" if $< == 0; next; }; # print if we're on a TTY or not print "$user: $domain\n" if -t STDOUT; my @lines = process $user, $domain, $log; if ($#lines > 0) { # may already have printed this once print "$user: $domain" if ! -t STDOUT; print @lines; } } } elsif (isdomain($arg)) { # check if the arg is a domain processbydomain($arg); } elsif (islog($arg)) { # check if the arg is a log processbylog($log); } elsif (isuser($arg)) { # check if the arg is a user processbyuser($arg); } else { print "REDO FROM START"; print "$arg is neither domain, log file, nor user account\n"; usage(); exit 1; } } exit 0; }}}