top_http_users 10.5 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76
#!/bin/ksh
#
# $Id: top_http_users 22 2005-04-11 17:21:15Z jerome $
# ----------------------------------------------------------------------
# AlternC - Web Hosting System
# Copyright (C) 2003 by the AlternC Development Team.
# http://alternc.org
# ----------------------------------------------------------------------
# Based on:
# Valentin Lacambre's web hosting softwares: http://altern.org/
# ----------------------------------------------------------------------
# LICENSE
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License (GPL)
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# To read the license please visit http://www.gnu.org/copyleft/gpl.html
# ----------------------------------------------------------------------
# Original Author of file: Jerome Moinet
# Purpose of file: Parse the apache logs to give the n most active users
# ----------------------------------------------------------------------
#
PATH=""
PROG_NAME=top_http_users
PROG_VERSION=0.1.0
ALTERNC_ROOT=/var/alternc
ALTERNC_ETC=/etc/alternc
ALTERNC_LIB=/usr/lib/alternc
ALTERNC_CONF_FILE=$ALTERNC_ETC/local.sh
LOG_DIR=/var/log/apache
TMP_ROOT=$ALTERNC_ROOT/tmp
RES_FILE=$TMP_ROOT/$PROG_NAME.res.$$
INTERMEDIATE_FILE=$TMP_ROOT/$PROG_NAME.int.$$
LOCK_FILE=/var/run/$PROG_NAME
export TEXTDOMAIN=alternc-admintools
YES=yes
NO=no

# Be sure to use the right programs
# and be sure they are there
awk=/usr/bin/awk
grep=/bin/grep
cat=/bin/cat
zcat=/bin/zcat
head=/usr/bin/head
id=/usr/bin/id
sort=/usr/bin/sort
rm=/bin/rm
mysql=/usr/bin/mysql
sed=/bin/sed
cut=/usr/bin/cut
tail=/usr/bin/tail
ls=/bin/ls
gettext=/usr/bin/gettext
printf=/usr/bin/printf
lockfileremove=/usr/bin/lockfile-remove
lockfilecreate=/usr/bin/lockfile-create
lockfiletouch=/usr/bin/lockfile-touch

# Must have gettext first to display error messages
[ -x "$gettext" ] || { echo "Cannot execute $gettext"; exit 1 ; }

for i in $awk $grep $cat $zcat $head $id $sort $rm $mysql $sed $cut $tail $ls $printf $lockfileremove $lockfilecreate $lockfiletouch
do
	[ -x "$i" ] || { echo "$($gettext "Unable to execute") ${i}."; exit 1 ; }
done


#-------------------------
77
set_messages()
78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108
#-------------------------
{
	# Language-dependent messages
	# Uses gettext and mo files.
	# Don't change these messages, change the .po file instead.

	USAGE=$($gettext -e "Usage: top_http_users [ options ] number\n\ntop_http_users is a program that gives brief statistics\non apache usage by parsing the apache logs.\n\nOptions:\n  -h, --help           This help text.\n  -v, --version        Show version.\n  -z, --use-gz-logs    Parse gzipped and .1, ...n apache logs instead of just parsing the current log.\n  -s, --ssl            Parse the apache-ssl logs instead of parsing the non-ssl apache logs.\n  -n, --number=NUMBER  parse the NUMBER last lines of the current log.\n                     This option is not compatible with the --use-gz-logs option.\nSee the top_http_users(8) manual page for more information.")
	NOT_FOUND_MSG=$($gettext "does not exist.")
	NON_NUM_MSG=$($gettext "The \"number\" argument must be a number.")
	NON_COMPATIBLE_MSG=$($gettext "The -n and -z options are not compatible.")
	NON_NUM_MSG_FOR_N=$($gettext "The -n option requieres a number as argument.")
	LOCKFILE_CREATION_FAILED=`$printf "$($gettext "%s is allready beeing executed.")" $PROG_NAME`
	NON_ROOT_MSG=$($gettext "You have to be root (uid 0) to execute this program.")
	MISSING_PROG=$($gettext "Unable to execute")
	HIT_RES_MSG=`$printf "$($gettext "Top %s domains served by apache, sorted by number of lines in log (using gzipped logs: %s):")" $NB_USERS $($gettext "$USE_GZ_LOGS")`
	SIZE_RES_MSG=`$printf "$($gettext "Top %s domains served by apache, sorted by size (using gzipped logs: %s):")" $NB_USERS $($gettext "$USE_GZ_LOGS")`
	TIME_RES_MSG=`$printf "$($gettext "Top %s domains served by apache, sorted by execution time in seconds (using gzipped logs: %s):")" $NB_USERS $($gettext "$USE_GZ_LOGS")`
	ACCOUNT_HIT_RES_MSG=`$printf "$($gettext "Top %s AlternC accounts served by apache, sorted by number of lines in logs (using gzipped logs: %s):")" $NB_USERS $($gettext "$USE_GZ_LOGS")`
	ACCOUNT_SIZE_RES_MSG=`$printf "$($gettext "Top %s AlternC accounts served by apache, sorted by size (using gzipped logs: %s):")" $NB_USERS $($gettext "$USE_GZ_LOGS")`
	ACCOUNT_TIME_RES_MSG=`$printf "$($gettext "Top %s AlternC accounts served by apache, sorted by execution time in seconds (using gzipped logs: %s):")" $NB_USERS $($gettext "$USE_GZ_LOGS")`
	DEBUG_1_MSG=$($gettext "Parsing")
	DEBUG_2_MSG=$($gettext "Getting account for each domain")
	DEBUG_3_MSG=$($gettext "Printing results")
	MISSING_CONF_FILE=`$printf "$($gettext "Can't find %s. Are you sure AlterncC is properly installed?")" $ALTERNC_CONF_FILE`
	MYSQL_UNREACHABLE_DATABASE=`$printf "$($gettext "Cannot access accounts database. Please check either %s or Mysql state.")" $ALTERNC_CONF_FILE`
	SQL_ERROR_MSG=$($gettext -e "Sorry, an sql error appeared. The error message is:\n%s")
	UNKNOWN_OPTION=$($gettext "Unknown %s option.")
}


#-------------------------
109
trap_EXIT()
110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170
#-------------------------
{
	# Does some cleaning
	$rm -f $RES_FILE
	$rm -f $INTERMEDIATE_FILE
	$lockfileremove $LOCK_FILE
	exit
}
trap trap_EXIT INT KILL TERM QUIT ABRT STOP HUP


#-------------------------
# Main
#-------------------------
set_messages
# Must be root
[ "`$id -u`" -ne 0 ] && { echo $NON_ROOT_MSG ; exit 1 ; }

# Parse args
IS_N_PARAM=false
USE_GZ_LOGS="$NO"
N_PARAM=""
COMMAND=$cat
for ARG in "$@" ; do
	[ "$IS_N_PARAM" = "true" ] && { shift ; IS_N_PARAM=false ; continue ; }
	[ "`$printf "$ARG\n" | $cut -c1`" != "-" ] && [ "$IS_N_PARAM" = "false" ] &&  break

	case $ARG in
		-h | --help        ) echo $PROG_NAME version $PROG_VERSION ; $printf "$USAGE\n" ; exit ;;
		-v | --version     ) echo $PROG_NAME version $PROG_VERSION ; exit ;;
		-s | --ssl         ) LOG_DIR=/var/log/apache-ssl ;;
		-z | --use-gz-logs ) USE_GZ_LOGS="$YES" ;;
		-n | --number=*    ) 
			if [ "$ARG" != "-n" ] ; then
				N_PARAM=`echo $ARG | $cut -d"=" -f2`
			else
				N_PARAM=$2
				IS_N_PARAM=true
			fi
			[ `echo "$N_PARAM" | $grep -c [^0-9]` != 0 ] && { echo "$NON_NUM_MSG_FOR_N" ; exit 1 ; }
			COMMAND="$tail -n $N_PARAM"
			;;
		*) $printf "${UNKNOWN_OPTION}\n" $ARG ; exit 1 ;;
	esac
	shift
done
LOG_FILE=$LOG_DIR/access.log

# -n and -z options are not compatible
[ "$USE_GZ_LOGS" = "$YES" ] && ! [ -z "$N_PARAM" ] && { echo $NON_COMPATIBLE_MSG ; exit 1 ; }
# Must have only 1 parameter
[ -z "$1" ] || [ "$#" -gt 1 ] && { $printf "$USAGE\n" ; exit 1 ; }
# Argument "number"  must be numeric
[ `echo "$1" | $grep -c [^0-9]` != 0 ] && { echo "$NON_NUM_MSG" ; exit 1 ; } || NB_USERS=$1
# These dirs/files must exist
[ -d "$LOG_DIR" ] || { echo "$LOG_DIR $NOT_FOUND_MSG" ; exit 1 ; }
[ -d "$TMP_ROOT" ] || { echo "$TMP_ROOT $NOT_FOUND_MSG" ; exit 1 ; }
[ -f "$LOG_FILE" ] || { echo "$LOG_FILE $NOT_FOUND_MSG" ; exit 1 ; }
# Have to get AlternC conf file :
[ -f "$ALTERNC_CONF_FILE" ] || { echo $MISSING_CONF_FILE ; exit 1 ; } && . $ALTERNC_CONF_FILE
# Must have access to mysql to retreive accounts owning domains :
171 172 173
mysql="$mysql --defaults-file=/etc/alternc/my.cnf -B -N -e"
$mysql "select count(*) from domaines_standby;" > /dev/null 2>&1
[ "$?" != 0 ] && { echo "$MYSQL_UNREACHABLE_DATABASE" ; exit 1 ; }
174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245
# Prevents executing more than one shell at the same time
$lockfilecreate --retry 1 $LOCK_FILE
if [ $? != 0 ]
then
	echo $LOCKFILE_CREATION_FAILED
	exit 1
fi
$lockfiletouch $LOCK_FILE &
BADGER="$!"


# Does the stuff
set_messages
# Have to parse files one by one or else system wil go on knees
[ "$DEBUG" ] && echo "$DEBUG_1_MSG $LOG_FILE" ; $COMMAND $LOG_FILE | $awk '{z=NF-1 ; domaine[$NF]++ ; if ($10 != "-") size[$NF]+=$10 ; time[$NF]+=$z} END {for (item in domaine) print item" "domaine[item]" "size[item]" "time[item]}' > $RES_FILE
for FILE in `$ls -1 $LOG_FILE.* | $grep -v "\.gz$"`; do
	[ "$USE_GZ_LOGS" = "$YES" ] && [ -f $FILE ] && { [ "$DEBUG" ] && echo "$DEBUG_1_MSG $FILE" ; $cat $FILE | $awk '{z=NF-1 ; domaine[$NF]++ ; if ($10 != "-") size[$NF]+=$10 ; time[$NF]+=$z} END {for (item in domaine) print item" "domaine[item]" "size[item]" "time[item]}' >> $RES_FILE ; }
done
if [ "$USE_GZ_LOGS" = "$YES" ] 
then
	for GZLOG in $LOG_FILE.*.gz
	do
		[ "$DEBUG" ] && echo "$DEBUG_1_MSG $GZLOG"
		$zcat $GZLOG | $awk '{z=NF-1 ; domaine[$NF]++ ; if ($10 != "-") size[$NF]+=$10 ; time[$NF]+=$z} END {for (item in domaine) print item" "domaine[item]" "size[item]" "time[item]}' >> $RES_FILE
	done
fi


# show results
$cat $RES_FILE | $awk '{domaine[$1]+=$2 ; size[$1]+=$3 ; time[$1]+=$4} END {for (item in domaine) print item" "domaine[item]" "size[item]" "time[item]}' > $INTERMEDIATE_FILE

[ "$DEBUG" ] && echo $DEBUG_2_MSG
> $RES_FILE
for i in `$cat $INTERMEDIATE_FILE | $sed s/" "/"@@@@"/g`
	do
		TMP=`echo $i | $sed s/"@@@@"/" "/g`
		DOMAIN=`echo $TMP | $cut -d " " -f1 | $sed s/\"//g | $sed s/"\\\\\\\\"/""/g`
		[ "$DEBUG" ] && echo DOMAIN : $DOMAIN
		ACCOUNT=`$mysql "select a.login, a.mail from membres a, sub_domaines b where a.uid = b.compte and concat(if(sub=\"\", \"\", concat(sub, \".\")), domaine)  = \"${DOMAIN}\";" 2>&1`
		[ "$?" != 0 ] && { $printf "$SQL_ERROR_MSG\n" "  $ACCOUNT" ; kill "${BADGER}" ; trap_EXIT ; }
		! [ -z "$ACCOUNT" ] && [ `echo $ACCOUNT | $grep -c "^ERROR"` = 0 ] && echo "$ACCOUNT $TMP" >> $RES_FILE
	done


[ "$DEBUG" ] && echo $DEBUG_3_MSG
echo $HIT_RES_MSG
$cat $INTERMEDIATE_FILE | $awk {'printf ("%20.0f %s\n", $2, $1)'} | $sort -gr | $head -n$NB_USERS
echo ""
echo $SIZE_RES_MSG
$cat $INTERMEDIATE_FILE | $awk {'printf ("%20.0f %s\n", $3, $1)'} | $sort -gr | $head -n$NB_USERS
echo ""
echo $TIME_RES_MSG
$cat $INTERMEDIATE_FILE | $awk {'printf ("%20.0f %s\n", $4, $1)'} | $sort -gr | $head -n$NB_USERS

$cat $RES_FILE | $awk '{size[$1]+=$5 ; time[$1]+=$6 ; hit[$1]+=$4 ; mail[$1]=$2} END {for (item in size) print size[item]" "time[item]" "hit[item]" "item" "mail[item]}' > $INTERMEDIATE_FILE

echo ""
echo $ACCOUNT_HIT_RES_MSG
$cat $INTERMEDIATE_FILE | $awk {'printf ("%20.0f %s (%s)\n", $3, $4, $5)'} | $sort -gr | $head -n$NB_USERS
echo ""
echo $ACCOUNT_SIZE_RES_MSG
$cat $INTERMEDIATE_FILE | $awk {'printf ("%20.0f %s (%s)\n", $1, $4, $5)'} | $sort -gr | $head -n$NB_USERS
echo ""
echo $ACCOUNT_TIME_RES_MSG
$cat $INTERMEDIATE_FILE | $awk {'printf ("%20.0f %s (%s)\n", $2, $4, $5)'} | $sort -gr | $head -n$NB_USERS


# cleanly exit and remove temp files
kill "${BADGER}"
trap_EXIT