Various stuff used personally or at work
These might be out of date, no warranty, etc.
Contents
adduser.local
Goes in /usr/local/sbin/
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
1 #!/usr/bin/python
2 #
3 # THIS FILE IS DISTRIBUTED BY CFENGINE! LOCAL CHANGES WILL BE DESTROYED!
4 #
5 # check-aacraid.py
6 #
7 # Grabs the output from "/usr/local/sbin/arcconf GETCONFIG 1 AL" then
8 # determines the health of various status indicators from the card
9 # and drives.
10 #
11 # v0.1 - only checks card information so far, not drives yet
12
13 import sys, os, re, string
14
15 c_status_re = re.compile('^\s*Controller Status\s*:\s*(.*)$')
16 c_defunct_re = re.compile('^\s*Defunct disk drive count\s:\s*([0-9]+).*$')
17 c_degraded_re = re.compile('^\s*Logical devices/Failed/Degraded\s*:\s*([0-9]+)/([0-9]+)/([0-9]+).*$')
18 b_status_re = re.compile('^\s*Status\s*:\s*(.*)$')
19 b_temp_re = re.compile('^\s*Over temperature\s*:\s*(.*)$')
20 b_capacity_re = re.compile('\s*Capacity remaining\s*:\s*([0-9]+)\s*percent.*$')
21 b_time_re = re.compile('\s*Time remaining \(at current draw\)\s*:\s*([0-9]+) days, ([0-9]+) hours, ([0-9]+) minutes.*$')
22
23 cstatus = cdefunct = cdegraded = bstatus = btemp = bcapacity = btime = ""
24 check_status = 0
25 result = ""
26
27 for line in os.popen4("/usr/bin/sudo /usr/local/sbin/arcconf GETCONFIG 1 AD")[1].readlines():
28 # Match the regexs
29 cstatus = c_status_re.match(line)
30 if cstatus:
31 if cstatus.group(1) != "Optimal":
32 check_status = 2
33 result += "Controller " + cstatus.group(1) + ","
34
35 cdefunct = c_defunct_re.match(line)
36 if cdefunct:
37 if int(cdefunct.group(1)) > 0:
38 check_status = 2
39 result += "Defunct drives " + cdefunct_group(1) + ","
40
41 cdegraded = c_degraded_re.match(line)
42 if cdegraded:
43 if int(cdegraded.group(2)) > 0:
44 check_status = 2
45 result += "Failed drives " + cdegraded.group(2) + ","
46 if int(cdegraded.group(3)) > 0:
47 check_status = 2
48 result += "Degraded drives " + cdegraded.group(3) + ","
49
50 bstatus = b_status_re.match(line)
51 if bstatus:
52 if bstatus.group(1) == "Charging":
53 if check_status < 2:
54 check_status = 1
55 elif bstatus.group(1) != "Optimal":
56 check_status = 2
57 result += "Battery Status " + bstatus.group(1) + ","
58
59 btemp = b_temp_re.match(line)
60 if btemp:
61 if btemp.group(1) != "No":
62 check_status = 2
63 result += "Battery Overtemp " + btemp.group(1) + ","
64
65 bcapacity = b_capacity_re.match(line)
66 if bcapacity:
67 result += "Battery Capacity " + bcapacity.group(1) + "%,"
68 if bcapacity.group(1) < 50:
69 if check_status < 2:
70 check_status = 1
71 if bcapacity.group(1) < 25:
72 check_status = 2
73
74 btime = b_time_re.match(line)
75 if btime:
76 timemins = int(btime.group(1)) * 1440 + int(btime.group(2)) * 60 + int(btime.group(3))
77 if timemins < 1440:
78 if check_status < 2:
79 check_status = 1
80 if timemins < 720:
81 check_status = 2
82 result += "Battery Time "
83 if timemins < 60:
84 result += str(timemins) + "mins,"
85 else:
86 result += str(timemins/60) + "hours,"
87
88 if result == "":
89 result = "No output from arcconf!"
90 check_status = 3
91
92 print result
93 sys.exit(check_status)
check-for-orphans
#
# 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 <<EOF
From: root@$HOSTNAME
To: X@example.com
Subject: Orphans Check $HOSTNAME
EOF
cat $tmpfile) #| /usr/sbin/sendmail -t -i
}
if test -x /usr/bin/yum ; then
# Yum system
# Due to how they are installed, kernel and gpg-pubkey packages are always listed
yum list extras | egrep -v "(kernel|gpg-pubkey)" > $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
# 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 <<EOF
From: root@$HOSTNAME
To: X@example.com
Subject: Update Check $HOSTNAME
EOF
cat $tmpfile) #| /usr/sbin/sendmail -t -i
}
if test -f /etc/fedora-release ; then
# Fedora Core
yum check-update > $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
# 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.
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.
#
# 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.
#
# 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
# Barney Desmond <barney@anchor.net.au> 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.
# 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
# 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
# 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
# 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
# by Jamie Wilkinson <jamie@anchor.net.au>
# 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
<VirtualHost *:80>
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
<IfModule mod_suexec.c>
SuexecUserGroup USER USER
</IfModule>
<IfModule mod_deflate.c>
AddOutputFilterByType DEFLATE text/html text/plain text/xml text/css
</IfModule>
</VirtualHost>
## HOWTO:
## * correct the IP address on the <VirtualHost> line
## * put your cursor on _THIS_ line
## * :.,$s/^#//
##
#<VirtualHost *:443>
# 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
#
# <IfModule mod_suexec.c>
# SuexecUserGroup USER USER
# </IfModule>
#
# <IfModule mod_deflate.c>
# AddOutputFilterByType DEFLATE text/html text/plain text/xml text/css
# </IfModule>
# <IfModule mod_ssl.c>
# SSLEngine on
#
# # :.,+3s,DOMAIN,THE_DOMAIN,
# SSLCertificateFile /etc/ssl/www.DOMAIN_crt
# SSLCertificateKeyFile /etc/ssl/www.DOMAIN_key
#
# SSLCACertificateFile /etc/ssl/foo.crt
# </IfModule>
#</VirtualHost>
webalize
This shit is ballin'!
#!/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 <barney@anchor.net.au> - 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 <<USAGE
usage: $0 [ --domain domain | --log access_log | --user username ]
USAGE
}
sub debug (@) {
print @_ if -t STDOUT;
}
sub process_with_awstats($$$) {
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 or die "couldn't create target directory $target";
#copy "/var/www/html/usage/htaccess_awstats_template", $target; # this line is redundant, I think. no sense keeping a per-domain template
}
# copy the template htaccess if it doesn't exist
if (! -f $target . "/.htaccess") {
copy("$root/var/www/html/usage/htaccess_awstats_template", $target . "/.htaccess") or die "failed copying in htaccess template";
# set up the password protection
my @htaccess;
tie @htaccess, 'Tie::File', $target . "/.htaccess";
for (@htaccess) {
s/USERNAME/$user/;
s/DOMAIN/$domain/;
}
untie @htaccess;
}
# Most of the time we just use the system-wide condif
# ensure $CONF/awstats.$DOMAIN.conf exists
my $CONF = "$root/etc/awstats";
my $conf = $CONF . "/awstats.$domain.conf";
if (! -f $conf) {
my @config;
# ensure the permanent config for running the cgi exists and is correct
tie @config, 'Tie::File', $conf;
@config = ();
if (-e "$root/etc/debian_version") {
push @config, "Include \"$root/etc/awstats/awstats.conf\"";
} else {
push @config, "Include \"$root/etc/awstats/awstats.model.conf\"";
}
push @config, "";
push @config, "SiteDomain=\"" . $domain . "\"";
push @config, "DirData=\"". $target . "\"";
push @config, "HostAliases=\"www.". $domain . "\"";
# this log format is "anchor"
#push @config, "LogFormat=\"%virtualname %host %method %logname %time1 %bytesd %url %code %refererquot %uaquot\"";
untie @config;
}
# We also offer the option of per-user awstats.conf - if there is one, fiddle things to use it instead
# confdir might point to /tmp, if we're using the user's config fragment
my $confdir = "";
# now create a config to build the database with
$conf = "/home/$user/awstats.conf";
if (-f $conf) {
debug "using config fragment at $conf \n";
$confdir = "-configdir=/tmp";
my @config;
tie @config, 'Tie::File', "/tmp/awstats.$domain.conf";
@config = ();
push @config, "Include \"/etc/awstats/awstats.model.conf\"";
push @config, "";
push @config, "SiteDomain=\"" . $domain . "\"";
push @config, "DirData=\"". $target . "\"";
push @config, "HostAliases=\"www.". $domain . "\"";
# this log format is "anchor"
#push @config, "LogFormat=\"%virtualname %host %method %logname %time1 %bytesd %url %code %refererquot %uaquot\"";
my @user;
tie @user, 'Tie::File', $conf;
for (@user) {
next if (m/DirData/);
next if (m/SiteDomain/);
next if (m/HostAliases/);
next if (m/Include/);
next if (m/LogFile/);
next if (m/LogFormat/);
push @config, $_;
}
untie @config;
debug "written temporary config to /tmp/awstats.$domain.conf\n";
}
# We can use gzipped or plain logfiles, we're that cool
my $logfile;
if ($log =~ m/.gz$/) {
$logfile = "\"gzip -cd $log |\"";
} else {
$logfile = $log;
}
my $updatecmd = "$awstats $confdir -config=$domain -dir=$target -update -LogFile=$logfile";
debug "exec'ing $updatecmd\n";
open AWSTATSUPDATE, $updatecmd . " 2>&1 >/dev/null |"
or die "awstats update exec pipe failed: $!";
while (<AWSTATSUPDATE>) {
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 (<WEBALIZER>) {
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;