MeidokonWiki:

Various stuff used personally or at work

These might be out of date, no warranty, etc.

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;

MeidokonWiki: Useful_shell_scripts (last edited 2010-02-03 20:36:01 by furinkan)