diff options
Diffstat (limited to 'src/pmdas/weblog')
-rw-r--r-- | src/pmdas/weblog/GNUmakefile | 75 | ||||
-rw-r--r-- | src/pmdas/weblog/Install | 694 | ||||
-rw-r--r-- | src/pmdas/weblog/README | 205 | ||||
-rw-r--r-- | src/pmdas/weblog/Remove | 43 | ||||
-rw-r--r-- | src/pmdas/weblog/Web.Alarms.pmchart | 22 | ||||
-rwxr-xr-x | src/pmdas/weblog/Web.Allservers.pmchart | 90 | ||||
-rwxr-xr-x | src/pmdas/weblog/Web.Perserver.Bytes.pmchart | 90 | ||||
-rwxr-xr-x | src/pmdas/weblog/Web.Perserver.Requests.pmchart | 91 | ||||
-rw-r--r-- | src/pmdas/weblog/Web.Requests.pmchart | 27 | ||||
-rw-r--r-- | src/pmdas/weblog/Web.Volume.pmchart | 25 | ||||
-rw-r--r-- | src/pmdas/weblog/check_match.c | 414 | ||||
-rw-r--r-- | src/pmdas/weblog/help | 654 | ||||
-rw-r--r-- | src/pmdas/weblog/pmda.c | 1205 | ||||
-rw-r--r-- | src/pmdas/weblog/pmns | 306 | ||||
-rw-r--r-- | src/pmdas/weblog/root | 10 | ||||
-rwxr-xr-x | src/pmdas/weblog/server.sh | 1228 | ||||
-rw-r--r-- | src/pmdas/weblog/sproc.c | 39 | ||||
-rw-r--r-- | src/pmdas/weblog/weblog.c | 3132 | ||||
-rw-r--r-- | src/pmdas/weblog/weblog.h | 140 | ||||
-rwxr-xr-x | src/pmdas/weblog/weblogconv.sh | 62 |
20 files changed, 8552 insertions, 0 deletions
diff --git a/src/pmdas/weblog/GNUmakefile b/src/pmdas/weblog/GNUmakefile new file mode 100644 index 0000000..aa68c72 --- /dev/null +++ b/src/pmdas/weblog/GNUmakefile @@ -0,0 +1,75 @@ +# +# Copyright (c) 2000-2001,2004 Silicon Graphics, Inc. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License 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. +# + +TOPDIR = ../../.. +include $(TOPDIR)/src/include/builddefs + +IAM = weblog +DOMAIN = WEBSERVER +TARGETS = $(IAM)$(EXECSUFFIX) check_match$(EXECSUFFIX) +CFILES = weblog.c pmda.c sproc.c +HFILES = weblog.h +SCRIPTS = Install Remove server.sh weblogconv.sh +CHARTS = Web.Alarms.pmchart Web.Requests.pmchart Web.Volume.pmchart \ + Web.Allservers.pmchart Web.Perserver.Bytes.pmchart \ + Web.Perserver.Requests.pmchart +DFILES = README +LSRCFILES = pmns help $(DFILES) root $(SCRIPTS) check_match.c $(CHARTS) + +LDIRT = domain.h $(TARGETS) check_match.o + +PMDADIR = $(PCP_PMDAS_DIR)/weblog +PMCHARTDIR = $(PCP_VAR_DIR)/config/pmchart +CONFDIR = $(PCP_VAR_DIR)/config/web + +LDLIBS = $(PCP_PMDALIB) $(LIB_FOR_PTHREADS) + +default: build-me + +include $(BUILDRULES) + +ifneq "$(TARGET_OS)" "mingw" +build-me: $(TARGETS) + +install: build-me + # $(INSTALL) -d $(CONFDIR) + # $(INSTALL) -m 644 weblog.conf $(CONFDIR)/weblog.conf + $(INSTALL) -d $(PMDADIR) + $(INSTALL) -m 755 $(IAM) $(PMDADIR)/pmda$(IAM) + $(INSTALL) -m 755 check_match $(SCRIPTS) $(PMDADIR) + $(INSTALL) -m 644 $(DFILES) root help pmns domain.h $(PMDADIR) + $(INSTALL) -m 644 Web.Alarms.pmchart $(PMCHARTDIR)/Web.Alarms + $(INSTALL) -m 644 Web.Requests.pmchart $(PMCHARTDIR)/Web.Requests + $(INSTALL) -m 644 Web.Volume.pmchart $(PMCHARTDIR)/Web.Volume + $(INSTALL) -m 755 Web.Allservers.pmchart $(PMCHARTDIR)/Web.Allservers + $(INSTALL) -m 755 Web.Perserver.Bytes.pmchart $(PMCHARTDIR)/Web.Perserver.Bytes + $(INSTALL) -m 755 Web.Perserver.Requests.pmchart $(PMCHARTDIR)/Web.Perserver.Requests +else +build-me: +install: +endif + +weblog$(EXECSUFFIX): $(OBJECTS) + +weblog.o: domain.h + +check_match$(EXECSUFFIX): check_match.o + $(CCF) -o $@ $(LDFLAGS) check_match.o $(LDLIBS) + +domain.h: ../../pmns/stdpmid + $(DOMAIN_MAKERULE) + +default_pcp: default + +install_pcp: install diff --git a/src/pmdas/weblog/Install b/src/pmdas/weblog/Install new file mode 100644 index 0000000..dcc86a1 --- /dev/null +++ b/src/pmdas/weblog/Install @@ -0,0 +1,694 @@ +#! /bin/sh +# +# Copyright (c) 2000,2003,2004 Silicon Graphics, Inc. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License 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. +# +# Install the weblog PMDA and/or PMNS +# + +. $PCP_DIR/etc/pcp.env +. $PCP_SHARE_DIR/lib/pmdaproc.sh + +# Override function from pmdaproc.sh +__choose_mode() +{ + if [ -n "$QUIET_INSTALL" ] ; then + do_pmda=true + else + __def=m + $do_pmda && __def=b + echo \ +'You will need to choose an appropriate configuration for installation of +the "'$iam'" Performance Metrics Domain Agent (PMDA). + + collector collect performance statistics on this system + monitor allow this system to monitor local and/or remote systems + both collector and monitor configuration for this system +' + while true + do + $PCP_ECHO_PROG $PCP_ECHO_N 'Please enter c(ollector) or m(onitor) or b(oth) ['$__def'] '"$PCP_ECHO_C" + read ans + case "$ans" + in + "") break + ;; + c|collector|b|both) + do_pmda=true + break + ;; + m|monitor) + do_pmda=false + break + ;; + *) echo "Sorry, that is not acceptable response ..." + ;; + esac + done + echo + + fi +} + +iam=weblog +pmda_interface=2 +forced_restart=false + +pmdaSetup + +pmns_name=web # metric names differ from PMDA name +daemon_opt=true # can install as daemon +dso_opt=false +pipe_opt=true # pipe IPC - YES +socket_opt=false # socket IPC - NO +socket_inet_def=2080 # default TCP port for Internet socket IPC +check_delay=10 # give the PMDA a chance to set itself up + +# PMDA specific constants +# +configDir=$PCP_VAR_DIR/config/web + +# PMDA variables +# +tmp=`mktemp -d /tmp/pcp.XXXXXXXXX` || exit 1 +trap "rm -rf $tmp; exit" 0 1 2 3 15 + +debugFlag=0 +do_debug=false + +configFile="" +delay=15 +chkDelay=20 +maxserv=80 + + +# --- start functions --- +# +_parseDefaults() +{ + echo "Extracting options from current installation ..." + while getopts D:d:i:l:n:pS:t:u: c + do + case $c in + \?) echo "Warning: Unrecognized option in $PCP_PMCDCONF_PATH" + echo " Remove line for pmdaweblog in $PCP_PMCDCONF_PATH and re-run ./Install" + exit 2;; + D) debugFlag=$OPTARG;; + n) chkDelay=$OPTARG;; + t) delay=$OPTARG;; + S) maxserv=$OPTARG;; + *) # old or boring flags, silently ignore + ;; + esac + done + shift `expr $OPTIND - 1` + if [ $# -eq 1 ] + then + configFile=$1 + elif [ $# -eq 0 ] + then + configFile="" + else + echo "Warning: unrecognized format for old specification in $PCP_PMCDCONF_PATH" + echo " Remove line for pmdaweblog in $PCP_PMCDCONF_PATH and re-run ./Install" + exit 2 + fi +} + +_defaultRegex() +{ + touch $1 + echo ' +# Common regular expressions specifications for parsing access and error logs +# Each regular expression specification should have a name (one word), +# specify the order of regex parameters (method and size), and +# a regular expression. Regular expressions for access logs require two +# arguments to be set while errors logs require only a match. +# +# Set the online HTML Users and Administrators Guide, pmdaweblog(1) and +# regexec(3) for more details. +# + +# pattern for CERN, NCSA, Netscape, Apache etc Access Logs +regex_posix CERN method,size ][ \\]+"([A-Za-z][-A-Za-z]+) [^"]*" [-0-9]+ ([-0-9]+) +# pattern for CERN, NCSA, Netscape etc Error Logs +regex_posix CERN_err - . +# pattern for Proxy Server Extended Log Format +regex_posix NS_PROXY 1,3,2,4 ][ ]+"([A-Za-z][-A-Za-z]+) [^"]*" ([-0-9]+) ([-0-9]+) ([-0-9]+) +# pattern for Squid Cache logs +regex_posix SQUID 4,3,2,1 [0-9]+\.[0-9]+[ ]+[0-9]+ [a-zA-Z0-9\.]+ ([_A-Z]+)\/([0-9]+) ([0-9]+) ([A-Z]+) +# pattern for Netscape SOCKS Server Access logs +regex_posix NS_SOCKS method,size (sockd)\[.*, ([0-9]+) bytes from .* \(http\) +# pattern for Netscape SOCKS Server Error logs +regex_posix NS_SOCKS_err - . +# pattern for FTP through a Netscape SOCKS Server Access log +regex_posix NS_FTP method,size (sockd)\[.*, ([0-9]+) bytes from .* \([0-9]+\) +# pattern for FTP through a Netscape SOCKS Server Error logs +regex_posix NS_FTP_err - . +# pattern for FTP Server access logs (normally in SYSLOG) +regex_posix SYSLOG_FTP method,size ftpd\[.*\]: ([gp][-A-Za-z]+)( ) +# pattern for FTP Server error logs (normally in SYSLOG) +regex_posix SYSLOG_FTP_err - FTP LOGIN FAILED +# pattern for WU_FTP Server access logs (normally in xferlog) +regex_posix WU_FTP size,method :[0-9][0-9] [0-9]+ [0-9]+ .+ ([0-9]+) .+ [ba] .+ ([io]) [arg] +# pattern for WU_FTP Server error logs (normally in SYSLOG/messages) +regex_posix WU_FTP_err - failed login + +# Server specifications. The format of each specification is +# "server" serverName on|off accessRegex accessFile errorRegex errorFile +# +# Set the online HTML Users and Administrators Guide and pmdaweblog(1) +# for more details. +#' >> $1 +} + +_parse_server() +{ + egrep "^server" | $PCP_AWK_PROG ' + { i=index($2, ":"); + if (i == 0) { + name = $2; + port = ""; + } + else { + name = substr($2,1,i-1); + port = sprintf("Port %d", substr($2, i+1, length($2) - i)); + } + printf("Server %s %s\n", name, port); + printf(" Access Log: %s (%s)\n", $5, $4); + printf(" Error Log: %s (%s)\n\n", $7, $6); + }' +} + +_default_config () +{ + rm -f $tmp/conf + touch $tmp/conf + _defaultRegex $tmp/conf + ./server.sh -q -l $tmp/conf + egrep "^server" $tmp/conf > /dev/null 2>&1 + _st=$? + if [ $_st -eq 0 ] + then + ./pmdaweblog -C $tmp/conf >$tmp/out 2>&1 + _st=$? + if [ $_st -eq 0 ] ; then + if [ -z "$configFile" ] + then + configFile=$configDir/$iam.conf + fi + rm -f $configFile + cp $tmp/conf $configFile + args="-D $debugFlag -t $delay -n $chkDelay -S $maxserv $configFile" + socket_opt=false + fi + fi + return $_st +} + +# +# --- end functions --- + +if $do_pmda +then + + [ ! -d $configDir ] && mkdir -p $configDir + + if [ -n "$QUIET_INSTALL" ] ; then + _default_config + if [ $? -eq 0 ] ; then + pmdaInstall + exit $? + else + exit 1 + fi + else + echo "----------------------------------------------------------------" + echo + echo "The default installation of the weblog PMDA will search for known" + echo "Web server configurations on this host and will setup the weblog" + echo "PMDA to monitor all associated Web server log files." + echo + echo "Otherwise, you will be prompted for the required information." + echo + $PCP_ECHO_PROG $PCP_ECHO_N "Do you want a default weblog PMDA installation [y] ""$PCP_ECHO_C" + read ans + echo + if [ "X$ans" = X -o "X$ans" = Xy -o "X$ans" = XY ] + then + _default_config + if [ $? -eq 0 ] ; then + pmdaInstall + exit $? + else + echo + echo "Unable to find any Web servers!" + echo "Reverting to detailed installation..." + fi + fi + + echo "----------------------------------------------------------------" + echo + $PCP_ECHO_PROG $PCP_ECHO_N "Checking for a previous PMDA installation ...""$PCP_ECHO_C" + + # weblogs -> weblog can be removed once all 1.0 betas are known to + # have gone away + ans=`$PCP_AWK_PROG < $PCP_PMCDCONF_PATH ' + $1 == "'$iam'" { + printf "%s",$6 + for (i=7;i<=NF;i++) printf " %s",$i + print "" + }'` + if [ -n "$ans" ] + then + echo " found" + _parseDefaults $ans + else + echo " appears to be a first-time install" + fi + + if [ -n "$configFile" ] + then + if [ -f "$configFile" ] + then + if [ $PCP_PLATFORM = linux ] && \ + egrep '^regex ' $configFile > /dev/null + then + echo "Warning: previous configuration file \"$configFile\"" + echo " appears to be an incompatible version." + $PCP_ECHO_PROG $PCP_ECHO_N "Do you wish to automatically update the configuration file? [y] ""$PCP_ECHO_C" + read ans + if [ "X$ans" = X -o "$ans" = "y" -o "$ans" = "Y" ] + then + ./weblogconv.sh $configFile $tmp/conf + if ./pmdaweblog -C $tmp/conf > /dev/null 2>&1 + then + cp $tmp/conf $configFile + else + echo "Warning: automatic conversion failed." + echo "You can either continue, and use the default configuration file or exit" + echo "this install procedure to manually update your existing configuration." + $PCP_ECHO_PROG $PCP_ECHO_N "Do you wish to continue with the default configuration? [n] ""$PCP_ECHO_C" + read ans + if [ "$ans" = "y" -o "$ans" = "Y" ] + then + configFile="" + else + exit 1 + fi + fi + fi + else + echo "Using previous configuration file \"$configFile\"" + fi + else + echo "Warning: previous configuration file \"$configFile\" no longer" + echo " exists, reverting to default" + configFile="" + fi + fi + + if [ "X$configFile" = X -a -f $configDir/$iam.conf ] + then + configFile=$configDir/$iam.conf + echo "Using previous configuration file \"$configFile\"" + fi + + if [ "X$configFile" != X ] + then + if [ -f $configFile ] + then + echo "The inital configuration file contains the following Web server details:" + echo + cat $configFile | _parse_server | ${PAGER-more} + echo + echo "------------------------------------------------------------------------------" + + if ./pmdaweblog -C $configFile >$tmp/out 2>&1 + then + : + else + echo "Warning: parsing this configuration file produced the following errors," + echo " and this file will be ignored." + + cat $tmp/out + echo + if [ "X$configFile" = "X$tmp/default" ] + then + echo "Arrgh ... this is the default configuration, I cannot recover from here!" + exit 1 + fi + configFile="" + fi + fi + fi + + echo + echo "A configuration file can be automatically generated. This can" + echo "be used to compare or replace an existing configuration file." + echo + + if [ "X$configFile" = X ] + then + $PCP_ECHO_PROG $PCP_ECHO_N "Do you want a configuration file to be automatically generated [y] ""$PCP_ECHO_C" + read ans + if [ "X$ans" = X ] + then + ans="y" + fi + else + $PCP_ECHO_PROG $PCP_ECHO_N "Do you want a configuration file to be automatically generated [n] ""$PCP_ECHO_C" + read ans + if [ "X$ans" = X ] + then + ans="n" + fi + fi + + if [ "X$ans" = "Xy" -o "X$ans" = "XY" ] + then + echo + echo "Now scanning for Web servers ..." + echo + + if [ ! -x ./server.sh ] + then + echo "Unable to scan for Web servers as ./server.sh is missing!" + else + rm -f $tmp/conf + touch $tmp/conf + _defaultRegex $tmp/conf + ./server.sh -l $tmp/conf + if egrep "^server" $tmp/conf > /dev/null 2>&1 + then + echo + echo "This is a possible configuration file for your system:" + echo + cat $tmp/conf | _parse_server | ${PAGER-more} + echo + echo "------------------------------------------------------------------------------" + echo + + if ./pmdaweblog -C $tmp/conf > /dev/null 2>&1 + then + if [ "X$configFile" = X ] + then + $PCP_ECHO_PROG $PCP_ECHO_N "Would you like to use this configuration file [y] ""$PCP_ECHO_C" + read ans + if [ "X$ans" = "Xy" -o "X$ans" = "XY" -o "X$ans" = X ] + then + cp $tmp/conf $configDir/$iam.conf + configFile=$configDir/$iam.conf + fi + else + echo "Would you like to replace your existing configuration file with" + $PCP_ECHO_PROG $PCP_ECHO_N "the generated file [n] ""$PCP_ECHO_C" + read ans + if [ "X$ans" != "Xn" -a "X$and" != "XN" -a "X$ans" != X ] + then + cp $tmp/conf $configFile + fi + fi + else + echo "Automated configuration file generation is broken!" + if [ "X$configFile" = X ] + then + echo "Please consult the manual on how to create a configuration file." + echo "Installation failed." + exit 1 + else + echo "Ignoring this file." + fi + fi + else + echo + echo "I could not find any Web servers." + fi + echo + fi + fi + + echo "------------------------------------------------------------------------------" + + echo + if [ "X$configFile" = X ] + then + $PCP_ECHO_PROG $PCP_ECHO_N "Do you want to specify some Web servers [n]: ""$PCP_ECHO_C" + serverAdded="false" + else + $PCP_ECHO_PROG $PCP_ECHO_N "Do you want to specify some more Web servers [n]: ""$PCP_ECHO_C" + serverAdded="true" + fi + + read ans + while [ "X$ans" = "Xy" -o "X$ans" = "XY" ] + do + if [ "X$configFile" = X ] + then + if [ "X$configFile" = X -a -f $configDir/$iam.conf ] + then + echo "Replacing existing configuration file $configDir/$iam.conf" + rm -f $configDir/$iam.conf + else + echo "Creating configuration file $configDir/$iam.conf" + fi + _defaultRegex $configDir/$iam.conf + configFile="$configDir/$iam.conf" + fi + + echo + serverName=`hostname` + $PCP_ECHO_PROG $PCP_ECHO_N "The name of the Web server [$serverName]: ""$PCP_ECHO_C" + read ans + if [ "X$ans" = X ] + then + serverName=`hostname` + else + serverName=$ans + fi + + echo + accessPath="" + while [ "X$accessPath" = X ] + do + $PCP_ECHO_PROG $PCP_ECHO_N "The path to the access log: + ""$PCP_ECHO_C" + read accessPath + if [ "X$accessPath" != X ] + then + if [ -f $accessPath ] + then + : + else + echo "$accessPath does not exist or is not a regular file" + accessPath="" + fi + fi + done + + echo + errorPath="" + while [ "X$errorPath" = X ] + do + $PCP_ECHO_PROG $PCP_ECHO_N "The path to the error log: + ""$PCP_ECHO_C" + read errorPath + if [ "X$errorPath" != X ] + then + if [ -f $errorPath ] + then + : + else + echo "$errorPath does not exist or is not a regular file" + errorPath="" + fi + fi + done + + echo + echo "The configuration file contains these specifications:" + echo + ${PAGER-more} $configFile + echo + echo "Does the configuration file contain appropriate regular expressions" + $PCP_ECHO_PROG $PCP_ECHO_N "for the \"$serverName\" Web server [y]: ""$PCP_ECHO_C" + read ans + echo + if [ "X$ans" = "Xn" -o "X$ans" = "XN" ] + then + $PCP_ECHO_PROG $PCP_ECHO_N "Do you wish to quit the installation to add new regular expressions [y]: ""$PCP_ECHO_C" + read ans + if [ "$Xans" = "Xy" -o "X$ans" = "XY" -o "X$ans" = X ] + then + echo "Edit $configFile and then rerun this Install script." + exit 1 + echo + echo "Skipping $serverName ..." + fi + else + accessRegex="" + while [ "X$accessRegex" = X ] + do + if egrep "^regex_posix CERN " $configFile > /dev/null 2>&1 + then + $PCP_ECHO_PROG $PCP_ECHO_N "The regex for the access log [CERN]: ""$PCP_ECHO_C" + accessRegex="CERN" + else + $PCP_ECHO_PROG $PCP_ECHO_N "The regex for the access log: ""$PCP_ECHO_C" + accessRegex="" + fi + read ans + if [ "X$ans" != X ] + then + accessRegex=$ans + fi + if [ "X$accessRegex" != X ] + then + if egrep "^regex_posix $accessRegex " $configFile > /dev/null 2>&1 + then + : + else + echo "Could not find $accessRegex in $configFile" + accessRegex="" + fi + fi + done + + echo + errorRegex="" + while [ "X$errorRegex" = X ] + do + if egrep "^regex_posix CERN_err " $configFile > /dev/null 2>&1 + then + $PCP_ECHO_PROG $PCP_ECHO_N "The regex for the error log [CERN_err]: ""$PCP_ECHO_C" + errorRegex="CERN_err" + else + $PCP_ECHO_PROG $PCP_ECHO_N "The regex for the error log: ""$PCP_ECHO_C" + errorRegex="" + fi + read ans + if [ "X$ans" != X ] + then + errorRegex=$ans + fi + if [ "X$errorRegex" != X ] + then + if egrep "^regex_posix $errorRegex " $configFile > /dev/null 2>&1 + then + : + else + echo "Could not find $errorRegex in $configFile" + errorRegex="" + fi + fi + done + + echo + echo "You have specified the following Web server:" + echo + server="server $serverName on $accessRegex $accessPath $errorRegex $errorPath" + echo "$server" + echo + $PCP_ECHO_PROG $PCP_ECHO_N "Is this correct [y]: + ""$PCP_ECHO_C" + read ans + if [ "X$ans" = "Xy" -o "X$ans" = "XY" -o "X$ans" = X ] + then + echo >> $configFile + echo "# User configured server called \"$serverName\"" >> $configFile + echo $server >> $configFile + serverAdded="true" + fi + fi + + echo + $PCP_ECHO_PROG $PCP_ECHO_N "Do you wish to specify another Web Server [n]: ""$PCP_ECHO_C" + read ans + echo + done + + if [ "$serverAdded" = "false" ] + then + rm -f $configFile + configFile="" + fi + + if [ "X$configFile" = X ] + then + echo "Please consult the manual on how to create a configuration file." + echo "Installation failed as no servers were specified." + exit 1 + fi + + echo + echo "You may modify the configuration file by hand and add servers" + echo "that are not currently listed, change their names, etc." + echo + $PCP_ECHO_PROG $PCP_ECHO_N "Do you wish to exit and modify the configuration file [n] ""$PCP_ECHO_C" + read ans + if [ "X$ans" != "Xn" -a "X$ans" != "XN" -a "X$ans" != X ] + then + echo + echo "Edit $configFile and then rerun this Install script." + exit 1 + fi + + echo + echo "------------------------------------------------------------------------------" + + echo + $PCP_ECHO_PROG $PCP_ECHO_N "The delay in seconds between forced reads of the log files [$delay] ""$PCP_ECHO_C" + read ans + if [ "X$ans" != X ] + then + delay=$ans + fi + + echo + $PCP_ECHO_PROG $PCP_ECHO_N "Number of seconds of inactivity before checking for log rotation [$chkDelay] ""$PCP_ECHO_C" + read ans + if [ "X$ans" != X ] + then + chkDelay=$ans + fi + + echo + $PCP_ECHO_PROG $PCP_ECHO_N "The maximum number of servers per agent process [$maxserv] ""$PCP_ECHO_C" + read ans + if [ "X$ans" != X ] + then + maxserv=$ans + fi + + if [ "$do_debug" = true ] + then + echo + $PCP_ECHO_PROG $PCP_ECHO_N "the Debugging Flag (see pmdbg(1)) [$debugFlag] ""$PCP_ECHO_C" + read ans + if [ "X$ans" != X ] + then + debugFlag=$ans + fi + fi + + args="-D $debugFlag -t $delay -n $chkDelay -S $maxserv $configFile" + + echo + echo "------------------------------------------------------------------------------" + echo + fi +fi + +pmdaInstall + +exit 0 + diff --git a/src/pmdas/weblog/README b/src/pmdas/weblog/README new file mode 100644 index 0000000..611a188 --- /dev/null +++ b/src/pmdas/weblog/README @@ -0,0 +1,205 @@ +Performance Co-Pilot Weblog PMDA for Monitoring of Web Server logs +================================================================== + +This PMDA is capable of monitoring the activity of multiple Web servers, +in terms of requests and bytes, in real time. The PMDA can also monitor +proxy server, SOCKS server and ftpd logs. + +Site configuration is discussed in the online HTML documentation located +at $PCP_DOC_DIR/pcpweb. This should be read before proceeding any further +with installing this PMDA. The file $PCP_DOC_DIR/pcpweb/README contains +instructions for installing this documentation. + +During the installation process, you may be prompted for several +parameters which will affect the behavior of the weblog PMDA. These +are discussed in the pmdaweblog(1) man page. + + +Installation of the Weblog PMDA +=============================== + +1. Check that there is no clash with the Performance Metrics Domain + number defined in domain.h and the other PMDAs currently in use + (see $PCP_PMCDCONF_PATH). If there is, edit domain.h and choose + another domain number. + +2. Ensure that the web server control files can be correctly located as + follows. + + Web Server Default Directory Environment Search for Config + Type Variable File(s) and/or Logs + Below the Default + Directory + + Netscape /usr/ns-home $NSROOTPATH httpd-*/obj.conf + and httpd-*/magnus.conf + /var/netscape/suitespot https-*/obj.conf + https-*/magnus.conf + proxy-*/obj.conf + proxy-*/magnus.conf + + Netscape /usr/ns-home $NSROOTPATH proxy-server/logs/sockd + Proxy + + Netscape /var/ns-proxy $NSPROXYPATH logs/access + Proxy logs/errors + logs/sockd + + Outbox /var/www/htdocs/outbox $OUTBOXPATH logs/access + logs/errors + + NCSA /var/www $NCSAPATH server/logs/access_log + server/logs/error_log + + Zeus /usr/local/zeus $ZEUSPATH server.ini + log/transfer + log/errors + + Apache /usr/apache $APACHEPATH conf/httpd.conf + conf/srm.conf + log/access_log + log/error_log + + Anon FTP /etc/passwd $PASSWDPATH [file, not dir] for ~ftp + /var/adm/SYSLOG $SYSLOGPATH [file, not dir] for + access and errors + + To over-ride the Default Directory for a particular type of Web + server, set the corresponding Environment Variable to the absolute + pathname of the directory. As a special case $NSROOTPATH for the + non-proxy Netscape Web server can be set to a colon (:) separated + list of directory names to be searched (in the style of the $PATH + for /bin/sh). + + +3. Then run the Install script (as root) + + # cd $PCP_PMDAS_DIR/weblog + # ./Install + +4. The installation script will prompt if this is a collector and/or + monitor installation. Briefly: + + o if there are Web servers on this host, then this is a collector host. + + o if monitoring tools (pmchart(1), pmlogger(1) etc.) will be run on + this host, then this is a monitoring host. + + Consult the HTML documentation for more details. A monitoring host + installation will install only the namespace and some application + configuration files. + +5. The next prompt will ask if this is a default installation. The + default installation will search for known Web server configurations + and install the PMDA to monitor any logs that are found. This is + appropriate for first time installations. The non-default + installation is described in points 6 to 8. + +6. The configuration file for the weblog PMDA must be found and + checked. The Install script will look in the likely places for an + existing file and prompt for confirmation. Otherwise, a + configuration file can be automatically generated by searching known + Web server configuration files and directories. + +7. The second stage of the Install script prompts for the pmdaweblog(1) + parameters. The default values should be adequate for an initial + installation. + +8. The final stage will install the agent and restart PMCD (the + Performance Metrics Collection Daemon). The Install script should + report that the Metrics are OK. + + +De-installation +=============== + +Simply use (as root) + + # cd $PCP_PMDAS_DIR/weblog + # ./Remove + + +Changing the settings +===================== + +The safest way to alter any settings that were entered in the Install +script is to re-run the Install script. Changes to the weblog.conf file +can be also be registered by running the Install script. + +To quickly test changes to the configuration files, the agent and pmcd +can be restarted as follows: + + To register any changes made to the weblog.conf file, the agent + must be killed and restarted: + + # pmsignal -a -s KILL pmdaweblog + # pmsignal -a -s HUP pmcd + + To register any changes to the $PCP_PMCDCONF_PATH file you must + restart PMCD: + + # $PCP_RC_DIR/pcp start + + +Troubleshooting +=============== + +0. If there is trouble locating the Web server access and error logs, + try running the server.sh script with diagnostics: + + $ cd $PCP_PMDAS_DIR/weblog + $ ./server.sh -q -v </dev/null + +1. After installing or restarting the agent, the PMCD log file + ($PCP_LOG_DIR/pmcd/pmcd.log) and the PMDA log file + ($PCP_LOG_DIR/pmcd/weblog.log) should be checked for any warnings + or errors. + +2. If the Install script reports some warnings when checking the + metrics, the problem should be listed in one of the log files. + +3. If the PMDA is configured to look at the correct access logs, and + the Web server is demonstrably updating those access logs, but the + exported performance metrics are not being updated, then the problem + may be in the pattern matching. To diagnose this: + + Find the corresponding "server" line in + $PCP_VAR_DIR/config/web/weblog.conf, e.g. + + server ha2.melbourne.sgi.com:80 on \ + CERN /usr/ns-home/httpd-ha2/logs/access \ + CERN_err /usr/ns-home/httpd-ha2/logs/errors + + the pattern is symbolicly named after the word "on" (CERN above) and + the path to the access log follows + (/usr/ns-home/httpd-ha2/logs/access above). These two are used as + the last two arguments to check_match below: + + $ cd $PCP_PMDAS_DIR/weblog + $ ./check_match $PCP_VAR_DIR/config/web/weblog.conf \ + CERN /usr/ns-home/httpd-ha2/logs/access + + If things are working OK, expect to see lines like: + + [1] match: method="GET" size="17198" + [2] match: method="GET" size="-" + [3] match: method="GET" size="27102" + [4] match: method="POST" size="4503" + [5] match: method="HEAD" size="-" + + If this does not happen, you need to review the format of the lines + in the access logs and modify the pattern by reference to the + regcmp(3) man page. + +4. Additional information can be logged if there appears to be problems + with the monitoring of server log files. Running the Install script + with the -D flag will add a prompt for a debugging flag. This can be + a combination of bits given by pmdbg -l: + + # pmdbg -l + + The application flags will cause the PMDA to report additional + information in $PCP_LOG_DIR/pmcd/weblog.log. DBG_TRACE_APPL0 reports + the least information and DBG_TRACE_APPL2 may report too much if the + server is handling many requests. + diff --git a/src/pmdas/weblog/Remove b/src/pmdas/weblog/Remove new file mode 100644 index 0000000..50beaa2 --- /dev/null +++ b/src/pmdas/weblog/Remove @@ -0,0 +1,43 @@ +#! /bin/sh +# +# Copyright (c) 2000,2004 Silicon Graphics, Inc. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License 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. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +# +# Remove the weblog PMDA +# + +# Get standard environment +. $PCP_DIR/etc/pcp.env + +# Get the common procedures and variable assignments +# +. $PCP_SHARE_DIR/lib/pmdaproc.sh + +# The name of the PMDA +# +iam=weblog + +# Do it +# +#_setup +pmdaSetup + +pmns_name=web # metric names differ from PMDA name + +#_remove +pmdaRemove + +exit 0 diff --git a/src/pmdas/weblog/Web.Alarms.pmchart b/src/pmdas/weblog/Web.Alarms.pmchart new file mode 100644 index 0000000..d346f80 --- /dev/null +++ b/src/pmdas/weblog/Web.Alarms.pmchart @@ -0,0 +1,22 @@ +#pmchart +# +# Web statistics (error rates) +# +# This file is installed by the script $PCP_PMDAS_DIR/weblog/Install +# +Version 2.0 host dynamic + +Chart Title "Web Alarms" Style stacking + Plot Color #-cycle Host * Metric web.allservers.errors + Plot Color #-cycle Host * Metric network.tcp.drops + Plot Color #-cycle Host * Metric network.tcp.conndrops + Plot Color #-cycle Host * Metric network.tcp.timeoutdrop + Plot Color #-cycle Host * Metric network.tcp.sndrexmitpack + Plot Color #-cycle Host * Metric network.tcp.rcvbadsum + Plot Color #-cycle Host * Metric network.tcp.rexmttimeo + Plot Color #-cycle Host * Metric network.mbuf.failed + Plot Color #-cycle Host * Metric network.mbuf.waited + Plot Color #-cycle Host * Metric swap.pagesout + +# +# Created Thu Jul 2 10:48:36 1998 diff --git a/src/pmdas/weblog/Web.Allservers.pmchart b/src/pmdas/weblog/Web.Allservers.pmchart new file mode 100755 index 0000000..4185e1b --- /dev/null +++ b/src/pmdas/weblog/Web.Allservers.pmchart @@ -0,0 +1,90 @@ +#!/bin/sh + +. $PCP_DIR/etc/pcp.env + +tmp=`mktemp -d /var/tmp/pcp.XXXXXXXXX` || exit 1 +trap "rm -rf $tmp; exit" 0 1 2 3 15 + +echo "/\"/s///g" >$tmp/sed + +pmprobe -I $* web.perserver.bytes.cached.total web.perserver.bytes.total > $tmp/pmprobe +l1=`head -n 1 $tmp/pmprobe` +l2=`tail -n 1 $tmp/pmprobe` + +num_caches=`echo $l1 | cut -f2 -d\ ` +num_servers=`echo $l2 | cut -f2 -d\ ` +if [ $num_servers -gt 0 ] +then + caches=`echo $l1 | cut -f3- -d\ ` + servers=`echo $l2 | cut -f3- -d\ ` +# hostname=`echo $servers | cut -f1 -d: | sed -f $tmp/sed` + + if [ $num_caches -le 0 ] + then + # an old pmda - quietly handle all servers as if they were CERN - show only totals + caches="NeVeR_MaTcH" + num_caches=0 + fi +elif [ $num_servers -eq 0 ] +then + $PCP_XCONFIRM_PROG -c -B OK -header "No Active Servers - cannot continue" \ + -t "$message" \ + -icon info > /dev/null + exit +else + message=`pmerr $num_servers | cut -f5- -d\ ` + $PCP_XCONFIRM_PROG -c -B OK -header "Fatal error - cannot continue" \ + -t "$message" \ + -icon error > /dev/null + exit +fi + +# +# if too many instances, turn off all legends +# +legendp=on +if [ $num_servers -gt 6 ] +then + legendp=off +fi + +if [ $num_servers -gt 12 ] +then + $PCP_XCONFIRM_PROG -c -B Cancel -b Continue -header \ + "Too many charts" \ + -t "There is 1 chart per server, more than can reasonably be displayed on the screen" \ + -icon warning | grep Cancel >/dev/null +if [ $? -eq 0 ] +then + exit +fi +fi + +# chart preamble +# +cat > $tmp/base <<End-of-File +#pmchart +Version 2.0 host dynamic + +End-of-File + +if [ $num_caches -ne $num_servers ] +then + echo Chart Title \"Total Requests serviced by all servers \" Style bars Legend off>> $tmp/base + echo Plot Color \#FF3030 Host \* Metric web.allservers.requests.total >> $tmp/base + echo Chart Title \"Total Bytes sent by all servers \" Style bars Legend off>> $tmp/base + echo Plot Color \#FF3030 Host \* Metric web.allservers.bytes.total >> $tmp/base +fi +if [ $num_caches -gt 0 ] +then + echo Chart Title \"Total Requests serviced by caching servers \" Style stacking Legend $legendp >> $tmp/base + echo Plot Color \#FFFF30 Host \* Metric web.allservers.requests.client.total >> $tmp/base + echo Plot Color \#3030FF Host \* Metric web.allservers.requests.cached.total >> $tmp/base + echo Plot Color \#FF3030 Host \* Metric web.allservers.requests.uncached.total >> $tmp/base + echo Chart Title \"Total Bytes sent by caching servers \" Style stacking Legend $legendp >> $tmp/base + echo Plot Color \#3030FF Host \* Metric web.allservers.bytes.cached.total >> $tmp/base + echo Plot Color \#FF3030 Host \* Metric web.allservers.bytes.uncached.total >> $tmp/base +fi + +cat $tmp/base +rm -rf $tmp diff --git a/src/pmdas/weblog/Web.Perserver.Bytes.pmchart b/src/pmdas/weblog/Web.Perserver.Bytes.pmchart new file mode 100755 index 0000000..1f57a27 --- /dev/null +++ b/src/pmdas/weblog/Web.Perserver.Bytes.pmchart @@ -0,0 +1,90 @@ +#!/bin/sh + +. $PCP_DIR/etc/pcp.env + +tmp=`mktemp -d /var/tmp/pcp.XXXXXXXXX` || exit 1 +trap "rm -rf $tmp; exit" 0 1 2 3 15 + +echo "/\"/s///g" >$tmp/sed + +pmprobe -I $* web.perserver.bytes.cached.total web.perserver.bytes.total > $tmp/pmprobe +l1=`head -n 1 $tmp/pmprobe` +l2=`tail -n 1 $tmp/pmprobe` + +num_caches=`echo $l1 | cut -f2 -d\ ` +num_servers=`echo $l2 | cut -f2 -d\ ` +if [ $num_servers -gt 0 ] +then + caches=`echo $l1 | cut -f3- -d\ ` + servers=`echo $l2 | cut -f3- -d\ ` + + if [ $num_caches -lt 0 ] + then + # an old pmda - quietly handle all servers as if they were CERN - show only totals + caches="NeVeR_MaTcH" + num_caches=0 + fi +elif [ $num_servers -eq 0 ] +then + $PCP_XCONFIRM_PROG -c -B OK -header "No Active Servers - cannot continue" \ + -t "$message" \ + -icon info > /dev/null + exit +else + message=`pmerr $num_servers | cut -f5- -d\ ` + $PCP_XCONFIRM_PROG -c -B OK -header "Fatal error - cannot continue" \ + -t "$message" \ + -icon error > /dev/null + exit +fi + +# +# if too many instances, turn off all legends +# +legendp=on +if [ $num_servers -gt 6 ] +then + legendp=off +fi + +if [ $num_servers -gt 12 ] +then + $PCP_XCONFIRM_PROG -c -B Cancel -b Continue -header \ + "Too many charts" \ + -t "There is 1 chart per server, more than can reasonably be displayed on the screen" \ + -icon warning | grep Cancel >/dev/null +if [ $? -eq 0 ] +then + exit +fi +fi + +# chart preamble +# +cat > $tmp/base <<End-of-File +#pmchart +Version 2.0 host dynamic + +End-of-File + +i=1 +while [ $i -le $num_servers ] +do +server=`echo $servers | cut -f$i -d\ ` +echo $caches | grep $server >/dev/null +if [ $? -eq 0 ] +then + j=`echo $server | sed -f $tmp/sed` + echo Chart Title \"Bytes sent by $j\" Style stacking Legend $legendp >> $tmp/base + echo Plot Color \#3030FF Host \* Metric web.perserver.bytes.cached.total Instance $j >> $tmp/base + echo Plot Color \#FF3030 Host \* Metric web.perserver.bytes.uncached.total Instance $j >> $tmp/base +else + j=`echo $server | sed -f $tmp/sed` + echo Chart Title \"Total Bytes sent by $j\" Style bars Legend off>> $tmp/base + echo Plot Color \#FF3030 Host \* Metric web.perserver.bytes.total Instance $j >> $tmp/base +fi +i=`expr $i + 1` +done + +cat $tmp/base +rm -rf $tmp diff --git a/src/pmdas/weblog/Web.Perserver.Requests.pmchart b/src/pmdas/weblog/Web.Perserver.Requests.pmchart new file mode 100755 index 0000000..77ff366 --- /dev/null +++ b/src/pmdas/weblog/Web.Perserver.Requests.pmchart @@ -0,0 +1,91 @@ +#!/bin/sh + +. $PCP_DIR/etc/pcp.env + +tmp=`mktemp -d /var/tmp/pcp.XXXXXXXXX` || exit 1 +trap "rm -rf $tmp; exit" 0 1 2 3 15 + +echo "/\"/s///g" >$tmp/sed + +pmprobe -I $* web.perserver.bytes.cached.total web.perserver.bytes.total > $tmp/pmprobe +l1=`head -n 1 $tmp/pmprobe` +l2=`tail -n 1 $tmp/pmprobe` + +num_caches=`echo $l1 | cut -f2 -d\ ` +num_servers=`echo $l2 | cut -f2 -d\ ` +if [ $num_servers -gt 0 ] +then + caches=`echo $l1 | cut -f3- -d\ ` + servers=`echo $l2 | cut -f3- -d\ ` + + if [ $num_caches -lt 0 ] + then + # an old pmda - quietly handle all servers as if they were CERN - show only totals + caches="NeVeR_MaTcH" + num_caches=0 + fi +elif [ $num_servers -eq 0 ] +then + $PCP_XCONFIRM_PROG -c -B OK -header "No Active Servers - cannot continue" \ + -t "$message" \ + -icon info > /dev/null + exit +else + message=`pmerr $num_servers | cut -f5- -d\ ` + $PCP_XCONFIRM_PROG -c -B OK -header "Fatal error - cannot continue" \ + -t "$message" \ + -icon error > /dev/null + exit +fi + +# +# if too many instances, turn off all legends +# +legendp=on +if [ $num_servers -gt 6 ] +then + legendp=off +fi + +if [ $num_servers -gt 12 ] +then + $PCP_XCONFIRM_PROG -c -B Cancel -b Continue -header \ + "Too many charts" \ + -t "There is 1 chart per server, more than can reasonably be displayed on the screen" \ + -icon warning | grep Cancel >/dev/null +if [ $? -eq 0 ] +then + exit +fi +fi + +# chart preamble +# +cat > $tmp/base <<End-of-File +#pmchart +Version 2.0 host dynamic + +End-of-File + +i=1 +while [ $i -le $num_servers ] +do +server=`echo $servers | cut -f$i -d\ ` +echo $caches | grep $server >/dev/null +if [ $? -eq 0 ] +then + j=`echo $server | sed -f $tmp/sed` + echo Chart Title \"Requests satisfied by $j\" Style stacking Legend $legendp >> $tmp/base + echo Plot Color \#FFFF30 Host \* Metric web.perserver.requests.client.total Instance $j >> $tmp/base + echo Plot Color \#3030FF Host \* Metric web.perserver.requests.cached.total Instance $j >> $tmp/base + echo Plot Color \#FF3030 Host \* Metric web.perserver.requests.uncached.total Instance $j >> $tmp/base +else + j=`echo $server | sed -f $tmp/sed` + echo Chart Title \"Total Requests satisfied by $j\" Style bars Legend off>> $tmp/base + echo Plot Color \#FF3030 Host \* Metric web.perserver.requests.total Instance $j >> $tmp/base +fi +i=`expr $i + 1` +done + +cat $tmp/base +rm -rf $tmp diff --git a/src/pmdas/weblog/Web.Requests.pmchart b/src/pmdas/weblog/Web.Requests.pmchart new file mode 100644 index 0000000..7775fd5 --- /dev/null +++ b/src/pmdas/weblog/Web.Requests.pmchart @@ -0,0 +1,27 @@ +#pmchart +# +# Web statistics (request rates) +# +# This file is installed by the script $PCP_PMDAS_DIR/weblog/Install +# +Version 2.0 host dynamic + +Chart Title "Requests by HTTP method" Style stacking + Plot Color rgbi:1.0/1.0/0.0 Host * Metric web.allservers.requests.get + Plot Color rgbi:0.0/1.0/1.0 Host * Metric web.allservers.requests.post + Plot Color rgbi:1.0/0.0/1.0 Host * Metric web.allservers.requests.head + Plot Color rgbi:1.0/1.0/0.6 Host * Metric web.allservers.requests.other +Chart Title "Requests by request size" Style stacking + Plot Color rgbi:1.0/0.8/0.6 Host * Metric web.allservers.requests.size.zero + Plot Color rgbi:0.6/1.0/0.6 Host * Metric web.allservers.requests.size.le3k + Plot Color rgbi:0.8/0.6/1.0 Host * Metric web.allservers.requests.size.le10k + Plot Color rgbi:1.0/0.65/0.3 Host * Metric web.allservers.requests.size.le30k + Plot Color rgbi:0.3/1.0/0.3 Host * Metric web.allservers.requests.size.le100k + Plot Color rgbi:0.65/0.3/1.0 Host * Metric web.allservers.requests.size.le300k + Plot Color rgbi:1.0/0.5/0.0 Host * Metric web.allservers.requests.size.le1m + Plot Color rgbi:0.0/1.0/0.0 Host * Metric web.allservers.requests.size.le3m + Plot Color rgbi:0.6/0.0/0.9 Host * Metric web.allservers.requests.size.gt3m + Plot Color rgbi:1.0/0.35/0.0 Host * Metric web.allservers.requests.size.unknown + +# +# Created Thu Jul 2 10:48:19 1998 diff --git a/src/pmdas/weblog/Web.Volume.pmchart b/src/pmdas/weblog/Web.Volume.pmchart new file mode 100644 index 0000000..569abd9 --- /dev/null +++ b/src/pmdas/weblog/Web.Volume.pmchart @@ -0,0 +1,25 @@ +#pmchart +# +# Web Statistics (data volume) +# +# This file is installed by the script $PCP_PMDAS_DIR/weblog/Install +# +Version 2.0 host dynamic + +Chart Title "Bytes sent by HTTP method" Style stacking + Plot Color rgbi:1.0/1.0/0.0 Host * Metric web.allservers.bytes.get + Plot Color rgbi:0.0/1.0/1.0 Host * Metric web.allservers.bytes.post + Plot Color rgbi:1.0/0.0/1.0 Host * Metric web.allservers.bytes.head + Plot Color rgbi:1.0/1.0/0.6 Host * Metric web.allservers.bytes.other +Chart Title "Bytes sent by request size" Style stacking + Plot Color rgbi:0.6/1.0/0.6 Host * Metric web.allservers.bytes.size.le3k + Plot Color rgbi:0.8/0.6/1.0 Host * Metric web.allservers.bytes.size.le10k + Plot Color rgbi:1.0/0.65/0.3 Host * Metric web.allservers.bytes.size.le30k + Plot Color rgbi:0.3/1.0/0.3 Host * Metric web.allservers.bytes.size.le100k + Plot Color rgbi:0.65/0.3/1.0 Host * Metric web.allservers.bytes.size.le300k + Plot Color rgbi:1.0/0.5/0.0 Host * Metric web.allservers.bytes.size.le1m + Plot Color rgbi:0.0/1.0/0.0 Host * Metric web.allservers.bytes.size.le3m + Plot Color rgbi:0.6/0.0/0.9 Host * Metric web.allservers.bytes.size.gt3m + +# +# Created Thu Jul 2 10:47:51 1998 diff --git a/src/pmdas/weblog/check_match.c b/src/pmdas/weblog/check_match.c new file mode 100644 index 0000000..5166e75 --- /dev/null +++ b/src/pmdas/weblog/check_match.c @@ -0,0 +1,414 @@ +/* + * Copyright (c) 2000,2003 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License 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. + */ + +/* + * Uses the same regular expression logic as pmdaweblog, but extracted + * here so new patterns and access logs can be tested + * + * Usage: check_match configfile pat_name [input] + * configfile regex spec file as used by pmdaweblog + * pat_name use only this names regex from configfile + * input test input to try and match, defaults to stdin + */ + +#include <ctype.h> +#include <pmapi.h> +#if defined(HAVE_REGEX_H) +#include <regex.h> +#endif +#include <sys/types.h> + +int +main(int argc, char *argv[]) +{ + FILE *fc; + char buf[1024]; + char *p; + char *q; +#ifdef HAVE_REGEX + char *comp = NULL; + char sub0[1024]; + char sub1[1024]; + char sub2[1024]; + char sub3[1024]; +#endif + int lno = 0; + int regex_posix = 0; + int cern_format = 0; + int common_extended_format = 0; + int squid_format = 0; + int methodpos = 1, c_statuspos = 2, sizepos = 2, s_statuspos = 2; + long client_cache_hits, proxy_cache_hits, remote_fetches; + double proxy_bytes, remote_bytes; +#if (defined HAVE_REGEXEC) && (defined HAVE_REGCOMP) + regex_t re = {0}; + regmatch_t pmatch[5]; + size_t nmatch = 5; +#endif + + + if (argc < 3 || argc > 4) { + fprintf(stderr, "Usage: check_match configfile pat_name [input]\n"); + exit(1); + } + + if ((fc = fopen(argv[1], "r")) == NULL) { + fprintf(stderr, "check_match: cannot open configfile \"%s\": %s\n", argv[1], osstrerror()); + exit(1); + } + + if (argc == 4) { + if (freopen(argv[3], "r", stdin) == NULL) { + fprintf(stderr, "check_match: cannot open input \"%s\": %s\n", argv[3], osstrerror()); + exit(1); + } + } + + while (fgets(buf, sizeof(buf), fc) != NULL) { + lno++; + + if (strncmp(buf, "regex", 5) != 0) continue; + if (strncmp(buf, "regex_posix", 11) == 0) { + regex_posix = 1; + p = &buf[11]; + } + else { + regex_posix = 0; + p = &buf[5]; + } + + while (*p && isspace((int)*p)) p++; + if (*p == '\0') continue; + q = p++; + while (*p && !isspace((int)*p)) p++; + if (*p == '\0') continue; + *p = '\0'; + + cern_format = squid_format = common_extended_format = 0; + + if (strcmp(q, argv[2]) == 0) { + if(regex_posix) { + + q = ++p; + while (*p && !isspace((int)*p)) p++; + if (*p == '\0') continue; + *p = '\0'; + fprintf(stderr, "args are (%s)\n", q); + if(strncmp(q, "method,size", 11) == 0) { + cern_format = 1; + methodpos = 1; + sizepos = 2; + } + else if(strncmp(q, "size,method", 11) == 0) { + methodpos = 2; + sizepos = 1; + } + else { + char *str; + int pos; + + pos = 1; + str=q; + do { + switch(str[0]) { + case '1': + methodpos = pos++; + break; + case '2': + sizepos = pos++; + break; + case '3': + c_statuspos = pos++; + break; + case '4': + s_statuspos = pos++; + break; + case '-': + methodpos = 1; + sizepos = 2; + str[0] = '\0'; + break; + case ',': + case '\0': + break; + default: + fprintf(stderr, + "could figure out arg order params (%s)\n", + str); + exit(1); + } + } while ( *str++ ); + + if(c_statuspos > 0 && s_statuspos > 0) { + if(strcmp(argv[2], "SQUID") == 0) + squid_format = 1; + else + common_extended_format = 1; + } else + cern_format = 1; + } + + fprintf(stderr, "cern: %d, cef: %d, squid: %d, MP: %d, SP: %d, CSP: %d, SSP: %d\n", + cern_format, common_extended_format, squid_format, + methodpos, sizepos, c_statuspos, s_statuspos); + } + + q = ++p; + while (*p && *p != '\n') p++; + while (p >= q && isspace((int)*p)) p--; + p[1] = '\0'; + if(regex_posix) { +#ifdef HAVE_REGCOMP + fprintf(stderr, "%s[%d]: regex_posix: %s\n", argv[1], lno, q); + fclose(fc); + if(regcomp(&re, q, REG_EXTENDED) != 0 ) { + fprintf(stderr, "Error: bad regular expression\n"); + exit(1); + } +#else + fprintf(stderr, "%s[%d]: no support for POSIX regexp\n", + argv[1], lno); +#endif + } + else { +#ifdef HAVE_REGCMP + if(strcmp(argv[2], "CERN") == 0) + cern_format = 1; + else if (strcmp(argv[2], "NS_PROXY") == 0) + common_extended_format = 1; + else if (strcmp(argv[2], "SQUID") == 0) + squid_format = 1; + + fprintf(stderr, "%s[%d]: regex: %s\n", argv[1], lno, q); + fclose(fc); + comp = regcmp(q, NULL); + if (comp == NULL) { + fprintf(stderr, "Error: bad regular expression\n"); + exit(1); + } +#else + fprintf(stderr, "%s[%d]: regcmp is not available\n", + argv[1], lno); +#endif + } + break; + } + } + + lno = 0; + remote_fetches = proxy_cache_hits = client_cache_hits = 0; + remote_bytes = proxy_bytes = 0.0; + while (fgets(buf, sizeof(buf), stdin) != NULL) { + lno++; + if(regex_posix) { +#ifdef HAVE_REGEXEC + if(regexec(&re, buf, nmatch, pmatch, 0) == 0) { + buf[pmatch[methodpos].rm_eo] = '\0'; + buf[pmatch[sizepos].rm_eo] = '\0'; + if(common_extended_format || squid_format) { + buf[pmatch[c_statuspos].rm_eo] = '\0'; + buf[pmatch[s_statuspos].rm_eo] = '\0'; + } + + if(common_extended_format) { + fprintf(stderr,"[%d] M: %s, S: %s, CS: %s, SS: %s\n", + lno, + &buf[pmatch[methodpos].rm_so], + &buf[pmatch[sizepos].rm_so], + &buf[pmatch[c_statuspos].rm_so], + &buf[pmatch[s_statuspos].rm_so]); + if(strcmp(&buf[pmatch[c_statuspos].rm_so], "200") == 0 && + strcmp(&buf[pmatch[s_statuspos].rm_so], "200") == 0) { + fprintf(stderr,"\tREMOTE fetch of %.0f bytes\n", + atof(&buf[pmatch[sizepos].rm_so])); + remote_fetches++; + remote_bytes += atof(&buf[pmatch[sizepos].rm_so]); + } + if(strcmp(&buf[pmatch[c_statuspos].rm_so], "200") == 0 && + (strcmp(&buf[pmatch[s_statuspos].rm_so], "304") == 0 || + strcmp(&buf[pmatch[s_statuspos].rm_so], "-") == 0)) { + fprintf(stderr,"\tCACHE return of %.0f bytes\n", + atof(&buf[pmatch[sizepos].rm_so])); + proxy_cache_hits++; + proxy_bytes += atof(&buf[pmatch[sizepos].rm_so]); + + } + if(strcmp(&buf[pmatch[c_statuspos].rm_so], "304") == 0 && + (strcmp(&buf[pmatch[s_statuspos].rm_so], "304") == 0 || + strcmp(&buf[pmatch[s_statuspos].rm_so], "-") == 0)) { + fprintf(stderr,"\tCLIENT hit of %.0f bytes\n", + atof(&buf[pmatch[sizepos].rm_so])); + client_cache_hits++; + } + } else if(squid_format) { + fprintf(stderr,"[%d] M: %s, S: %s, CS: %s, SS: %s\n", + lno, + &buf[pmatch[methodpos].rm_so], + &buf[pmatch[sizepos].rm_so], + &buf[pmatch[c_statuspos].rm_so], + &buf[pmatch[s_statuspos].rm_so]); + if(strcmp(&buf[pmatch[c_statuspos].rm_so], "200") == 0 && + (strstr(&buf[pmatch[s_statuspos].rm_so], + "_MISS")!=NULL || + strstr(&buf[pmatch[s_statuspos].rm_so], + "_CLIENT_REFRESH")!=NULL || + strstr(&buf[pmatch[s_statuspos].rm_so], + "_SWAPFAIL")!=NULL)){ + fprintf(stderr,"\tREMOTE fetch of %.0f bytes (code: %s, Squid result code: %s)\n", + atof(&buf[pmatch[sizepos].rm_so]), + &buf[pmatch[c_statuspos].rm_so], &buf[pmatch[s_statuspos].rm_so]); + remote_fetches++; + remote_bytes += atof(&buf[pmatch[sizepos].rm_so]); + } + if(strcmp(&buf[pmatch[c_statuspos].rm_so], "200") == 0 && + strstr(&buf[pmatch[s_statuspos].rm_so], "_HIT") != NULL) { + fprintf(stderr,"\tCACHE return of %.0f bytes (code: %s, Squid result code: %s)\n", + atof(&buf[pmatch[sizepos].rm_so]), + &buf[pmatch[c_statuspos].rm_so], &buf[pmatch[s_statuspos].rm_so]); + proxy_cache_hits++; + proxy_bytes += atof(&buf[pmatch[sizepos].rm_so]); + + } + if(strcmp(&buf[pmatch[c_statuspos].rm_so], "304") == 0 && + strstr(&buf[pmatch[s_statuspos].rm_so], "_HIT") != NULL) { + fprintf(stderr,"\tCLIENT hit of %.0f bytes (code: %s, Squid result code: %s)\n", + atof(&buf[pmatch[sizepos].rm_so]), + &buf[pmatch[c_statuspos].rm_so], &buf[pmatch[s_statuspos].rm_so]); + client_cache_hits++; + } + } else { + fprintf(stderr, "[%d] match: method=\"%s\" size=\"%s\"\n", lno, + &buf[pmatch[methodpos].rm_so], &buf[pmatch[sizepos].rm_so]); + } + } + else + fprintf(stderr, "[%d] no match: %s\n", lno, buf); +#else + fprintf(stderr, "[%d] - no regexec()\n", lno); +#endif + } + else { +#ifdef HAVE_REGEX + if (regex(comp, buf, sub0, sub1, sub2, sub3) != NULL) { + if(common_extended_format) { + + fprintf(stderr,"[%d] M: %s, S: %s, CS: %s, SS: %s\n", + lno, sub0, sub1, sub2, sub3); + + if(strcmp(sub2, "200") == 0 && + strcmp(sub3, "200") == 0 ) { + fprintf(stderr,"\tREMOTE fetch of %s bytes\n", sub1); + remote_fetches++; + remote_bytes += atof(sub1); + } + if(strcmp(sub2, "200") == 0 && + (strcmp(sub3, "304") == 0 || strcmp(sub3, "-") == 0)) { + fprintf(stderr,"\tCACHE return of %s bytes\n", sub1); + proxy_cache_hits++; + proxy_bytes += atof(sub1); + } + if(strcmp(sub2, "304") == 0 && + (strcmp(sub3, "304") == 0 || strcmp(sub3, "-") == 0)) { + fprintf(stderr,"\tCLIENT hit of %s bytes\n", sub1); + client_cache_hits++; + } + } else if(squid_format) { + + fprintf(stderr,"[%d] M: %s, S: %s, CS: %s, SS: %s\n", + lno, sub0, sub1, sub2, sub3); + + if(strcmp(sub2, "200") == 0 && + (strstr(sub3, "_MISS") != NULL || + strstr(sub3, "_CLIENT_REFRESH")!= NULL || + strstr(sub3, "_SWAPFAIL") != NULL)){ + + fprintf(stderr,"\tREMOTE fetch of %.0f bytes (code: %s, Squid result code: %s)\n", + atof(sub1), + sub2, sub3); + + remote_fetches++; + remote_bytes += atof(sub1); + } + if(strcmp(sub2, "200") == 0 && + strstr(sub3, "_HIT") != NULL) { + + fprintf(stderr,"\tCACHE return of %.0f bytes (code: %s, Squid result code: %s)\n", + atof(sub1), + sub2, sub3); + + proxy_cache_hits++; + proxy_bytes += atof(sub1); + + } + if(strcmp(sub2, "304") == 0 && + strstr(sub3, "_HIT") != NULL) { + + fprintf(stderr,"\tCLIENT hit of %.0f bytes (code: %s, Squid result code: %s)\n", + atof(sub3), + sub2, sub3); + + client_cache_hits++; + } + } else { + fprintf(stderr, "[%d] match: method=\"%s\" size=\"%s\"\n", lno, + sub0, sub1); + } + } + else + fprintf(stderr, "[%d] no match: %s\n", lno, buf); +#else + fprintf(stderr, "[%d] - no regex()\n", lno); +#endif + } + } + + if(common_extended_format || squid_format) { + fprintf(stderr,"Proxy Cache Summary Report\n\n"); + + fprintf(stderr, + "# requests %ld\n# client cache hits %ld\n# cache hits %ld\n# remote fetches %ld\n", + (client_cache_hits + proxy_cache_hits + remote_fetches), + client_cache_hits, proxy_cache_hits, remote_fetches); + fprintf(stderr, + "\nTotal Mbytes %f bytes\nFrom proxy cache %f Mbytes\nFrom remote sites %f Mbytes\n\n", + (proxy_bytes + remote_bytes)/1000000.0, + proxy_bytes/1000000.0, remote_bytes/1000000.0); + + fprintf(stderr, + "Client Cache %% hit rate: %.2f\n", + 100.0*(float)client_cache_hits/(float)(client_cache_hits + proxy_cache_hits + remote_fetches)); + fprintf(stderr, + "Proxy Cache %% hit rate: %.2f\n", + 100.0*(float)proxy_cache_hits/(float)(client_cache_hits + proxy_cache_hits + remote_fetches)); + fprintf(stderr, + "Local Cache %% hit rate: %.2f\n", + 100.0*(float)(client_cache_hits + proxy_cache_hits)/ + (float)(client_cache_hits + proxy_cache_hits + remote_fetches)); + + fprintf(stderr, + "\nAverage fetch size: Proxy -> Client: %.2f Kb\n", + proxy_bytes/proxy_cache_hits/1000.0); + fprintf(stderr, + "Average fetch size: Remote -> Client : %.2f Kb\n", + remote_bytes/remote_fetches/1000.0); + + fprintf(stderr,"\nClient Cache bandwidth reduction effectiveness: UNKNOWN\n"); + fprintf(stderr, + "Proxy Cache bandwidth reduction effectiveness: %f%%\n", + 100.0*proxy_bytes/(proxy_bytes + remote_bytes)); + + } + + exit(0); +} diff --git a/src/pmdas/weblog/help b/src/pmdas/weblog/help new file mode 100644 index 0000000..3a523e6 --- /dev/null +++ b/src/pmdas/weblog/help @@ -0,0 +1,654 @@ +# +# +# Copyright (c) 2000,2004 Silicon Graphics, Inc. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License 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. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +# + +@ web.config.numservers number of servers in configuration file +The number of Web servers specified in the configuration file. +The log files for these Web servers may or may not be monitored, +see web.perserver.watched and web.allservers.numwatched. + +@ web.config.catchup maximum time (secs) before Web server logs are probed +The time in seconds after which monitored Web server logs will be +examined, even if there have been no requests for performance metrics +from those logs. The "catch up" process spreads the load and minimizes +the latency at the first requests for metrics that have not been +requested for a long time. + +This metric has the initial value of the -t delay option to +pmdaweblog, and may be altered using pmStore(1). + +@ web.config.catchuptime time (secs) to perform catchup +Accumulated elapsed time in which the Web logs PMDA has been performing +the "catch up" process to examine all Web server logs. + +@ web.config.check time (secs) after which stationary logs will be re-opened +Web server log files that are not changing are periodically closed and +re-opened to detect possible log file rotation. This metric controls +how often a stationary log file will be re-opened. + +This metric has the initial value of the -n idlesec option to +pmdaweblog, and may be altered using pmStore(1). + +@ web.allservers.numwatched number of servers being monitored +The number of Web servers that are being monitored, as opposed +the number specified in the configuration file. + +See also web.config.numservers and web.perserver.watched. + +@ web.allservers.numalive number of watched servers that are alive +The number of servers that are being watchedly watched that have both +logs files. + +@ web.allservers.errors number of errors reported by all watched servers +The number of errors reported by all watched servers. + +@ web.allservers.requests.total requests processed by all servers +The total number of HTTP requests processed by all watched servers. + +@ web.allservers.bytes.total bytes sent by all servers +The total number of bytes sent by all watched servers. + +@ web.allservers.requests.get GET requests handled by all watched servers +The number of HTTP GET requests that were processed by all watched servers. + +@ web.allservers.bytes.get bytes sent in reply by all servers to GET requests +The number of bytes that have been sent by all watched servers in reply +to HTTP GET requests. + +@ web.allservers.requests.head HEAD requests handled by all watched servers +The number of HTTP HEAD requests that were processed by all watched +servers. + +@ web.allservers.bytes.head bytes sent in reply by all servers to HEAD requests +The number of bytes that have been sent by all watched servers in reply +to HTTP HEAD requests. + +@ web.allservers.requests.post POST requests handled by all watched servers +The number of HTTP POST requests that were processed by all watched +servers. + +@ web.allservers.bytes.post bytes sent in reply by all servers to POST requests +The number of bytes that have been sent by all watched servers in reply +to HTTP POST requests. + +@ web.allservers.requests.other other requests handled by all watch servers +The number of HTTP requests, other than GET, HEAD and POST, that were +processed by all watched servers. + +@ web.allservers.bytes.other bytes sent in reply by servers to other requests +The number of bytes that have been sent by this server in reply to HTTP +requests other than GET, HEAD or POST. + +@ web.allservers.requests.size.zero replies of 0 bytes sent by all servers +The total number of HTTP requests that required a response of 0 bytes from +all watched servers. + +@ web.allservers.bytes.size.zero total bytes sent in 0k replies +The total number of bytes sent in replies of 0k by all watched servers. +This metric is always zero and is provided for consistency only. + +@ web.allservers.requests.size.le3k replies of <= 3k sent by all servers +The number of HTTP requests that required a response of less than or equal +to 3k from all watched servers. + +@ web.allservers.bytes.size.le3k total bytes sent in <= 3k replies +The total number of bytes sent in replies of less than or equal to 3k in +size by all watched servers. + +@ web.allservers.requests.size.le10k replies of <= 10k sent by all servers +The number of HTTP requests that required a response of less than or equal +to 10k from all watched servers. + +@ web.allservers.bytes.size.le10k total bytes sent in <= 10k replies +The total number of bytes sent in replies of less than or equal to 10k in +size by all watched servers. + +@ web.allservers.requests.size.le30k replies of <= 30k sent by all servers +The number of HTTP requests that required a response of less than or equal +to 30k from all watched servers. + +@ web.allservers.bytes.size.le30k total bytes sent in <= 30k replies +The total number of bytes sent in replies of less than or equal to 30k in +size by all watched servers. + +@ web.allservers.requests.size.le100k replies of <= 100k sent by all servers +The number of HTTP requests that required a response of less than or equal +to 100k from all watched servers. + +@ web.allservers.bytes.size.le100k total bytes sent in <= 100k replies +The total number of bytes sent in replies of less than or equal to 100k in +size by all watched servers. + +@ web.allservers.requests.size.le300k replies of <= 300k sent by all servers +The number of HTTP requests that required a response of less than or equal +to 300k from all watched servers. + +@ web.allservers.bytes.size.le300k total bytes sent in <= 300k replies +The total number of bytes sent in replies of less than or equal to 300k in +size by all watched servers. + +@ web.allservers.requests.size.le1m replies of <= 1M sent by all servers +The number of HTTP requests that required a response of less than or equal +to 1M from all watched servers. + +@ web.allservers.bytes.size.le1m total bytes sent in <= 1M replies +The total number of bytes sent in replies of less than or equal to 1M in +size by all watched servers. + +@ web.allservers.requests.size.le3m replies of <= 3M sent by all servers +The number of HTTP requests that required a response of less than or equal +to 3M from all watched servers. + +@ web.allservers.bytes.size.le3m total bytes sent in <= 3M replies +The total number of bytes sent in replies of less than or equal to 3M in +size by all watched servers. + +@ web.allservers.requests.size.gt3m replies of > 3M sent by all servers +The number of HTTP requests that required a response of greater than +3M from all watched servers. + +@ web.allservers.bytes.size.gt3m total bytes sent > 3M replies +The total number of bytes sent in replies of greater than 3M in size by +all watched servers. + +@ web.allservers.requests.size.unknown replies of unknown size by all servers +The number of HTTP requests that required a response of unknown size +from all watched servers. + +@ web.allservers.requests.client.total requests satisfied by client caches for all cacheing servers +The total number of HTTP GET/IMS requests that resulted in "Not Modified" +responses from cache (and remote if checked). These are client cache hits. + +@ web.allservers.requests.cached.total requests satisfied by server caches for all cacheing servers +The total number of HTTP GET/IMS requests that resulted in "Not Modified" +responses from the remote site or were deemed cache hits via other +mechanisms such as recency. These are server cache hits and result in +data transferred from cache to client. + +@ web.allservers.requests.cached.size.zero replies of 0 bytes sent by all caches +The number of HTTP GET cache hits that required a response of 0 bytes from +all watched caches. + +@ web.allservers.requests.cached.size.le3k replies of <= 3k sent by all caches +The number of HTTP GET cache hits that required a response of less than +or equal to 3k from all watched caches. + +@ web.allservers.requests.cached.size.le10k replies of <= 10k sent by all caches +The number of HTTP GET cache hits that required a response of less than +or equal to 10k from all watched caches. + +@ web.allservers.requests.cached.size.le30k replies of <= 30k sent by all caches +The number of HTTP GET cache hits that required a response of less than +or equal to 30k from all watched caches. + +@ web.allservers.requests.cached.size.le100k replies of <= 100k sent by all caches +The number of HTTP GET cache hits that required a response of less than +or equal to 100k from all watched caches. + +@ web.allservers.requests.cached.size.le300k replies of <= 300k sent by all caches +The number of HTTP GET cache hits that required a response of less than +or equal to 300k from all watched caches. + +@ web.allservers.requests.cached.size.le1m replies of <= 1M sent by all caches +The number of HTTP GET cache hits that required a response of less than +or equal to 1M from all watched caches. + +@ web.allservers.requests.cached.size.le3m replies of <= 3M sent by all caches +The number of HTTP GET cache hits that required a response of less than +or equal to 3M from all watched caches. + +@ web.allservers.requests.cached.size.gt3m replies of > 3M sent by all caches +The number of HTTP GET cache hits that required a response of greater than +3M from all watched caches. + +@ web.allservers.requests.cached.size.unknown replies of unknown size by all caches +The number of HTTP GET cache hits that required a response of unknown +size from all watched caches. + +@ web.allservers.requests.uncached.total requests satisfied by remote server for all cacheing servers +The total number of HTTP GET/IMS requests that resulted in a real data +transfer from the remote server. These are either cache misses, or the +remote file had been modified since the cache entry was made. + +@ web.allservers.requests.uncached.size.zero replies of 0 bytes sent by all caches +The number of HTTP GET remote fetches that required a response of 0 +bytes from all watched caches. + +@ web.allservers.requests.uncached.size.le3k replies of <= 3k sent by all caches +The number of HTTP GET remote fetches that required a response of less +than or equal to 3k through all watched caches. + +@ web.allservers.requests.uncached.size.le10k replies of <= 10k sent by all caches +The number of HTTP GET remote fetches that required a response of less +than or equal to 10k through all watched caches. + +@ web.allservers.requests.uncached.size.le30k replies of <= 30k sent by all caches +The number of HTTP GET remote fetches that required a response of less +than or equal to 30k through all watched caches. + +@ web.allservers.requests.uncached.size.le100k replies of <= 100k sent by all caches +The number of HTTP GET remote fetches that required a response of less +than or equal to 100k through all watched caches. + +@ web.allservers.requests.uncached.size.le300k replies of <= 300k sent by all caches +The number of HTTP GET remote fetches that required a response of less +than or equal to 300k through all watched caches. + +@ web.allservers.requests.uncached.size.le1m replies of <= 1M sent by all caches +The number of HTTP GET remote fetches that required a response of less +than or equal to 1M through all watched caches. + +@ web.allservers.requests.uncached.size.le3m replies of <= 3M sent by all caches +The number of HTTP GET remote fetches that required a response of less +than or equal to 3M through all watched caches. + +@ web.allservers.requests.uncached.size.gt3m replies of > 3M sent by all caches +The number of HTTP GET remote fetches that required a response of greater +than 3M through all watched caches. + +@ web.allservers.requests.uncached.size.unknown replies of unknown size by all caches +The number of HTTP GET requests that required a response of unknown size +through all watched caches. + +@ web.allservers.bytes.cached.total bytes sent by caches as a result of cache hits for all cacheing servers +The total number of bytes sent to client due to HTTP GET/IMS requests +that resulted in "Not Modified"responses from the remote site or were +deemed cache hits via other mechanisms such as recency. + +@ web.allservers.bytes.cached.size.zero total bytes sent in 0k replies +The total number of bytes sent to client by cache hit replies of 0k +by all watched caches. This metric is always zero and is provided for +consistency only. + +@ web.allservers.bytes.cached.size.le3k total bytes sent in <= 3k replies +The total number of bytes sent to client by cache hit replies of less +than or equal to 3k in size by all watched caches. + +@ web.allservers.bytes.cached.size.le10k total bytes sent in <= 10k replies +The total number of bytes sent to client by cache hit replies of less +than or equal to 10k in size by all watched caches. + +@ web.allservers.bytes.cached.size.le30k total bytes sent in <= 30k replies +The total number of bytes sent to client by cache hit replies of less +than or equal to 30k in size by all watched caches. + +@ web.allservers.bytes.cached.size.le100k total bytes sent in <= 100k replies +The total number of bytes sent to client by cache hit replies of less +than or equal to 100k in size by all watched caches. + +@ web.allservers.bytes.cached.size.le300k total bytes sent in <= 300k replies +The total number of bytes sent to client by cache hit replies of less +than or equal to 300k in size by all watched caches. + +@ web.allservers.bytes.cached.size.le1m total bytes sent in <= 1M replies +The total number of bytes sent to client by cache hit replies of less +than or equal to 1M in size by all watched caches. + +@ web.allservers.bytes.cached.size.le3m total bytes sent in <= 3M replies +The total number of bytes sent to client by cache hit replies of less +than or equal to 3M in size by all watched caches. + +@ web.allservers.bytes.cached.size.gt3m total bytes sent > 3M replies +The total number of bytes sent to client by cache hit replies of greater +than 3M in size by all watched caches. + +@ web.allservers.bytes.uncached.total bytes sent by remote servers as a result of cache misses for all cacheing servers +The total number of bytes sent to client from the remote server. These +are either cache misses, or the remote file had been modified since +the cache entry was made. + +@ web.allservers.bytes.uncached.size.zero total bytes sent in 0k replies +The total number of bytes sent to client from the remote server of 0k +by all watched caches. This metric is always zero and is provided for +consistency only. + +@ web.allservers.bytes.uncached.size.le3k total bytes sent in <= 3k replies +The total number of bytes sent to client from the remote server of less +than or equal to 3k in size by all watched caches. + +@ web.allservers.bytes.uncached.size.le10k total bytes sent in <= 10k replies +The total number of bytes sent to client from the remote server of less +than or equal to 10k in size by all watched caches. + +@ web.allservers.bytes.uncached.size.le30k total bytes sent in <= 30k replies +The total number of bytes sent to client from the remote server of less +than or equal to 30k in size by all watched caches. + +@ web.allservers.bytes.uncached.size.le100k total bytes sent in <= 100k replies +The total number of bytes sent to client from the remote server of less +than or equal to 100k in size by all watched caches. + +@ web.allservers.bytes.uncached.size.le300k total bytes sent in <= 300k replies +The total number of bytes sent to client from the remote server of less +than or equal to 300k in size by all watched caches. + +@ web.allservers.bytes.uncached.size.le1m total bytes sent in <= 1M replies +The total number of bytes sent to client from the remote server of less +than or equal to 1M in size by all watched caches. + +@ web.allservers.bytes.uncached.size.le3m total bytes sent in <= 3M replies +The total number of bytes sent to client from the remote server of less +than or equal to 3M in size by all watched caches. + +@ web.allservers.bytes.uncached.size.gt3m total bytes sent > 3M replies +The total number of bytes sent to client from the remote server of +greater than 3M in size by all watched caches. + +@ web.perserver.watched flag set to 1 if monitoring this server +A flag which is set to 1 if this server is being monitored. +This metric may be altered using pmStore(1). + +@ web.perserver.numlogs number of readable log files for this server +The number of log files that are readable for the server. + +@ web.perserver.errors number of logged errors by this server +The number of errors (and other administrative messages) that have been +logged in the error log by this server. + +@ web.perserver.requests.total requests processed by this server +The number of HTTP requests processed by this server. + +@ web.perserver.bytes.total bytes sent by this server +The number of bytes this server has sent. + +@ web.perserver.requests.get GET requests handled by server +The number of HTTP GET requests that were processed by this server. + +@ web.perserver.bytes.get bytes sent in reply to GET requests +The number of bytes that have been sent by this server in reply to HTTP +GET requests. + +@ web.perserver.requests.head HEAD requests handled by server +The number of HTTP HEAD requests that were processed by this server. + +@ web.perserver.bytes.head bytes sent in reply to HEAD requests +The number of bytes that have been sent by this server in reply to HTTP +HEAD requests. + +@ web.perserver.requests.post POST requests handled by server +The number of HTTP POST requests that were processed by this server. + +@ web.perserver.bytes.post bytes sent in reply to POST requests +The number of bytes that have been sent by this server in reply to HTTP +POST requests. + +@ web.perserver.requests.other other requests handled by server +The number of HTTP requests, other than GET, HEAD and POST, that were +processed by this server. + +@ web.perserver.bytes.other bytes sent in reply to other requests +The number of bytes that have been sent by this server in reply to HTTP +requests other than GET, HEAD or POST. + +@ web.perserver.requests.size.zero requests requiring 0 bytes in reply +The number of HTTP requests that required a response of 0 bytes from this +server. + +@ web.perserver.bytes.size.zero total bytes sent in 0k replies. +The total number of bytes sent in replies of 0k by this server. This metric +is always 0 and is provided for consistency only. + +@ web.perserver.requests.size.le3k requests requiring <= 3k replies +The number of HTTP requests that required a response of less than or equal +to 3k from this server. + +@ web.perserver.bytes.size.le3k total bytes sent in <= 3k replies +The total number of bytes sent in replies of less than or equal to 3k in +size. + +@ web.perserver.requests.size.le10k requests requiring <= 10k replies +The number of HTTP requests that required a response of less than or equal +to 10k from this server. + +@ web.perserver.bytes.size.le10k total bytes sent in <= 10k replies +The total number of bytes sent in replies of less than or equal to 10k in +size. + +@ web.perserver.requests.size.le30k requests requiring <= 30k replies +The number of HTTP requests that required a response of less than or equal +to 30k from this server. + +@ web.perserver.bytes.size.le30k total bytes sent in <= 30k replies +The total number of bytes sent in replies of less than or equal to 30k in +size. + +@ web.perserver.requests.size.le100k requests requiring <= 100k replies +The number of HTTP requests that required a response of less than or equal +to 100k from this server. + +@ web.perserver.bytes.size.le100k total bytes sent in <= 100k replies +The total number of bytes sent in replies of less than or equal to 100k in +size. + +@ web.perserver.requests.size.le300k requests requiring <= 300k replies +The number of HTTP requests that required a response of less than or equal +to 300k from this server. + +@ web.perserver.bytes.size.le300k total bytes sent in <= 300k replies +The total number of bytes sent in replies of less than or equal to 300k in +size. + +@ web.perserver.requests.size.le1m requests requiring <= 1M replies +The number of HTTP requests that required a response of less than or equal +to 1M from this server. + +@ web.perserver.bytes.size.le1m total bytes sent in <= 1M replies +The total number of bytes sent in replies of less than or equal to 1M in +size. + +@ web.perserver.requests.size.le3m requests requiring <= 3M replies +The number of HTTP requests that required a response of less than or equal +to 3M from this server. + +@ web.perserver.bytes.size.le3m total bytes sent in <= 3M replies +The total number of bytes sent in replies of less than or equal to 3M in +size. + +@ web.perserver.requests.size.gt3m requests requiring > 3M replies +The number of HTTP requests that required a response of greater than +3M from this server. + +@ web.perserver.bytes.size.gt3m total bytes sent > 3M replies +The total number of bytes sent in replies of greater than 3M in size. + +@ web.perserver.requests.size.unknown requests of unknown size +The number of HTTP requests that required a response of unknown size from +this server. + +@ web.perserver.logidletime seconds since log last modified +The number of seconds since the access log for this server was modified. + +@ web.perserver.requests.client.total requests satisfied by client caches for this cache +The total number of HTTP GET/IMS requests that resulted in "Not Modified" +responses from cache (and remote if checked). These are client cache hits. + +@ web.perserver.requests.cached.total requests satisfied by server caches for this cache +The total number of HTTP GET/IMS requests that resulted in "Not Modified" +responses from the remote site or were deemed cache hits via other +mechanisms such as recency. These are server cache hits and result in +data transferred from cache to client. + +@ web.perserver.requests.cached.size.zero replies of 0 bytes sent by this cache +The number of HTTP GET cache hits that required a response of 0 bytes from +this cache. + +@ web.perserver.requests.cached.size.le3k replies of <= 3k sent by this cache +The number of HTTP GET cache hits that required a response of less than +or equal to 3k from this cache. + +@ web.perserver.requests.cached.size.le10k replies of <= 10k sent by this cache +The number of HTTP GET cache hits that required a response of less than +or equal to 10k from this cache. + +@ web.perserver.requests.cached.size.le30k replies of <= 30k sent by this cache +The number of HTTP GET cache hits that required a response of less than +or equal to 30k from this cache. + +@ web.perserver.requests.cached.size.le100k replies of <= 100k sent by this cache +The number of HTTP GET cache hits that required a response of less than +or equal to 100k from this cache. + +@ web.perserver.requests.cached.size.le300k replies of <= 300k sent by this cache +The number of HTTP GET cache hits that required a response of less than +or equal to 300k from this cache. + +@ web.perserver.requests.cached.size.le1m replies of <= 1M sent by this cache +The number of HTTP GET cache hits that required a response of less than +or equal to 1M from this cache. + +@ web.perserver.requests.cached.size.le3m replies of <= 3M sent by this cache +The number of HTTP GET cache hits that required a response of less than +or equal to 3M from this cache. + +@ web.perserver.requests.cached.size.gt3m replies of > 3M sent by this cache +The number of HTTP GET cache hits that required a response of greater than +3M from this cache. + +@ web.perserver.requests.cached.size.unknown replies of unknown size by this cache +The number of HTTP GET cache hits that required a response of unknown +size from this cache. + +@ web.perserver.requests.uncached.total requests satisfied by remote server for this cache +The total number of HTTP GET/IMS requests that resulted in a real data +transfer from the remote server. These are either cache misses, or the +remote file had been modified since the cache entry was made. + +@ web.perserver.requests.uncached.size.zero replies of 0 bytes sent by this cache +The number of HTTP GET remote fetches that required a response of 0 +bytes from this cache. + +@ web.perserver.requests.uncached.size.le3k replies of <= 3k sent by this cache +The number of HTTP GET remote fetches that required a response of less +than or equal to 3k through this cache. + +@ web.perserver.requests.uncached.size.le10k replies of <= 10k sent by this cache +The number of HTTP GET remote fetches that required a response of less +than or equal to 10k through this cache. + +@ web.perserver.requests.uncached.size.le30k replies of <= 30k sent by this cache +The number of HTTP GET remote fetches that required a response of less +than or equal to 30k through this cache. + +@ web.perserver.requests.uncached.size.le100k replies of <= 100k sent by this cache +The number of HTTP GET remote fetches that required a response of less +than or equal to 100k through this cache. + +@ web.perserver.requests.uncached.size.le300k replies of <= 300k sent by this cache +The number of HTTP GET remote fetches that required a response of less +than or equal to 300k through this cache. + +@ web.perserver.requests.uncached.size.le1m replies of <= 1M sent by this cache +The number of HTTP GET remote fetches that required a response of less +than or equal to 1M through this cache. + +@ web.perserver.requests.uncached.size.le3m replies of <= 3M sent by this cache +The number of HTTP GET remote fetches that required a response of less +than or equal to 3M through this cache. + +@ web.perserver.requests.uncached.size.gt3m replies of > 3M sent by this cache +The number of HTTP GET remote fetches that required a response of greater +than 3M through this cache. + +@ web.perserver.requests.uncached.size.unknown replies of unknown size by this cache +The number of HTTP GET requests that required a response of unknown size +through this cache. + +@ web.perserver.bytes.cached.total bytes sent by caches as a result of cache hits for this cache +The total number of bytes sent to client due to HTTP GET/IMS requests +that resulted in "Not Modified"responses from the remote site or were +deemed cache hits via other mechanisms such as recency. + +@ web.perserver.bytes.cached.size.zero total bytes sent in 0k replies +The total number of bytes sent to client by cache hit replies of 0k by +this cache. This metric is always zero and is provided for consistency +only. + +@ web.perserver.bytes.cached.size.le3k total bytes sent in <= 3k replies +The total number of bytes sent to client by cache hit replies of less +than or equal to 3k in size by this cache. + +@ web.perserver.bytes.cached.size.le10k total bytes sent in <= 10k replies +The total number of bytes sent to client by cache hit replies of less +than or equal to 10k in size by this cache. + +@ web.perserver.bytes.cached.size.le30k total bytes sent in <= 30k replies +The total number of bytes sent to client by cache hit replies of less +than or equal to 30k in size by this cache. + +@ web.perserver.bytes.cached.size.le100k total bytes sent in <= 100k replies +The total number of bytes sent to client by cache hit replies of less +than or equal to 100k in size by this cache. + +@ web.perserver.bytes.cached.size.le300k total bytes sent in <= 300k replies +The total number of bytes sent to client by cache hit replies of less +than or equal to 300k in size by this cache. + +@ web.perserver.bytes.cached.size.le1m total bytes sent in <= 1M replies +The total number of bytes sent to client by cache hit replies of less +than or equal to 1M in size by this cache. + +@ web.perserver.bytes.cached.size.le3m total bytes sent in <= 3M replies +The total number of bytes sent to client by cache hit replies of less +than or equal to 3M in size by this cache. + +@ web.perserver.bytes.cached.size.gt3m total bytes sent > 3M replies +The total number of bytes sent to client by cache hit replies of greater +than 3M in size by this cache. + +@ web.perserver.bytes.uncached.total bytes sent by remote servers as a result of cache misses for this cache +The total number of bytes sent to client from the remote server. These +are either cache misses, or the remote file had been modified since +the cache entry was made. + +@ web.perserver.bytes.uncached.size.zero total bytes sent in 0k replies +The total number of bytes sent to client from the remote server of 0k by +this cache. This metric is always zero and is provided for consistency +only. + +@ web.perserver.bytes.uncached.size.le3k total bytes sent in <= 3k replies +The total number of bytes sent to client from the remote server of less +than or equal to 3k in size by this cache. + +@ web.perserver.bytes.uncached.size.le10k total bytes sent in <= 10k replies +The total number of bytes sent to client from the remote server of less +than or equal to 10k in size by this cache. + +@ web.perserver.bytes.uncached.size.le30k total bytes sent in <= 30k replies +The total number of bytes sent to client from the remote server of less +than or equal to 30k in size by this cache. + +@ web.perserver.bytes.uncached.size.le100k total bytes sent in <= 100k replies +The total number of bytes sent to client from the remote server of less +than or equal to 100k in size by this cache. + +@ web.perserver.bytes.uncached.size.le300k total bytes sent in <= 300k replies +The total number of bytes sent to client from the remote server of less +than or equal to 300k in size by this cache. + +@ web.perserver.bytes.uncached.size.le1m total bytes sent in <= 1M replies +The total number of bytes sent to client from the remote server of less +than or equal to 1M in size by this cache. + +@ web.perserver.bytes.uncached.size.le3m total bytes sent in <= 3M replies +The total number of bytes sent to client from the remote server of less +than or equal to 3M in size by this cache. + +@ web.perserver.bytes.uncached.size.gt3m total bytes sent > 3M replies +The total number of bytes sent to client from the remote server of +greater than 3M in size by this cache. + diff --git a/src/pmdas/weblog/pmda.c b/src/pmdas/weblog/pmda.c new file mode 100644 index 0000000..6c1967f --- /dev/null +++ b/src/pmdas/weblog/pmda.c @@ -0,0 +1,1205 @@ +/* + * Web PMDA, based on generic driver for a daemon-based PMDA + * + * Copyright (c) 2012 Red Hat. + * Copyright (c) 2000-2003 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License 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. + */ + +#include "weblog.h" +#include "domain.h" +#if defined(HAVE_REGEX_H) +#include <regex.h> +#endif +#if defined(HAVE_SYS_WAIT_H) +#include <sys/wait.h> +#endif +#if defined(HAVE_SCHED_H) +#include <sched.h> +#endif + +#ifdef IS_SOLARIS +#define CLONE_VM 0x00000100 +#elif !defined(CLONE_VM) +#define CLONE_VM 0x0 +#endif + +#ifndef HAVE_SPROC +int sproc (void (*entry) (void *), int flags, void *arg); +#endif + +/* path to the configuration file */ +static char *configFileName = (char*)0; + +/* line number of configuration file */ +static int line = 0; + +/* number of errors in configuration file */ +static int err = 0; + +/* configured for num of servers */ +__uint32_t wl_numServers = 0; + +/* number of active servers */ +__uint32_t wl_numActive = 0; + +/* check logs every 15 seconds by default */ +__uint32_t wl_refreshDelay = 15; + +/* re-open logs if unchanged in this number of seconds */ +__uint32_t wl_chkDelay = 20; + +/* max servers per sproc */ +__uint32_t wl_sprocThresh = 80; + +/* number of sprocs spawned */ +__uint32_t wl_numSprocs = 0; + +/* number of regex parsed */ +__uint32_t wl_numRegex = 0; + +/* list of web servers */ +WebServer *wl_servers = (WebServer*)0; + +/* list of regular expressions */ +WebRegex *wl_regexTable = (WebRegex*)0; + +/* instance table of web servers */ +pmdaInstid *wl_serverInst = (pmdaInstid*)0; + +/* list of sprocs spawned from the main process */ +WebSproc *wl_sproc; + +/* default name for log file */ +char *wl_logFile = "weblog.log"; + +/* default path to help file */ +char wl_helpFile[MAXPATHLEN]; + +/* default user name for PMDA */ +char *wl_username; + +/* + * Usage Information + */ + +void +usage(void) +{ + fprintf(stderr, + "Usage: %s [options] configfile\n\ +\n\ +Options\n\ + -C check configuration and exit\n\ + -d domain PMDA domain number\n\ + -h helpfile get help text from helpfile rather than default path\n\ + -i port expect PMCD to connect on given inet port (number or name)\n\ + -l logfile redirect diagnostics and trace output (default weblog.log)\n\ + -n idlesec number of seconds of weblog inactivity before checking for\n\ + log rotation\n\ + -p expect PMCD to supply stdin/stdout (pipe)\n\ + -S num number of web servers per sproc\n\ + -t delay maximum number of seconds between reading weblog files\n\ + -u socket expect PMCD to connect on given unix domain socket\n\ + -U username user account to run under (default \"pcp\")\n\ + -6 port expect PMCD to connect on given ipv6 port (number or name)\n\ +\n\ +If none of the -i, -p or -u options are given, the configuration file is\n\ +checked and then %s terminates.\n", pmProgname, pmProgname); + exit(1); +} + +void +logmessage(int priority, const char *format, ...) +{ + va_list arglist; + char buffer[2048]; + char *level; + char *p; + time_t now; + + buffer[0] = '\0'; + time(&now); + + switch (priority) { + case LOG_EMERG : + level = "Emergency"; + break; + case LOG_ALERT : + level = "Alert"; + break; + case LOG_CRIT : + level = "Critical"; + break; + case LOG_ERR : + level = "Error"; + break; + case LOG_WARNING : + level = "Warning"; + break; + case LOG_NOTICE : + level = "Notice"; + break; + case LOG_INFO : + level = "Info"; + break; + case LOG_DEBUG : + level = "Debug"; + break; + default: + level = "???"; + break; + } + + va_start (arglist, format); + vsnprintf (buffer, sizeof(buffer), format, arglist); + for (p = buffer; *p; p++); + if (*(--p) == '\n') *p = '\0'; + fprintf (stderr, "[%.19s] %s(%" FMT_PID ") %s: %s\n", ctime(&now), pmProgname, getpid(), level, buffer) ; + va_end (arglist) ; +} + +/* + * Errors message during parsing of config file + */ + +static void +yyerror(char *s) +{ + fprintf(stderr, "[%s:%d] Error: %s\n", configFileName, line, s); + err++; +} + +/* + * Warning message during parsing of config file + */ + +static void +yywarn(char *s) +{ + fprintf(stderr, "[%s:%d] Warning: %s\n", configFileName, line, s); +} + +/* + * skip remaining characters on this line + */ + +static void +skip_to_eol(FILE *f) +{ + int c; + + while ((c = fgetc(f)) != EOF) { + if (c == '\n') + return; + } + return; +} + +/* + * Are we at the end of the line (sucks up spaces and tabs which may preceed + * EOL) + */ + +static void +check_to_eol(FILE *f) +{ + int c; + int i = 0; + + while ((c = fgetc(f)) != EOF) { + if (c == '\n') + break; + if (c == ' ' || c == '\t') + continue; + i++; + } + if (i) + yywarn("additional words in line, ignored"); + + return; +} + +/* + * Get a word. A word if any text until a whitespace + */ + +static int +getword(FILE *f, char *buf, int len) +{ + int c; + char *bend = &buf[len-1]; + + while ((c = fgetc(f)) != EOF) { + if (c == ' ' || c == '\t') + continue; + ungetc(c, f); + break; + } + + while ((c = fgetc(f)) != EOF) { + if (c == ' ' || c == '\t') + break; + if (c == '\n') { + ungetc(c, f); + break; + } + if (buf < bend) { + *buf++ = c; + continue; + } + else { + yyerror("word too long, remainder of line ignored"); + return -1; + } + } + *buf = '\0'; + + return c == EOF ? 0 : 1; +} + +/* + * Get the next line from buffer + */ + +static int +get_to_eol(FILE *f, char *buf, int len) +{ + int c; + char *bend = &buf[len-1]; + + while ((c = fgetc(f)) != EOF) { + if (c == ' ' || c == '\t') + continue; + ungetc(c, f); + break; + } + + while ((c = fgetc(f)) != EOF) { + if (c == '\n') + break; + if (buf < bend) { + *buf++ = c; + continue; + } + else { + yyerror("list of words too long, remainder of line ignored"); + return -1; + } + } + *buf = '\0'; + return c == EOF ? 0 : 1; +} + +/* + * Replacement for pmdaMainLoop + * Has a select loop on pipe from PMCD, reads in PDUs and acts on them + * appropriately. + */ + +static void +receivePDUs(pmdaInterface *dispatch) +{ + int nfds = 0; + time_t interval = 0; + int sts = 0; + struct timeval timeout; + fd_set rfds; + + + FD_ZERO(&rfds); + nfds = fileno(stdin)+1; + + for (;;) { + + FD_SET(fileno(stdin), &rfds); + __pmtimevalNow(&timeout); + timeout.tv_usec = 0; + interval = (time_t)wl_refreshDelay - (timeout.tv_sec % (time_t)wl_refreshDelay); + timeout.tv_sec = interval; + +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL1) + logmessage(LOG_DEBUG, "Select set for %d seconds\n", + interval); +#endif + + sts = select(nfds, &rfds, (fd_set*)0, (fd_set*)0, &timeout); + if (sts < 0) { + logmessage(LOG_ERR, "Error on fetch select: %s", netstrerror()); + exit(1); + } + + if (sts == 0) { + +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL1) + logmessage(LOG_DEBUG, "Select timed out\n"); +#endif + + refreshAll(); + continue; + } + + if (__pmdaMainPDU(dispatch) < 0){ + exit(1); + } + + if (interval == 0) { + refreshAll(); + } + + } +} + +/* + * Catch an SPROC dying, report what we know, and exit + * -- when main exits, other sprocs will get SIGHUP and exit quietly + */ +static void +onchld(int dummy) +{ + int done; + int waitStatus; + int sprocNum; + + while ((done = waitpid(-1, &waitStatus, WNOHANG)) > 0) { + for (sprocNum = 1; + wl_sproc[sprocNum].pid != done && sprocNum <= wl_numSprocs; + sprocNum++); + + if (sprocNum > wl_numSprocs) + { + logmessage(LOG_INFO, + "Unexpected child process (pid=%d) died!\n", + done); + continue; + } + + if (WIFEXITED(waitStatus)) { + + if (WEXITSTATUS(waitStatus) == 0) + logmessage(LOG_INFO, + "Sproc %d (pid=%d) exited normally\n", + sprocNum, done); + else + logmessage(LOG_INFO, + "Sproc %d (pid=%d) exited with status = %d\n", + sprocNum, done, WEXITSTATUS(waitStatus)); + } + else if (WIFSIGNALED(waitStatus)) { + +#ifdef WCOREDUMP + if (WCOREDUMP(waitStatus)) + logmessage(LOG_INFO, + "Sproc %d (pid=%d) received signal = %d and dumped core\n", + sprocNum, done, WTERMSIG(waitStatus)); +#endif + logmessage(LOG_INFO, + "Sproc %d (pid=%d) received signal = %d\n", + sprocNum, done, WTERMSIG(waitStatus)); + } + else { + logmessage(LOG_INFO, + "Sproc %d (pid=%d) died, reason unknown\n", + sprocNum, done); + } + + logmessage(LOG_INFO, + "Sproc %d managed servers %d to %d\n", + sprocNum, + wl_sproc[sprocNum].firstServer, + wl_sproc[sprocNum].lastServer); + } + + logmessage(LOG_INFO, "Main process exiting\n"); + exit(0); +} + +/* + * Parse command line args and the configuration file. Also sets up and fires + * off the required sprocs + */ + +int +main(int argc, char **argv) +{ + WebServer *server = (WebServer *)0; + WebSproc *proc = (WebSproc *)0; + + char *endnum = (char*)0; + char buf1[FILENAME_MAX]; + char buf2[FILENAME_MAX]; + char emess[120]; + char *pstart, *pend; + char argsDone, argFound; + char *err_msg; + + int i = 0; + int argCount = 0; + int checkOnly = 0; + int sts = 0; + int sep = __pmPathSeparator(); + int n = 0; + int serverTableSize = 0; + int regexTableSize = 0; + + FILE *configFile = (FILE*)0; + FILE *tmpFp = (FILE*)0; + + pmdaInterface desc; + struct timeval delta; + + struct { + int *argPos; + char *argString; + } regexargs[2]; + +#ifdef PCP_DEBUG + struct timeval start; + struct timeval end; + double startTime; +#endif + + __pmSetProgname(argv[0]); + __pmGetUsername(&wl_username); + +#ifdef PCP_DEBUG + __pmtimevalNow(&start); +#endif + + wl_isDSO = 0; + + snprintf(wl_helpFile, sizeof(wl_helpFile), "%s%c" "weblog" "%c" "help", + pmGetConfig("PCP_PMDAS_DIR"), sep, sep); + pmdaDaemon(&desc, PMDA_INTERFACE_2, pmProgname, WEBSERVER, + wl_logFile, wl_helpFile); + + while ((n = pmdaGetOpt(argc, argv, "CD:d:h:i:l:n:pS:t:u:U:6:?", + &desc, &err)) != EOF) { + switch (n) { + + case 'C': + checkOnly = 1; + break; + + case 'S': + wl_sprocThresh = (int)strtol(optarg, &endnum, 10); + if (*endnum != '\0') { + fprintf(stderr, "%s: -S requires numeric argument\n", + pmProgname); + err++; + } + break; + + case 'n': + if (pmParseInterval(optarg, &delta, &err_msg) < 0) { + (void)fprintf(stderr, + "%s: -n requires a time interval: %s\n", + err_msg, pmProgname); + free(err_msg); + err++; + } + else { + wl_chkDelay = delta.tv_sec; + } + break; + + case 't': + if (pmParseInterval(optarg, &delta, &err_msg) < 0) { + (void)fprintf(stderr, + "%s: -t requires a time interval: %s\n", + err_msg, pmProgname); + free(err_msg); + err++; + } + else { + wl_refreshDelay = delta.tv_sec; + } + break; + + case 'U': + wl_username = optarg; + break; + + default: + fprintf(stderr, "%s: Unknown option \"-%c\"", pmProgname, (char)n); + err++; + break; + } + } + + if (err || optind != argc-1) { + usage(); + } + + line = 0; + configFileName = argv[optind]; + configFile = fopen(configFileName, "r"); + + if (configFile == (FILE*)0) { + fprintf(stderr, "Unable to open config file %s\n", configFileName); + usage(); + } + + if (checkOnly == 0) { + /* + * if doing more than just parsing, force errors from here + * on into the logfile + */ + pmdaOpenLog(&desc); + __pmSetProcessIdentity(wl_username); + } + + /* + * Parse the configuration file + */ + + /* These settings should be reflected below */ + regexargs[0].argString = strdup("method"); + regexargs[1].argString = strdup("size"); + + while(!feof(configFile)) { + + sts = getword(configFile, buf1, sizeof(buf1)); + + if (sts == 0) { + /* End of File */ + break; + } + + line++; + + if (sts < 0) { + /* error, reported in getword() */ + skip_to_eol(configFile); + continue; + } + + if (buf1[0] == '\0' || buf1[0] == '#') { + /* comment, or nothing in the line, next line please */ + skip_to_eol(configFile); + continue; + } + + if (strcasecmp(buf1, "regex_posix") == 0) { + /* + * Parse a regex specification + */ + + if (wl_numRegex == regexTableSize) { + regexTableSize += 2; + wl_regexTable = (WebRegex*)realloc(wl_regexTable, + regexTableSize * sizeof(WebRegex)); + if (wl_regexTable == (WebRegex*)0) { + __pmNoMem("main.wl_regexInst", + (wl_numRegex + 1) * sizeof(WebRegex), + PM_FATAL_ERR); + } + } + + sts = getword(configFile, buf1, sizeof(buf1)); + + if (sts <= 0 || buf1[0] == '\0') { + if (sts >= 0) + yyerror("unable to extract regex name"); + skip_to_eol(configFile); + continue; + } + + wl_regexTable[wl_numRegex].name = strdup(buf1); + + if (wl_numRegex) { + for (n = 0; n < wl_numRegex; n++) { + if (strcmp(wl_regexTable[n].name, + wl_regexTable[wl_numRegex].name) == 0) { + + snprintf(emess, sizeof(emess), "duplicate regex name (%s)", + wl_regexTable[wl_numRegex].name); + yyerror(emess); + break; + } + } + if (n < wl_numRegex) { + skip_to_eol(configFile); + continue; + } + } + + sts = getword(configFile, buf1, sizeof(buf1)); + + if (sts <= 0 || buf1[0] == '\0') { + if (sts >= 0) + yyerror("unable to extract regex match parameters"); + skip_to_eol(configFile); + continue; + } + + regexargs[0].argPos = &(wl_regexTable[wl_numRegex].methodPos); + regexargs[1].argPos = &(wl_regexTable[wl_numRegex].sizePos); + wl_regexTable[wl_numRegex].methodPos = 0; + wl_regexTable[wl_numRegex].sizePos = 0; + wl_regexTable[wl_numRegex].sizePos = 0; + wl_regexTable[wl_numRegex].s_statusPos = 0; + + pstart = buf1; + argCount = 0; + do { + argFound = 0; + argsDone = 1; + argCount++; + for(pend = pstart; *pend; pend++) { + if(*pend == ',') { + *pend = '\0'; + argsDone = 0; + break; + } + } + for(i = 0; i < sizeof(regexargs) / sizeof(regexargs[0]); i++) { + if(strcmp(pstart, regexargs[i].argString) == 0) { + *regexargs[i].argPos = argCount; + argFound = 1; + break; + } + } + if(!argFound) { + /* not the old method,size style */ + switch(pstart[0]) { + case '1': + wl_regexTable[wl_numRegex].methodPos = argCount; + argFound = 1; + break; + case '2': + wl_regexTable[wl_numRegex].sizePos = argCount; + argFound = 1; + break; + case '3': + wl_regexTable[wl_numRegex].c_statusPos = argCount; + argFound = 1; + break; + case '4': + wl_regexTable[wl_numRegex].s_statusPos = argCount; + argFound = 1; + break; + case '-': + wl_regexTable[wl_numRegex].methodPos = argCount++; + wl_regexTable[wl_numRegex].sizePos = argCount; + argFound = 1; + argsDone = 1; + break; + default: + break; + } + } + pstart = pend + 1; + } while(argsDone == 0 && argFound != 0); + + if(argFound == 0) { + yyerror("invalid keyword in regex match parameters"); + skip_to_eol(configFile); + continue; + } + + sts = get_to_eol(configFile, buf1, sizeof(buf1)); + + if (sts <= 0 || buf1[0] == '\0') { + if (sts >= 0) + yyerror("unable to extract regex"); + else + skip_to_eol(configFile); + continue; + } + + wl_regexTable[wl_numRegex].regex = malloc(sizeof(*wl_regexTable[wl_numRegex].regex)); + if(wl_regexTable[wl_numRegex].regex == NULL) { + __pmNoMem("main.wl_regex", + sizeof(*wl_regexTable[wl_numRegex].regex), + PM_FATAL_ERR); + } + + if (regcomp(wl_regexTable[wl_numRegex].regex, buf1, REG_EXTENDED) != 0) { + yyerror("unable to compile regex"); + continue; + } + +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) + logmessage(LOG_DEBUG, "%d regex %s: %s\n", + wl_numRegex, wl_regexTable[wl_numRegex].name, buf1); +#endif + + wl_regexTable[wl_numRegex].posix_regexp = 1; + wl_numRegex++; + } +#ifdef NON_POSIX_REGEX + else if (strcasecmp(buf1, "regex") == 0) { + /* + * Parse a regex specification + */ + + if (wl_numRegex == regexTableSize) { + regexTableSize += 2; + wl_regexTable = (WebRegex*)realloc(wl_regexTable, + regexTableSize * sizeof(WebRegex)); + if (wl_regexTable == (WebRegex*)0) { + __pmNoMem("main.wl_regexInst", + (wl_numRegex + 1) * sizeof(WebRegex), + PM_FATAL_ERR); + } + } + + sts = getword(configFile, buf1, sizeof(buf1)); + + if (sts <= 0 || buf1[0] == '\0') { + if (sts >= 0) + yyerror("unable to extract regex name"); + skip_to_eol(configFile); + continue; + } + + wl_regexTable[wl_numRegex].name = strdup(buf1); + + if (wl_numRegex) { + for (n = 0; n < wl_numRegex; n++) { + if (strcmp(wl_regexTable[n].name, + wl_regexTable[wl_numRegex].name) == 0) { + + snprintf(emess, sizeof(emess), "duplicate regex name (%s)", + wl_regexTable[wl_numRegex].name); + yyerror(emess); + break; + } + } + if (n < wl_numRegex) { + skip_to_eol(configFile); + continue; + } + } + + sts = get_to_eol(configFile, buf1, sizeof(buf1)); + + if (sts <= 0 || buf1[0] == '\0') { + if (sts >= 0) + yyerror("unable to extract regex"); + else + skip_to_eol(configFile); + continue; + } + + if(strstr(buf1, "$2") != NULL && strstr(buf1, "$3") != NULL ) { + /* + * extended caching server format + * + * although these aren't used in the non-regex code, they + * are a good enough placeholder until server->counts.extendedp + * is set below + */ + wl_regexTable[wl_numRegex].c_statusPos = 1; + wl_regexTable[wl_numRegex].s_statusPos = 1; + + } + wl_regexTable[wl_numRegex].np_regex = regcmp(buf1, (char*)0); + + if (wl_regexTable[wl_numRegex].np_regex == (char*)0) { + yyerror("unable to compile regex"); + continue; + } + +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) + logmessage(LOG_DEBUG, "%d NON POSIX regex %s: %s\n", + wl_numRegex, wl_regexTable[wl_numRegex].name, buf1); +#endif + + wl_regexTable[wl_numRegex].posix_regexp = 0; + wl_numRegex++; + } +#endif + else if (strcasecmp(buf1, "server") == 0) { + /* + * Parse a server specification + */ + + if (wl_numServers == serverTableSize) { + serverTableSize += 4; + wl_serverInst = (pmdaInstid*)realloc(wl_serverInst, + serverTableSize * sizeof(pmdaInstid)); + if (wl_serverInst == (pmdaInstid*)0) { + __pmNoMem("main.wl_serverInst", + (wl_numServers + 1) * sizeof(pmdaInstid), + PM_FATAL_ERR); + } + + wl_servers = (WebServer*)realloc(wl_servers, + serverTableSize * sizeof(WebServer)); + if (wl_servers == (WebServer*)0) { + __pmNoMem("main.wl_servers", + (wl_numServers + 1) * sizeof(WebServer), + PM_FATAL_ERR); + } + } + + /* Get server name */ + + sts = getword(configFile, buf1, sizeof(buf1)); + + if (sts <= 0 || buf1[0] == '\0') { + if (sts >= 0) + yyerror("unable to extract server name"); + skip_to_eol(configFile); + continue; + } + + if (wl_numServers) { + for (n = 0; n < wl_numServers; n++) { + if (strcmp(buf1, wl_serverInst[n].i_name) == 0) { + snprintf(emess, sizeof(emess), "duplicate server name (%s)", buf1); + yyerror(emess); + break; + } + } + if (n < wl_numServers) { + skip_to_eol(configFile); + continue; + } + } + + wl_serverInst[wl_numServers].i_name = strdup(buf1); + wl_serverInst[wl_numServers].i_inst = wl_numServers; + + server = &(wl_servers[wl_numServers]); + memset(server, 0, sizeof(*server)); + server->access.filePtr = -1; + server->error.filePtr = -1; + + /* Get server active flag */ + + sts = getword(configFile, buf1, sizeof(buf1)); + + if (sts <= 0 || buf1[0] == '\0') { + if (sts >= 0) + yyerror("unable to extract active flag"); + skip_to_eol(configFile); + continue; + } + + if (strcasecmp(buf1, "on") == 0) { + server->counts.active = 1; + } + else if (strcasecmp(buf1, "off") == 0) { + server->counts.active = 0; + } + else { + yyerror("illegal active flag"); + skip_to_eol(configFile); + continue; + } + + /* Get access log regex and file name */ + + + sts = getword(configFile, buf1, sizeof(buf1)); + + if (sts <= 0 || buf1[0] == '\0') { + if (sts >= 0) + yyerror("unable to extract access log regex"); + skip_to_eol(configFile); + continue; + } + + sts = getword(configFile, buf2, sizeof(buf2)); + + if (sts <= 0 || buf2[0] == '\0') { + if (sts >= 0) + yyerror("unable to extract access log name"); + skip_to_eol(configFile); + continue; + } + + for (n = 0; n < wl_numRegex; n++) + if (strcmp(buf1, wl_regexTable[n].name) == 0) + break; + + if (n == wl_numRegex) { + snprintf(emess, sizeof(emess), "access log regex \"%s\" not defined", buf1); + yyerror(emess); + skip_to_eol(configFile); + continue; + } else if(wl_regexTable[n].c_statusPos > 0 && + wl_regexTable[n].s_statusPos > 0) { + /* common extended format or one that uses the same codes */ + server->counts.extendedp = 1; + if(strcmp(wl_regexTable[n].name, "SQUID") == 0) { + /* + * default squid format - uses text codes not numerics + * so it *has* to be a special case + */ + server->counts.extendedp = 2; + } + } + + server->access.format = n; + server->access.fileName = strdup(buf2); + + if (server->counts.active) { + tmpFp = fopen(server->access.fileName, "r"); + if (tmpFp == (FILE*)0) { + snprintf(emess, sizeof(emess), "cannot open access log \"%s\"", buf2); + yywarn(emess); + server->access.filePtr = -1; + } + else + fclose(tmpFp); + } + + /* Get error log regex and file name */ + + sts = getword(configFile, buf1, sizeof(buf1)); + + if (sts <= 0 || buf1[0] == '\0') { + if (sts >= 0) + yyerror("unable to extract error log regex"); + skip_to_eol(configFile); + continue; + } + + sts = getword(configFile, buf2, sizeof(buf2)); + + if (sts <= 0 || buf2[0] == '\0') { + if (sts >= 0) + yyerror("unable to extract error log name"); + skip_to_eol(configFile); + continue; + } + + for (n = 0; n < wl_numRegex; n++) + if (strcmp(buf1, wl_regexTable[n].name) == 0) + break; + + if (n == wl_numRegex) { + snprintf(emess, sizeof(emess), "error log regex \"%s\" not defined", buf1); + yyerror(emess); + skip_to_eol(configFile); + continue; + } + + server->error.format = n; + server->error.fileName = strdup(buf2); + + if (server->counts.active) { + tmpFp = fopen(server->error.fileName, "r"); + if (tmpFp == (FILE*)0) { + snprintf(emess, sizeof(emess), "cannot open error log \"%s\"", buf2); + yywarn(emess); + server->error.filePtr = -1; + } + else + fclose(tmpFp); + } + + check_to_eol(configFile); + +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) { + logmessage(LOG_DEBUG, "%d Server %s, %d, %d, %s, %d, %s\n", + wl_numServers, + wl_serverInst[wl_numServers].i_name, + server->counts.active, + server->access.format, + server->access.fileName, + server->error.format, + server->error.fileName); + } +#endif + + if (server->counts.active) + wl_numActive++; + + wl_numServers++; + } + else { + snprintf(emess, sizeof(emess), "illegal keyword \"%s\"", buf1); + yyerror(emess); + skip_to_eol(configFile); + continue; + } + } + + if (wl_numServers == 0) { + yyerror("no servers were specified in the configuration file!"); + } + + fclose(configFile); + + if (checkOnly || err) { + /* errors, or parse only, no PMCD communication option */ + exit(err); + } + + wl_indomTable[0].it_numinst = wl_numServers; + wl_indomTable[0].it_set = wl_serverInst; + + web_init(&desc); + pmdaConnect(&desc); + + /* catch any sprocs dying */ + + signal(SIGCHLD, onchld); + + /* fire off all the sprocs that we need */ + + wl_numSprocs = (wl_numServers-1) / wl_sprocThresh; + wl_sproc = (WebSproc*)malloc((wl_numSprocs+1) * sizeof(WebSproc)); + if (wl_sproc == NULL) { + logmessage(LOG_ERR, + "wl_numServers = %d, wl_sprocThresh = %d", + wl_numServers, + wl_sprocThresh); + __pmNoMem("main.wl_sproc", + (wl_numSprocs+1) * sizeof(WebSproc), + PM_FATAL_ERR); + } + + + for (n = 0; n <= wl_numSprocs; n++) + { + proc = &wl_sproc[n]; + proc->pid = -1; + proc->methodStr = (char *)0; + proc->sizeStr = (char *)0; + proc->c_statusStr = (char *)0; + proc->s_statusStr = (char *)0; + proc->strLength = 0; + } + + if (wl_numSprocs) { + + for (n=1; n<=wl_numSprocs; n++) { + proc = &wl_sproc[n]; + + sts = pipe1(proc->inFD); + if (sts) { + logmessage(LOG_ERR, + "Cannot allocate fileDes 1 for sproc[%d]", + n); + exit(1); + } + +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL2) + logmessage(LOG_DEBUG, + "Creating in pipe (in=%d, out=%d) for sproc %d\n", + proc->inFD[0], + proc->inFD[1], + n); +#endif + + sts = pipe1(proc->outFD); + if (sts) { + logmessage(LOG_ERR, + "Cannot allocate fileDes 2 for sproc[%d]", + n); + exit(1); + } + +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL2) + logmessage(LOG_DEBUG, + "Creating out pipe (in=%d, out=%d) for sproc %d\n", + proc->outFD[0], + proc->outFD[1], + n); +#endif + + proc->firstServer = (n)*wl_sprocThresh; + if (n != wl_numSprocs) + proc->lastServer = proc->firstServer + + wl_sprocThresh - 1; + else + proc->lastServer = wl_numServers - 1; + + logmessage(LOG_INFO, + "Creating sproc [%d] for servers %d to %d\n", + n, proc->firstServer, proc->lastServer); + + proc->id = n; + +#ifndef HAVE_SPROC + proc->pid = sproc(sprocMain, CLONE_VM, (void*)(&proc->id)); +#else + proc->pid = sproc(sprocMain, PR_SADDR, (void*)(&proc->id)); +#endif + + if (proc->pid < 0) { + logmessage(LOG_ERR, "main: error creating sproc %d: %s\n", + n, osstrerror()); + exit(1); + } + +#ifdef PCP_DEBUG + if(pmDebug & DBG_TRACE_APPL0) { + logmessage(LOG_INFO, + "main: created sproc %d: pid %" FMT_PID "\n", + n, + proc->pid); + } +#endif + + /* close off unwanted pipes */ + + if(close(proc->inFD[0]) < 0) { + logmessage(LOG_WARNING, + "main: pipe close(fd=%d) failed: %s\n", + proc->inFD[0], osstrerror()); + } + if(close(proc->outFD[1]) < 0) { + logmessage(LOG_WARNING, + "main: pipe close(fd=%d) failed: %s\n", + proc->outFD[1], osstrerror()); + } + } + } + + wl_sproc[0].firstServer = 0; + wl_sproc[0].lastServer = (wl_numServers <= wl_sprocThresh) ? + wl_numServers - 1 : wl_sprocThresh - 1; + +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) + logmessage(LOG_DEBUG, + "Main process will monitor servers 0 to %d\n", + wl_sproc[0].lastServer); +#endif + + for (n=0; n <= wl_sproc[0].lastServer; n++) { + if (wl_servers[n].counts.active) { + openLogFile(&(wl_servers[n].access)); + openLogFile(&(wl_servers[n].error)); + } + } + +#ifdef PCP_DEBUG + __pmtimevalNow(&end); + startTime = (end.tv_sec - start.tv_sec) + + ((end.tv_usec - start.tv_usec) / 1000000.0); + if (pmDebug & DBG_TRACE_APPL0) + logmessage(LOG_DEBUG, "Agent started in %f seconds", startTime); +#endif + + receivePDUs(&desc); + + logmessage(LOG_INFO, "Connection to PMCD closed by PMCD\n"); + logmessage(LOG_INFO, "Last fetch took %d msec\n", wl_catchupTime); + logmessage(LOG_INFO, "Exiting...\n"); + + return 0; +} diff --git a/src/pmdas/weblog/pmns b/src/pmdas/weblog/pmns new file mode 100644 index 0000000..fa2038d --- /dev/null +++ b/src/pmdas/weblog/pmns @@ -0,0 +1,306 @@ +/* + * Web Performance Metric Domain (PMD) Identifiers + * + * Copyright (c) 2000-2004 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Cluster numbers are used by agent to indicate: + * + * 0 - Does not require any servers to be updated + * 1 - Requires all servers to be updated + * 2 - Requires only this server to be updated + * + * Changes to this file must also be carried into the help file and weblog.c + * (especially the meta table, web_fetch and web_store). + * + */ + +web { + config + allservers + perserver +} + +web.config { + numservers WEBSERVER:0:0 /* in the PMDA configuration file */ + catchup WEBSERVER:0:1 /* maximum catch-up period (secs) */ + catchuptime WEBSERVER:0:2 /* time spent in catchup (msecs) */ + check WEBSERVER:0:3 /* inactivity period (secs), before + trying to re-open files */ +} + +web.allservers { + numwatched WEBSERVER:0:4 /* number to be watched */ + numalive WEBSERVER:0:5 /* number of the watched servers + that are apparently alive */ + requests + bytes + errors WEBSERVER:1:6 +} + +web.allservers.requests { + total WEBSERVER:1:7 + get WEBSERVER:1:8 + head WEBSERVER:1:9 + post WEBSERVER:1:10 + other WEBSERVER:1:11 + size + client + cached + uncached +} + +web.allservers.bytes { + total WEBSERVER:1:12 + get WEBSERVER:1:13 + head WEBSERVER:1:14 + post WEBSERVER:1:15 + other WEBSERVER:1:16 + size + cached + uncached +} + +web.allservers.requests.size { + zero WEBSERVER:1:17 + le3k WEBSERVER:1:18 + le10k WEBSERVER:1:19 + le30k WEBSERVER:1:20 + le100k WEBSERVER:1:21 + le300k WEBSERVER:1:22 + le1m WEBSERVER:1:23 + le3m WEBSERVER:1:24 + gt3m WEBSERVER:1:25 + unknown WEBSERVER:1:66 +} + +web.allservers.bytes.size { + zero WEBSERVER:1:26 + le3k WEBSERVER:1:27 + le10k WEBSERVER:1:28 + le30k WEBSERVER:1:29 + le100k WEBSERVER:1:30 + le300k WEBSERVER:1:31 + le1m WEBSERVER:1:32 + le3m WEBSERVER:1:33 + gt3m WEBSERVER:1:34 +} + +web.allservers.requests.client { + total WEBSERVER:3:1 +} + +web.allservers.requests.cached { + total WEBSERVER:3:11 + size +} + +web.allservers.requests.cached.size { + zero WEBSERVER:3:12 + le3k WEBSERVER:3:13 + le10k WEBSERVER:3:14 + le30k WEBSERVER:3:15 + le100k WEBSERVER:3:16 + le300k WEBSERVER:3:17 + le1m WEBSERVER:3:18 + le3m WEBSERVER:3:19 + gt3m WEBSERVER:3:20 + unknown WEBSERVER:3:21 +} + +web.allservers.requests.uncached { + total WEBSERVER:3:31 + size +} + +web.allservers.requests.uncached.size { + zero WEBSERVER:3:32 + le3k WEBSERVER:3:33 + le10k WEBSERVER:3:34 + le30k WEBSERVER:3:35 + le100k WEBSERVER:3:36 + le300k WEBSERVER:3:37 + le1m WEBSERVER:3:38 + le3m WEBSERVER:3:39 + gt3m WEBSERVER:3:40 + unknown WEBSERVER:3:41 +} + +web.allservers.bytes.cached { + total WEBSERVER:3:51 + size +} + +web.allservers.bytes.cached.size { + zero WEBSERVER:3:52 + le3k WEBSERVER:3:53 + le10k WEBSERVER:3:54 + le30k WEBSERVER:3:55 + le100k WEBSERVER:3:56 + le300k WEBSERVER:3:57 + le1m WEBSERVER:3:58 + le3m WEBSERVER:3:59 + gt3m WEBSERVER:3:60 +} + +web.allservers.bytes.uncached { + total WEBSERVER:3:71 + size +} + +web.allservers.bytes.uncached.size { + zero WEBSERVER:3:72 + le3k WEBSERVER:3:73 + le10k WEBSERVER:3:74 + le30k WEBSERVER:3:75 + le100k WEBSERVER:3:76 + le300k WEBSERVER:3:77 + le1m WEBSERVER:3:78 + le3m WEBSERVER:3:79 + gt3m WEBSERVER:3:80 +} + +web.perserver { + watched WEBSERVER:0:35 /* is this server being watched? */ + numlogs WEBSERVER:2:36 /* number of logs I can read */ + requests + bytes + errors WEBSERVER:2:37 + logidletime WEBSERVER:2:68 +} + +web.perserver.requests { + total WEBSERVER:2:38 /* per server */ + get WEBSERVER:2:39 + head WEBSERVER:2:40 + post WEBSERVER:2:41 + other WEBSERVER:2:42 + size + client + cached + uncached +} + +web.perserver.bytes { + total WEBSERVER:2:43 /* per server */ + get WEBSERVER:2:44 + head WEBSERVER:2:45 + post WEBSERVER:2:46 + other WEBSERVER:2:47 + size + cached + uncached +} + +web.perserver.requests.size { + zero WEBSERVER:2:48 + le3k WEBSERVER:2:49 + le10k WEBSERVER:2:50 + le30k WEBSERVER:2:51 + le100k WEBSERVER:2:52 + le300k WEBSERVER:2:53 + le1m WEBSERVER:2:54 + le3m WEBSERVER:2:55 + gt3m WEBSERVER:2:56 + unknown WEBSERVER:2:67 +} + +web.perserver.bytes.size { + zero WEBSERVER:2:57 + le3k WEBSERVER:2:58 + le10k WEBSERVER:2:59 + le30k WEBSERVER:2:60 + le100k WEBSERVER:2:61 + le300k WEBSERVER:2:62 + le1m WEBSERVER:2:63 + le3m WEBSERVER:2:64 + gt3m WEBSERVER:2:65 +} + +web.perserver.requests.client { + total WEBSERVER:4:1 +} + +web.perserver.requests.cached { + total WEBSERVER:4:11 + size +} + +web.perserver.requests.cached.size { + zero WEBSERVER:4:12 + le3k WEBSERVER:4:13 + le10k WEBSERVER:4:14 + le30k WEBSERVER:4:15 + le100k WEBSERVER:4:16 + le300k WEBSERVER:4:17 + le1m WEBSERVER:4:18 + le3m WEBSERVER:4:19 + gt3m WEBSERVER:4:20 + unknown WEBSERVER:4:21 +} + +web.perserver.requests.uncached { + total WEBSERVER:4:31 + size +} + +web.perserver.requests.uncached.size { + zero WEBSERVER:4:32 + le3k WEBSERVER:4:33 + le10k WEBSERVER:4:34 + le30k WEBSERVER:4:35 + le100k WEBSERVER:4:36 + le300k WEBSERVER:4:37 + le1m WEBSERVER:4:38 + le3m WEBSERVER:4:39 + gt3m WEBSERVER:4:40 + unknown WEBSERVER:4:41 +} + +web.perserver.bytes.cached { + total WEBSERVER:4:51 + size +} + +web.perserver.bytes.cached.size { + zero WEBSERVER:4:52 + le3k WEBSERVER:4:53 + le10k WEBSERVER:4:54 + le30k WEBSERVER:4:55 + le100k WEBSERVER:4:56 + le300k WEBSERVER:4:57 + le1m WEBSERVER:4:58 + le3m WEBSERVER:4:59 + gt3m WEBSERVER:4:60 +} + +web.perserver.bytes.uncached { + total WEBSERVER:4:71 + size +} + +web.perserver.bytes.uncached.size { + zero WEBSERVER:4:72 + le3k WEBSERVER:4:73 + le10k WEBSERVER:4:74 + le30k WEBSERVER:4:75 + le100k WEBSERVER:4:76 + le300k WEBSERVER:4:77 + le1m WEBSERVER:4:78 + le3m WEBSERVER:4:79 + gt3m WEBSERVER:4:80 +} + diff --git a/src/pmdas/weblog/root b/src/pmdas/weblog/root new file mode 100644 index 0000000..de85ad7 --- /dev/null +++ b/src/pmdas/weblog/root @@ -0,0 +1,10 @@ +/* + * fake "root" for validating the local PMNS subtree + */ + +#include <stdpmid> + +root { web } + +#include "pmns" + diff --git a/src/pmdas/weblog/server.sh b/src/pmdas/weblog/server.sh new file mode 100755 index 0000000..fc63dfa --- /dev/null +++ b/src/pmdas/weblog/server.sh @@ -0,0 +1,1228 @@ +#! /bin/sh +# +# Copyright (c) 2000-2001,2003 Silicon Graphics, Inc. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License 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. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +# + +# Get standard environment +. $PCP_DIR/etc/pcp.env + +# Setup some default paths +_logdir=/var/log +_msgfil=messages + +# +# Every supported server defines 10 variables which are used by addServer() +# and installFiles() to create a server specification for the weblog PMDA +# and a URL for the webping PMDA, respectively. +# +# access The full path to the access log +# accessRegex The regex for the access log +# errors The full path to the error log +# errorRegex The regex for the error log +# serverPath The server path +# serverDesc A desciption of the type of server +# serverName The name for the server (must be unique) +# serverPort Port the server is bound to +# docs The full path to the document root +# "$noDocs" - indicates there is no document root +# http The URL for the server +# files How to put the HTML files into the doc root +# "link" - create a soft link +# "copy" - copy the files +# "skip" - do not create URLs for this server +# + +do_logs=false +do_files=false +do_auto=false +do_verbose=false +debug=false +noDocs="???" +unknownDocs="" +docsDir="$PCP_DOC_DIR/pcpweb/" +link="pcpweb" +file1="index.html" +file2="planning.html" +file3="tasks.html" +pfx=" " +skipping="${pfx}skipping..." +tmp=`mktemp -d /tmp/pcp.XXXXXXXXX` || exit 1 +trap "rm -rf $tmp; exit" 0 1 2 3 15 + +# Duplicate entry check file +# +rm -f $tmp/dup +touch $tmp/dup + +# pv 816562 - try to work around $PCP_DOC_DIR confusion +[ ! -x $docsDir -a -x /usr/pcp/doc/pcpweb/ ] && docsDir=/usr/pcp/doc/pcpweb/ + +LOCALHOSTNAME=`hostname` + +# web server attribute names (global) +# +ATTRLIST="serverPort serverName docs access errors" + +# look for netscape stuff here +# +NSROOTPATH="${NSROOTPATH-/usr/ns-home:/var/netscape/suitespot:/var/netscape/fasttrack}" + +# the Netscape server types we know how to handle +# +NSTYPE="httpd https proxy" + +# look for old outbox +# +OUTBOXPATH="${OUTBOXPATH-/var/mc-httpd}" + +# look for old Netscape Proxy Servers here +# +NSPROXYPATH="${NSPROXYPATH-/var/ns-proxy}" + +# look for NCSA servers here +# +NCSAPATH="${NCSAPATH-/var/www}" + +# look for Zeus servers here +# +ZEUSPATH="${ZEUSPATH-/usr/local/zeus}" + +# look for Squid Object caches here +# +SQUIDPATH="${SQUIDPATH-/usr/local/squid}" + +# look for Apache servers here +# +APACHEPATH="${APACHEPATH-/etc/apache2:/etc/httpd}" + +# look for anonymous ftp here +# +FTPPATH="${FTPPATH:-$_logdir}" + +# look for password file here (to determine ~ftp) +# +PASSWDPATH="${PASSWDPATH-/etc/passwd}" + +# look for SYSLOG here +# +SYSLOGPATH="${SYSLOGPATH:-$_logdir/$_msgfil}" + +# look for xferlog here (for wu_ftp) +# XFERLOG is default for wu_ftp +XFERLOG="${XFERLOG:-$_logdir/xferlog}" +# WUFTPLOG is used in sgi freeware wu_ftp +WUFTPLOG="${WUFTPLOG-$_logdir/wu-ftpd.log}" + +_getLogFiles() +{ + $PCP_ECHO_PROG $PCP_ECHO_N "Full path to access log [$access] ""$PCP_ECHO_C" + read ans + if [ -n "$ans" ] + then + access="$ans" + if [ ! -f "$access" ] + then + echo "Warning: $access does not exist at this time." + fi + fi + + $PCP_ECHO_PROG $PCP_ECHO_N "Full path to error log [$errors] ""$PCP_ECHO_C" + read ans + if [ -n "$ans" ] + then + errors="$ans" + if [ ! -f "$errors" ] + then + echo "Warning: $errors does not exist at this time." + fi + fi +} + +# _addServer_check +# 1: servername +# 2: other args for log file +# 3: server description +_addServer_check() +{ + if grep "$1" $tmp/dup > /dev/null 2>&1 + then + echo "${pfx}Error: already found a server with the name \"$1\"" + if $do_auto + then + echo "$skipping" + else + $PCP_ECHO_PROG $PCP_ECHO_N "New name for server (or return to skip this server): ""$PCP_ECHO_C" + read ans + if [ -n "$ans" ] + then + if grep "$ans" $tmp/dup > /dev/null 2>&1 + then + echo "${pfx}Error: new server name already exists" + echo "$skipping" + else + echo >> $logFile + echo "# $3." >> $logFile + echo "server $ans $2" >> $logFile + echo "$1" >> $tmp/dup + fi + else + echo "$skipping" + fi + fi + else + echo >> $logFile + echo "# $3." >> $logFile + echo "server $1 $2" >> $logFile + echo "$1" >> $tmp/dup + fi +} + +_addServer() +{ + echo "Found $serverDesc at $serverPath" + if [ \( -f "$access" -o -c "$access" \) -a \( -f "$errors" -o -c "$errors" \) ] + then + found="true" + if [ -z "$serverPort" ] + then + echo "${pfx}identified as $serverName" + _addServer_check "$serverName" "on $accessRegex $access $errorRegex $errors" "$serverDesc" + else + echo "${pfx}identified as $serverName:$serverPort" + _addServer_check "$serverName:$serverPort" "on $accessRegex $access $errorRegex $errors" "$serverDesc" + fi + echo + else + echo "${pfx}Error: log files are not in the expected place:" + echo "${pfx} $access" + echo "${pfx} $errors" + if $do_auto + then + echo "$skipping" + else + echo + echo "Do you want to specify an alternate location for the log files" + $PCP_ECHO_PROG $PCP_ECHO_N "(otherwise this server will be ignored) [y] ""$PCP_ECHO_C" + read ans + if [ -z "$ans" -o "$ans" = "y" -o "$ans" = "Y" ] + then + _getLogFiles + if [ -z "$serverPort" ] + then + _addServer_check "$serverName" "on $accessRegex $access $errorRegex $errors" "$serverDesc" + else + _addServer_check "$serverName:$serverPort" "on $accessRegex $access $errorRegex $errors" "$serverDesc" + fi + else + echo "$skipping" + fi + fi + echo + fi +} + +_installFiles() +{ + + echo + echo "Found $serverDesc at $serverPath" + if [ "$docs" != "$noDocs" ] + then + if [ "$docs" = "$unknownDocs" ] + then + echo "${pfx}Error: unable to determine document root" + if $do_auto + then + docs=$unknownDocs + echo "$skipping" + else + echo + $PCP_ECHO_PROG $PCP_ECHO_N "Path to document root (return to skip HTML link for this server): ""$PCP_ECHO_C" + read ans + if [ -n "$ans" ] + then + if [ -d "$ans" ] + then + docs="$ans" + else + echo "\"$ans\" is not a directory." + echo "No link to HTML files will be created for this server." + fi + fi + fi + elif [ ! -d "$docs" ] + then + echo "${pfx}Error: document root cannot be found at:" + echo "${pfx} $docs" + if $do_auto + then + docs=$unknownDocs + echo "$skipping" + else + echo + echo "Path to document root (return to skip HTML link for this server)" + $PCP_ECHO_PROG $PCP_ECHO_N "$docs: ""$PCP_ECHO_C" + read ans + if [ -n "$ans" ] + then + if [ -d $ans ] + then + docs=$ans + else + echo "${pfx}Error: \"$ans\" is not a directory." + docs=$unknownDocs + echo "$skipping" + fi + else + docs=$unknownDocs + fi + fi + else + echo "${pfx}document root found at:" + echo "${pfx}$docs" + fi + + if [ "$docs" != "$unknownDocs" ] + then + if [ ! -f $docs/$link/$file1 -o ! -f $docs/$link/$file2 -o ! -f $docs/$link/$file3 ] + then + if [ "$files" = "link" ] + then + $PCP_ECHO_PROG $PCP_ECHO_N "Do you want a link to some sample HTML files created in this directory [y] ""$PCP_ECHO_C" + elif [ "$files" = "copy" ] + then + $PCP_ECHO_PROG $PCP_ECHO_N "Do you want some sample HTML files installed in this directory [y] ""$PCP_ECHO_C" + fi + + read ans + if [ -z "$ans" -o "$ans" = "y" -o "$ans" = "Y" ] + then + if [ "$files" = "link" ] + then + [ -L $docs/$link ] && rm -f $docs/$link + if [ -f $docs/$link ] + then + echo "${pfx}Error: $docs/$link already exists." + echo "$skipping" + else + ln -s $docsDir $docs/$link + fi + elif [ "$files" = "copy" ] + then + if [ ! -f $docs/$link -o -d $docs/$link ] + then + cp $docsDir/$file1 $docs/$link/$file1 + cp $docsDir/$file2 $docs/$link/$file2 + cp $docsDir/$file3 $docs/$link/$file3 + else + echo "${pfx}Error: $docs/$link is not a directory." + echo "$skipping" + fi + fi + + if [ -z "$http" ] + then + echo "$http/$link/$file1" >> $logFile + echo "$http/$link/$file2" >> $logFile + echo "$http/$link/$file3" >> $logFile + fi + fi + else + echo "${pfx}Note: the link to the sample HTML files already exists." + if [ -n "$http" ] + then + echo "$http/$link/$file1" >> $logFile + echo "$http/$link/$file2" >> $logFile + echo "$http/$link/$file3" >> $logFile + fi + fi + else + if [ "$docs" != "$unknownDocs" ] + then + echo "$skipping" + fi + fi + else + echo "${pfx}Error: no document root, cannot install files." + echo "$skipping" + fi + echo +} + +_switchAction() +{ + if $do_verbose + then + echo "------------------------------------------------------------" + echo "access = $access" + echo "accessRegex = $accessRegex" + echo "errors = $errors" + echo "errorRegex = $errorRegex" + echo "serverPath = $serverPath" + echo "serverDesc = $serverDesc" + echo "serverName = $serverName" + echo "serverPort = $serverPort" + echo "docs = $docs" + echo "http = $http" + echo "files = $files" + echo "------------------------------------------------------------" + echo + fi + + if $do_logs + then + _addServer + else + _installFiles + fi +} + +_scan_config() +{ + serverPort= + serverName= + errors= + access= + docs= + + eval `cat $* 2>/dev/null | $PCP_AWK_PROG ' +NF == 2 && tolower($1) == "port" { print "serverPort=" $2; next } +NF == 2 && tolower($1) == "errorlog" { print "errors=" $2; next } +NF == 2 && tolower($1) == "servername" { print "serverName=" $2; next } +tolower($1) == "init" { for (i=1; i<=NF; i++) { + if (match($i, "^access=")) print $i + if (match($i, "^global=")) printf "access=%s ",substr($i,8,length($i)-7) + } + next + } +tolower($0) ~ /fn="document-root"/ || tolower($0) ~ /fn=document-root/ { + for (i=1; i<=NF; i++) { + if (match($i, "^root=")) + printf "docs=%s ",substr($i,6,length($i)-5) + } + next + }'` +} + +_netscape_extract() +{ + here=`pwd` + echo >$tmp/ns + for root in `echo "$NSROOTPATH" | sed -e 's/:/ /g'` + do + for type in $NSTYPE + do + for dir in $root/$type-* + do + [ -L "$dir" ] && continue + if [ -d "$dir" ] + then + cd $dir + check=`pwd` + cd $here + match=`grep "^$check " $tmp/ns` + if [ ! -z "$match" ] + then + echo "$match $dir" | $PCP_AWK_PROG ' + { if ($1 == $2) + printf "The server at %s appears to be a link to %s which is already monitored as %s\n", $3, $2, $1 + else if ($1 == $3) { + printf "The server at %s was already detected,\n", $1 + printf "using the link %s. ", $2 + } + else { + printf "The server at %s was already detected,\n", $1 + printf "using the link %s. The link %s,\n", $2, $3 + printf "which is also to this server, will be ignored.\n" + } + }' + echo "$skipping" + continue + fi + echo "$check $dir" >>$tmp/ns + access= + errors= + docs= + serverName= + serverPort= + + if [ ! -f $dir/config/obj.conf ] + then + echo "Found Netscape $type Server at $dir" + echo "${pfx}Error: unable to find configuration file:" + echo "${pfx} $dir/config/obj.conf" + echo "$skipping" + echo + continue + elif [ ! -f $dir/config/magnus.conf ] + then + echo "Found Netscape $type Server at $dir" + echo "${pfx}Error: unable to find configuration file:" + echo "${pfx} $dir/config/magnus.conf" + echo "$skipping" + echo + continue + fi + + _scan_config $dir/config/obj.conf $dir/config/magnus.conf + + # fix server name as Netscape often adds a trailing '.' + serverName=`echo $serverName | sed -e 's/\.$//'` + + if [ -z "$serverName" -o -z "$serverPort" ] + then + echo "Found Netscape $type Server at $dir" + echo "${pfx}Error: unable to determine server name or port from:" + echo "${pfx} $dir/config/magnus.conf" + echo "$skipping" + echo + continue + fi + + if [ -z "$access" ] + then + access=$dir/logs/access + echo "Found Netscape $type Server at $dir" + echo "${pfx}Warning: unable to determine access log name, assuming:" + echo "${pfx} $access" + echo + fi + + if [ -z "$errors" ] + then + errors=$dir/logs/errors + echo "Found Netscape $type Server at $dir" + echo "${pfx}Warning: unable to determine error log name, assuming:" + echo "${pfx} $errors" + echo + fi + + # + # figure out if this is a caching server or not + # + if [ -f $access ] + then + num_lines=`wc -l $access | $PCP_AWK_PROG '{print $1}'` + if [ $num_lines -gt 1 ] + then + num_fields=`tail -n 1 $access | cut -f3 -d\" | wc -w` + if [ $num_fields -eq 11 ] + then + accessRegex="NS_PROXY" + else + accessRegex="CERN" + fi + else + accessRegex="CERN" + fi + fi + + errorRegex="CERN_err" + serverPath="$dir" + serverDesc="Netscape $type Server" + http="GET http://$serverName:$serverPort" + files="link" + + _switchAction + fi + done + done + done +} + +_zeus_extract() +{ + if [ -d $ZEUSPATH ] + then + if [ -f $ZEUSPATH/server.ini ] + then + rm -f $tmp/zeus + touch $tmp/zeus + $PCP_AWK_PROG < $ZEUSPATH/server.ini -v hostname=$LOCALHOSTNAME -v out=$tmp/zeus -v ini=$ZEUSPATH/server.ini -F'=' ' +BEGIN { mode = 0; + port = 0; + name = ""; + access = "???"; + errors = "???"; + docs = "???"; + host = hostname; + } + +/^port/ { if (mode == 0) + port=$2; + next + } +$1 ~ /\[Admin/ { mode = 1; next } +$1 ~ /\[Server/ { if (mode == 2) { + printf("%s %s %s %d %s %s\n", name, access, errors, port, host, docs) > out; + name=""; access="???"; errors="???"; docs="???"; + host = hostname + } + else + mode = 2; + + i = match($1, " .*]"); + if (i == 0) { + mode = 1 + } + else { + name = substr($1, RSTART+1, RLENGTH-2); + } + next + } +/^logdir/ { if (mode == 2) { + access = sprintf("%s/transfer", $2); + errors = sprintf("%s/errors", $2); + } + next + } +/^docroot/ { if (mode == 2) + docs = $2; + next + } +/^ipname/ { if (mode == 2) + host = $2; + next + } +END { if (mode == 2) + printf("%s %s %s %d %s %s\n", name, access, errors, port, host, docs) > out; + }' + + if [ -f $tmp/zeus -a -s $tmp/zeus ] + then + accessRegex=CERN + errorRegex=CERN_err + serverPath=$ZEUSPATH + files="link" + lines=`wc -l $tmp/zeus | $PCP_AWK_PROG '{ print $1 }'` + count=1 + while [ $count -le $lines ] + do + eval `$PCP_AWK_PROG < $tmp/zeus -v line=$count ' +NR == line { printf("serverName=%s\naccess=%s\nerrors=%s\nserverPort=%d\nhttp=%s\ndocs=%s\n", $1, $2, $3, $4, $5, $6); exit }'` + + serverDesc="Zeus Server $serverName" + serverName="zeus-$serverName" + http="GET http://$http:$serverPort" + + if [ "$access" = "???" ] + then + access=$ZEUSPATH/log/transfer + echo "Found $serverDesc at $serverPath" + echo "${pfx}Warning: unable to determine access log name, assuming:" + echo "${pfx} $access" + echo + fi + if [ "$errors" = "???" ] + then + errors=$ZEUSPATH/log/errors + echo "Found $serverDesc at $serverPath" + echo "${pfx}Warning: unable to determine error log name, assuming:" + echo "${pfx} $errors" + echo + fi + if [ "$docs" = "???" ] + then + docs=$ZEUSPATH/docroot + echo "Found $serverDesc at $serverPath" + echo "${pfx}Warning: unable to determine document root, assuming:" + echo "${pfx} $docs" + echo + fi + + _switchAction + count=`expr $count + 1` + done + else + echo "Found Zeus Server/s at $ZEUSPATH" + echo "${pfx}Error: could not detect any servers in configuration file:" + echo "${pfx} $ZEUSPATH/server.ini" + echo "$skipping" + fi + + else + echo "Found Zeus Server at $ZEUSPATH" + echo "${pfx}Error: unable to find configuration file:" + echo "${pfx} $ZEUSPATH/server.ini" + echo "$skipping" + fi + fi +} + +_squid_extract() +{ + if [ -d $SQUIDPATH ] + then + if [ -f $SQUIDPATH/etc/squid.conf ] + then + rm -f $tmp/squid + touch $tmp/squid + $PCP_AWK_PROG < $SQUIDPATH/etc/squid.conf -v hostname=${LOCALHOSTNAME} -v out=$tmp/squid ' +BEGIN { port = 3128; + name = hostname; + access = "/usr/local/squid/logs/access.log"; + errors = "/dev/null"; + host = hostname; + } + +$1 == "emulate_httpd_log" { if ( $2 == "on" ) + mode = 1; + } +$1 == "cache_access_log" { access = $2; + } +$1 == "http_port" { port = $2; + } +$1 == "visible_hostname" { name = $2; + } +END { if (mode == 0) + printf("SQUID %s %s %s %d %s\n", name, access, errors, port, host) > out; + else + printf("CERN %s %s %s %d %s\n", name, access, errors, port, host) > out; + }' + + if [ -f $tmp/squid -a -s $tmp/squid ] + then + grep CERN $tmp/squid >/dev/null + if [ $? -eq 0 ] + then + accessRegex=CERN + else + accessRegex=SQUID + fi + errorRegex=CERN_err + serverPath=$SQUIDPATH + files="skip" + eval `$PCP_AWK_PROG < $tmp/squid ' +{ printf("serverName=%s\naccess=%s\nerrors=%s\nserverPort=%d\nhttp=%s\n", $2, $3, $4, $5, $6); exit }'` + + serverDesc="Squid Server $serverName" + serverName="squid-$serverName" + http="GET http://$http:$serverPort" + docs=$noDocs + + if [ "$access" = "???" ] + then + access=$SQUIDPATH/log/transfer + echo "Found $serverDesc at $serverPath" + echo "${pfx}Warning: unable to determine access log name, assuming:" + echo "${pfx} $access" + echo + fi + if [ "$errors" = "???" ] + then + errors=$SQUIDPATH/log/errors + echo "Found $serverDesc at $serverPath" + echo "${pfx}Warning: unable to determine error log name, assuming:" + echo "${pfx} $errors" + echo + fi + + _switchAction + else + echo "Found Squid Server/s at $SQUIDPATH" + echo "${pfx}Error: could not detect any servers in configuration file:" + echo "${pfx} $SQUIDPATH/squid.conf" + echo "$skipping" + fi + + else + echo "Found Squid Server at $SQUIDPATH" + echo "${pfx}Error: unable to find configuration file:" + echo "${pfx} $SQUIDPATH/squid.conf" + echo "$skipping" + fi + fi +} + +_apache_extract() +{ + for apchroot in `echo "$APACHEPATH" | sed -e 's/:/ /g'` + do + $debug && echo "_apache_extract: apachroot=$apchroot" + if [ -d "$apchroot/sites-available" ] + then + config="$apchroot/sites-available/" + elif [ -d "$apchroot/vhosts.d" ] + then + config="$apchroot/vhosts.d/" + elif [ -d "$apchroot/conf" ] + then + config="$apchroot/conf/" + elif [ -f "$apchroot/httpd.conf" ] + then + config="$apchroot/httpd.conf" + else + continue + fi + + for config in `echo ${config}*` + do + $debug && echo "_apache_extract: config=$config" + [ -f "$config" ] || continue + + cat $config \ + | sed -e's/#.*//' -e'/^$/d' \ + | $PCP_AWK_PROG -v def=`hostname` ' + BEGIN { + curnam=def; + names[def] = curnam; + ports[def] = 80; + } + $1 == "<VirtualHost" { + sub(">", "", $2); + n = split ($2, nm, ":"); + if ( n == 1 ) { + curnam=$2; + port=ports[def]; + } else { + if ( length (nm[1]) && nm[1] != "*" ) { + curnam = nm[1]; + } else { + curnam = def; + } + if ( length (nm[2]) ) { + port = nm[2]; + } else { + port = ports[def]; + } + } + cn=sprintf("%s:%d", curnam, port); + names[cn] = curnam; + ports[cn] = port; + curnam = cn; + } + $1 == "ServerName" { names[curnam] = $2; } + $1 == "Port" { ports[curnam] = $2; } + $1 == "</VirtualHost>" { curnam=def; } + $1 == "ErrorLog" { + path = $2 + sub (/\$\{APACHE_LOG_DIR\}/, "/var/log/apache2", path) + if ( match (path, "/") != 1 ) { + erlog[curnam] = sprintf ("%s/%s", "'$apchroot'", path); + } else { + erlog[curnam] = path; + } + } + $1 == "DocumentRoot" { + path = $2 + sub (/\$\{APACHE_LOG_DIR\}/, "/var/log/apache2", path) + if ( match (path, "/") != 1 ) { + docs[curnam] = sprintf ("%s/%s", "'$apchroot'", path); + } else { + docs[curnam] = path; + } + } + $1 == "TransferLog" { + path = $2 + sub (/\$\{APACHE_LOG_DIR\}/, "/var/log/apache2", path) + if ( match (path, "/") != 1 ) { + tlog[curnam] = sprintf ("%s/%s", "'$apchroot'", path); + } else { + tlog[curnam] = path; + } + } + $1 == "CustomLog" && ($3 == "common" || $3 == "combined") { + path = $2 + sub (/\$\{APACHE_LOG_DIR\}/, "/var/log/apache2", path) + if ( match (path, "/") != 1 ) { + tlog[curnam] = sprintf ("%s/%s", "'$apchroot'", path); + } else { + tlog[curnam] = path; + } + } + END { + for ( n in names ) { + print names[n], ports[n], tlog[n], erlog[n], docs[n]; + } + }'\ + | while read serverName serverPort access errors docs ; do + $debug && echo "_apache_extract: serverName=$serverName serverPort=$serverPort access=$access errors=$errors docs=$docs" + + accessRegex=CERN + errorRegex=CERN_err + serverPath="$apchroot" + serverDesc="Apache Server" + files="copy" + + _switchAction + done + done + done +} + +while [ $# -gt 0 ] +do + case $1 + in + + -d) # debug + debug=true + ;; + + -f) # install dummy HTML files in server document root + do_files=true + if [ $# -gt 1 ] + then + shift + logFile=$1 + else + echo "-f requires the name of log file" + exit 1 + fi + ;; + + -l) # generate pmdaweblog configuration file + do_logs=true + if [ $# -gt 1 ] + then + shift + logFile=$1 + else + echo "-l requires the name of log file" + exit 1 + fi + ;; + + -q) # do not prompt for misconfigured servers + do_auto=true + ;; + + -v) # verbose + do_verbose=true + ;; + + *) # USAGE + echo "Usage: server.sh [-dqv] [-f logFile] [-l logFile]" + exit 1 + ;; + esac + shift +done + +if [ "$do_logs" = "true" -a "$do_files" = "true" ] +then + echo "May only perform one of the two options at any one time" + exit 1 +fi + +if $do_files +then + if [ ! -f $docsDir/$file1 -o ! -f $docsDir/$file2 -o ! -f $docsDir/$file3 ] + then + echo "Some or all of the sample HTML files ($file1, $file2, $file3)" + echo "are missing. Cannot continue!" + exit 1 + fi +fi + +_netscape_extract + +# Another common place for Netscape servers + +if [ -d $OUTBOXPATH ] +then + access=$OUTBOXPATH/logs/access + accessRegex="CERN" + errors=$OUTBOXPATH/logs/errors + errorRegex="CERN_err" + serverPath=$OUTBOXPATH + serverDesc="Outbox Server" + serverName="outbox-$LOCALHOSTNAME" + serverPort= + docs="$OUTBOXPATH/html" + http="GET http://$LOCALHOSTNAME" + files="link" + _switchAction +fi + +# Netscape Proxy Server + +if [ -d $NSPROXYPATH/logs ] +then + access=$NSPROXYPATH/logs/access + accessRegex="CERN" + errors=$NSPROXYPATH/logs/errors + errorRegex="CERN_err" + serverPath=$NSPROXYPATH + serverDesc="Old Netscape proxy Server" + serverName="proxy-$LOCALHOSTNAME" + serverPort= + docs=$noDocs + http="" + files="skip" + _switchAction +fi + +# Netscape SOCKS Proxy Server + +if [ -f $NSROOTPATH/proxy-server/logs/sockd ] +then + access=$NSROOTPATH/proxy-server/logs/sockd + accessRegex="NS_SOCKS" + errors=/dev/null + errorRegex="NS_SOCKS_err" + serverPath=$NSROOTPATH/proxy-server + serverDesc="Netscape SOCKS Proxy Server" + serverName="socks-$LOCALHOSTNAME" + serverPort= + docs=$noDocs + http="" + files="skip" + _switchAction + + if [ "$do_logs" = "true" ] + then + echo + $PCP_ECHO_PROG $PCP_ECHO_N "Would you like to log SOCKS ftp transactions [y] ""$PCP_ECHO_C" + read ans + if [ -z "$ans" -o "$ans" = "y" -o "$ans" = "Y" ] + then + access=$NSROOTPATH/proxy-server/logs/sockd + accessRegex="NS_FTP" + errors=/dev/null + errorRegex="NS_FTP_err" + serverPath=$NSROOTPATH/proxy-server + serverDesc="FTP through Netscape SOCKS Server" + serverName="ftp-socks-$LOCALHOSTNAME" + serverPort= + docs=$noDocs + http="" + files="skip" + _switchAction + fi + fi +fi + +if [ -f $NSPROXYPATH/logs/sockd ] +then + access=$NSPROXYPATH/logs/sockd + accessRegex="NS_SOCKS" + errors=/dev/null + errorRegex="NS_SOCKS_err" + serverPath=$NSPROXYPATH + serverDesc="Netscape SOCKS Proxy Server" + serverName="socks-$LOCALHOSTNAME" + serverPort= + docs=$noDocs + http="" + files="skip" + _switchAction + + if [ "$do_logs" = "true" ] + then + echo + $PCP_ECHO_PROG $PCP_ECHO_N "Would you like to log SOCKS ftp transactions [y] ""$PCP_ECHO_C" + read ans + if [ -z "$ans" -o "$ans" = "y" -o "$ans" = "Y" ] + then + access=$NSPROXYPATH/logs/sockd + accessRegex="NS_FTP" + errors=/dev/null + errorRegex="NS_FTP_err" + serverPath=$NSPROXYPATH + serverDesc="FTP through Netscape SOCKS Server" + serverName="ftp-socks-$LOCALHOSTNAME" + serverPort= + docs=$noDocs + http="" + files="skip" + _switchAction + fi + fi +fi + +# NCSA (or derived) Server + +if [ -d $NCSAPATH/server ] +then + access=$NCSAPATH/server/logs/access_log + accessRegex="CERN" + errors=$NCSAPATH/server/logs/error_log + errorRegex="CERN_err" + serverPath=$NCSAPATH/server + serverDesc="NCSA (or derived) Server" + serverName="ncsa-$LOCALHOSTNAME" + serverPort= + docs="$NCSAPATH/htdocs" + http="GET http://$LOCALHOSTNAME" + files="link" + _switchAction +fi + +# Zeus Server + +_zeus_extract + +# Squid Server + +_squid_extract + +# Apache Server + +_apache_extract + +# Oracle Webserver + +if [ -n "$ORACLE_HOME" ] +then + if [ -d $ORACLE_HOME/ows ] + then + serverPath=$ORACLE_HOME/ows + for i in `ls $serverPath/log/sv*.log*` + do + serverPort=`basename $i | sed 's/sv//' | sed 's/\.log//'` + access=$i + accessRegex="CERN" + errors="$serverPath/ows/log/sv$serverPort.err" + errorRegex="CERN_err" + serverDesc="Oracle Webserver" + serverName="ows-$serverPort" + docs="$serverPath/doc" + http="GET http://${LOCALHOSTNAME}:$serverPort" + files="link" + _switchAction + done + fi +fi + +# Harvest Cache + +if [ -n "$HARVEST_HOME" ] +then + serverPath=$HARVEST_HOME + serverPort=80 + access=$serverPath/cache.access.log + accessRegex="CERN" + errors="$serverPath/cache.log" + errorRegex="CERN_err" + serverDesc="Harvest Cache" + serverName="harvest-cache" + docs="$noDocs" + http="" + files="skip" + _switchAction +elif [ -d /usr/local/harvest ] +then + serverPath=/usr/local/harvest + serverPort=80 + access=$serverPath/cache.access.log + accessRegex="CERN" + errors="$serverPath/cache.log" + errorRegex="CERN_err" + serverDesc="Harvest Cache" + serverName="harvest-cache" + docs="$noDocs" + http="" + files="skip" + _switchAction +fi + +# FTP +if [ -n "$QUIET_INSTALL" ]; then + ans=y +else + echo + if [ "$do_logs" = "true" ] + then + $PCP_ECHO_PROG $PCP_ECHO_N "Do you also want to monitor ftp transactions [y] ""$PCP_ECHO_C" + else + $PCP_ECHO_PROG $PCP_ECHO_N "Would you like the sample HTML files installed for anonymous ftp [y] ""$PCP_ECHO_C" + fi + read ans +fi + +if [ -z "$ans" -o "$ans" = "y" -o "$ans" = "Y" ] +then + echo + if [ -f $PASSWDPATH ] + then + if [ -n "$QUIET_INSTALL" ] ; then + ans=y + else + $PCP_ECHO_PROG $PCP_ECHO_N "Do you want to monitor wu_ftp [n] ""$PCP_ECHO_C" + read ans + echo + fi + if [ "$ans" = "y" -o "$ans" = "Y" ] + then + wulog="$WUFTPLOG" + if [ -f "$WUFTPLOG" ] + then + wulog="$WUFTPLOG" + elif [ -f "$XFERLOG" ] + then + wulog="$XFERLOG" + else + if $do_auto + then + echo "${pfx}Error: log files are not in the expected place:" + echo "$pfx $XFERLOG" + echo "$pfx or $WUFTPLOG" + echo "$skipping" + wulog="" + fi + fi + + access="$wulog" + accessRegex="WU_FTP" + errors="$SYSLOGPATH" + errorRegex="WU_FTP_err" + serverDesc="WU_FTP Server" + serverName="wu_ftp" + serverPath=$FTPPATH + serverPort= + else + access=$SYSLOGPATH + accessRegex="SYSLOG_FTP" + errors=$SYSLOGPATH + errorRegex="SYSLOG_FTP_err" + serverDesc="FTP Server" + serverName="ftpd" + serverPath=$FTPPATH + serverPort= + fi + + if [ ! -z "$access" ] + then + docs="" + docs=`$PCP_AWK_PROG -F: '$1 == "ftp" { print $6 "/pub"; exit }' < ${PASSWDPATH}` + if [ -z "$docs" ] + then + echo "Found FTP Server at $FTPPATH" + echo "${pfx}Error: user ftp is not listed in the password file:" + echo "${pfx} $PASSWDPATH" + echo "$skipping" + else + http="GET ftp://$LOCALHOSTNAME/pub" + files="copy" + _switchAction + fi + fi + else + echo "Found FTP Server at $FTPPATH" + echo "${pfx}Error: unable to find password file:" + echo "${pfx} $PASSWDPATH" + echo "$skipping" + fi +fi + +if $do_logs +then + : +else + [ -f "$logFile" ] && sort -u $logFile -o $logFile +fi diff --git a/src/pmdas/weblog/sproc.c b/src/pmdas/weblog/sproc.c new file mode 100644 index 0000000..e9fdc7d --- /dev/null +++ b/src/pmdas/weblog/sproc.c @@ -0,0 +1,39 @@ +/* + * Web PMDA, based on generic driver for a daemon-based PMDA + * + * Copyright (c) 2000,2004 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "weblog.h" +#if defined(HAVE_PTHREAD_H) +#include <pthread.h> +#endif +#if defined(HAVE_SCHED_H) +#include <sched.h> +#endif + +#if defined(HAVE_PTHREAD_H) +static pthread_t sproc_thread; + +int sproc (void (*entry) (void *), int flags, void *arg) +{ + int retval; + + retval = pthread_create(&sproc_thread, NULL, (void (*))entry, NULL); + return retval; +} +#endif diff --git a/src/pmdas/weblog/weblog.c b/src/pmdas/weblog/weblog.c new file mode 100644 index 0000000..570d104 --- /dev/null +++ b/src/pmdas/weblog/weblog.c @@ -0,0 +1,3132 @@ +/* + * Copyright (c) 2000,2004 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License 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. + */ + +#include "weblog.h" +#include "domain.h" +#include <ctype.h> +#include <sys/stat.h> +#if defined(HAVE_SYS_RESOURCE_H) +#include <sys/resource.h> +#endif +#if defined(HAVE_SYS_PRCTL_H) +#include <sys/prctl.h> +#endif +#if defined(HAVE_SYS_WAIT_H) +#include <sys/wait.h> +#endif + +/* + * Types of metrics, used by fetch to more efficiently calculate metrics + */ + +enum MetaType { + wl_globalPtr, wl_offset32, wl_offset64, wl_totalAggregate, + wl_serverAggregate, wl_requestMethod, wl_bytesMethod, + wl_requestSize, wl_requestCachedSize, wl_requestUncachedSize, + wl_bytesSize, wl_bytesCachedSize, wl_bytesUncachedSize, + wl_watched, wl_numAlive, wl_nosupport, wl_numMetaTypes +}; + +/* + * Return code of checkLogFile() + */ + +enum LogFileCode { + wl_ok, wl_opened, wl_reopened, wl_closed, wl_unableToOpen, wl_unableToStat, + wl_irregularFile, wl_dormant +}; + +static WebCount dummyCount; + +/* + * Instance domain table + * This is completed when parsing the config file + */ + +pmdaIndom wl_indomTable[] = +{ +#define WEBLOG_INDOM 0 + { WEBLOG_INDOM, 0, 0 } +}; + +/* + * Metric specific data to help identify each metric during a fetch + * + * MG: Note: must be in the same order as wl_metric table below. + * + */ + +typedef struct { + int m_type; + __psint_t m_offset; +} WebMetric; + +WebMetric wl_metricInfo[] = +{ +/* config.numservers */ + { wl_globalPtr, (__psint_t)&wl_numServers }, +/* config.catchup */ + { wl_globalPtr, (__psint_t)&wl_refreshDelay }, +/* config.catchuptime */ + { wl_globalPtr, (__psint_t)&wl_catchupTime }, +/* config.check */ + { wl_globalPtr, (__psint_t)&wl_chkDelay }, +/* allserves.numwatched */ + { wl_globalPtr, (__psint_t)&wl_numActive }, +/* allserves.numalive */ + { wl_numAlive, (__psint_t)0 }, +/* allservers.errors */ + { wl_totalAggregate, (__psint_t)0 }, +/* allservers.requests.total */ + { wl_totalAggregate, (__psint_t)1 }, +/* allservers.requests.get */ + { wl_requestMethod, (__psint_t)wl_httpGet }, +/* allservers.requests.head */ + { wl_requestMethod, (__psint_t)wl_httpHead }, +/* allservers.requests.post */ + { wl_requestMethod, (__psint_t)wl_httpPost }, +/* allservers.requests.other */ + { wl_requestMethod, (__psint_t)wl_httpOther }, +/* allservers.bytes.total */ + { wl_totalAggregate, (__psint_t)2 }, +/* allservers.bytes.get */ + { wl_bytesMethod, (__psint_t)wl_httpGet }, +/* allservers.bytes.head */ + { wl_bytesMethod, (__psint_t)wl_httpHead }, +/* allservers.bytes.post */ + { wl_bytesMethod, (__psint_t)wl_httpPost }, +/* allservers.bytes.other */ + { wl_bytesMethod, (__psint_t)wl_httpOther }, +/* allservers.requests.size.zero */ + { wl_requestSize, (__psint_t)wl_zero }, +/* allservers.requests.size.le3k */ + { wl_requestSize, (__psint_t)wl_le3k }, +/* allservers.requests.size.le10k */ + { wl_requestSize, (__psint_t)wl_le10k }, +/* allservers.requests.size.le30k */ + { wl_requestSize, (__psint_t)wl_le30k }, +/* allservers.requests.size.le100k */ + { wl_requestSize, (__psint_t)wl_le100k }, +/* allservers.requests.size.le300k */ + { wl_requestSize, (__psint_t)wl_le300k }, +/* allservers.requests.size.le1m */ + { wl_requestSize, (__psint_t)wl_le1m }, +/* allservers.requests.size.le3m */ + { wl_requestSize, (__psint_t)wl_le3m }, +/* allservers.requests.size.gt3m */ + { wl_requestSize, (__psint_t)wl_gt3m }, +/* allservers.requests.size.unknown */ + { wl_requestSize, (__psint_t)wl_unknownSize }, +/* allservers.requests.client.total */ + { wl_totalAggregate, (__psint_t)3 }, +/* allservers.requests.cached.total */ + { wl_totalAggregate, (__psint_t)4 }, +/* allservers.requests.cached.size.zero */ + { wl_requestCachedSize, (__psint_t)wl_zero }, +/* allservers.requests.cached.size.le3k */ + { wl_requestCachedSize, (__psint_t)wl_le3k }, +/* allservers.requests.cached.size.le10k */ + { wl_requestCachedSize, (__psint_t)wl_le10k }, +/* allservers.requests.cached.size.le30k */ + { wl_requestCachedSize, (__psint_t)wl_le30k }, +/* allservers.requests.cached.size.le100k */ + { wl_requestCachedSize, (__psint_t)wl_le100k }, +/* allservers.requests.cached.size.le300k */ + { wl_requestCachedSize, (__psint_t)wl_le300k }, +/* allservers.requests.cached.size.le1m */ + { wl_requestCachedSize, (__psint_t)wl_le1m }, +/* allservers.requests.cached.size.le3m */ + { wl_requestCachedSize, (__psint_t)wl_le3m }, +/* allservers.requests.cached.size.gt3m */ + { wl_requestCachedSize, (__psint_t)wl_gt3m }, +/* allservers.requests.cached.size.unknown */ + { wl_requestCachedSize, (__psint_t)wl_unknownSize }, +/* allservers.requests.uncached.total */ + { wl_totalAggregate, (__psint_t)5 }, +/* allservers.requests.uncached.size.zero */ + { wl_requestUncachedSize, (__psint_t)wl_zero }, +/* allservers.requests.uncached.size.le3k */ + { wl_requestUncachedSize, (__psint_t)wl_le3k }, +/* allservers.requests.uncached.size.le10k */ + { wl_requestUncachedSize, (__psint_t)wl_le10k }, +/* allservers.requests.uncached.size.le30k */ + { wl_requestUncachedSize, (__psint_t)wl_le30k }, +/* allservers.requests.uncached.size.le100k */ + { wl_requestUncachedSize, (__psint_t)wl_le100k }, +/* allservers.requests.uncached.size.le300k */ + { wl_requestUncachedSize, (__psint_t)wl_le300k }, +/* allservers.requests.uncached.size.le1m */ + { wl_requestUncachedSize, (__psint_t)wl_le1m }, +/* allservers.requests.uncached.size.le3m */ + { wl_requestUncachedSize, (__psint_t)wl_le3m }, +/* allservers.requests.uncached.size.gt3m */ + { wl_requestUncachedSize, (__psint_t)wl_gt3m }, +/* allservers.requests.uncached.size.unknown */ + { wl_requestUncachedSize, (__psint_t)wl_unknownSize }, +/* allservers.bytes.size.zero */ + { wl_bytesSize, (__psint_t)wl_zero }, +/* allservers.bytes.size.le3k */ + { wl_bytesSize, (__psint_t)wl_le3k }, +/* allservers.bytes.size.le10k */ + { wl_bytesSize, (__psint_t)wl_le10k }, +/* allservers.bytes.size.le30k */ + { wl_bytesSize, (__psint_t)wl_le30k }, +/* allservers.bytes.size.le100k */ + { wl_bytesSize, (__psint_t)wl_le100k }, +/* allservers.bytes.size.le300k */ + { wl_bytesSize, (__psint_t)wl_le300k }, +/* allservers.bytes.size.le1m */ + { wl_bytesSize, (__psint_t)wl_le1m }, +/* allservers.bytes.size.le3m */ + { wl_bytesSize, (__psint_t)wl_le3m }, +/* allservers.bytes.size.gt3m */ + { wl_bytesSize, (__psint_t)wl_gt3m }, +/* allservers.bytes.cached.total */ + { wl_totalAggregate, (__psint_t)6 }, +/* allservers.bytes.cached.size.zero */ + { wl_bytesCachedSize, (__psint_t)wl_zero }, +/* allservers.bytes.cached.size.le3k */ + { wl_bytesCachedSize, (__psint_t)wl_le3k }, +/* allservers.bytes.cached.size.le10k */ + { wl_bytesCachedSize, (__psint_t)wl_le10k }, +/* allservers.bytes.cached.size.le30k */ + { wl_bytesCachedSize, (__psint_t)wl_le30k }, +/* allservers.bytes.cached.size.le100k */ + { wl_bytesCachedSize, (__psint_t)wl_le100k }, +/* allservers.bytes.cached.size.le300k */ + { wl_bytesCachedSize, (__psint_t)wl_le300k }, +/* allservers.bytes.cached.size.le1m */ + { wl_bytesCachedSize, (__psint_t)wl_le1m }, +/* allservers.bytes.cached.size.le3m */ + { wl_bytesCachedSize, (__psint_t)wl_le3m }, +/* allservers.bytes.cached.size.gt3m */ + { wl_bytesCachedSize, (__psint_t)wl_gt3m }, +/* allservers.bytes.uncached.total */ + { wl_totalAggregate, (__psint_t)7 }, +/* allservers.bytes.uncached.size.zero */ + { wl_bytesUncachedSize, (__psint_t)wl_zero }, +/* allservers.bytes.uncached.size.le3k */ + { wl_bytesUncachedSize, (__psint_t)wl_le3k }, +/* allservers.bytes.uncached.size.le10k */ + { wl_bytesUncachedSize, (__psint_t)wl_le10k }, +/* allservers.bytes.uncached.size.le30k */ + { wl_bytesUncachedSize, (__psint_t)wl_le30k }, +/* allservers.bytes.uncached.size.le100k */ + { wl_bytesUncachedSize, (__psint_t)wl_le100k }, +/* allservers.bytes.uncached.size.le300k */ + { wl_bytesUncachedSize, (__psint_t)wl_le300k }, +/* allservers.bytes.uncached.size.le1m */ + { wl_bytesUncachedSize, (__psint_t)wl_le1m }, +/* allservers.bytes.uncached.size.le3m */ + { wl_bytesUncachedSize, (__psint_t)wl_le3m }, +/* allservers.bytes.uncached.size.gt3m */ + { wl_bytesUncachedSize, (__psint_t)wl_gt3m }, +/* perserver.watched */ + { wl_watched, (__psint_t)&dummyCount.active }, +/* perserver.numlogs */ + { wl_offset32, (__psint_t)&dummyCount.numLogs }, +/* perserver.errors */ + { wl_offset32, (__psint_t)&dummyCount.errors }, +/* perserver.requests.total */ + { wl_serverAggregate, (__psint_t)0 }, +/* perserver.requests.get */ + { wl_requestMethod, (__psint_t)wl_httpGet }, +/* perserver.requests.head */ + { wl_requestMethod, (__psint_t)wl_httpHead }, +/* perserver.requests.post */ + { wl_requestMethod, (__psint_t)wl_httpPost }, +/* perserver.requests.other */ + { wl_requestMethod, (__psint_t)wl_httpOther }, +/* perserver.bytes.total */ + { wl_serverAggregate, (__psint_t)1 }, +/* perserver.bytes.get */ + { wl_bytesMethod, (__psint_t)wl_httpGet }, +/* perserver.bytes.head */ + { wl_bytesMethod, (__psint_t)wl_httpHead }, +/* perserver.bytes.post */ + { wl_bytesMethod, (__psint_t)wl_httpPost }, +/* perserver.bytes.other */ + { wl_bytesMethod, (__psint_t)wl_httpOther }, +/* perserver.requests.size.zero */ + { wl_requestSize, (__psint_t)wl_zero }, +/* perserver.requests.size.le3k */ + { wl_requestSize, (__psint_t)wl_le3k }, +/* perserver.requests.size.le10k */ + { wl_requestSize, (__psint_t)wl_le10k }, +/* perserver.requests.size.le30k */ + { wl_requestSize, (__psint_t)wl_le30k }, +/* perserver.requests.size.le100k */ + { wl_requestSize, (__psint_t)wl_le100k }, +/* perserver.requests.size.le300k */ + { wl_requestSize, (__psint_t)wl_le300k }, +/* perserver.requests.size.le1m */ + { wl_requestSize, (__psint_t)wl_le1m }, +/* perserver.requests.size.le3m */ + { wl_requestSize, (__psint_t)wl_le3m }, +/* perserver.requests.size.gt3m */ + { wl_requestSize, (__psint_t)wl_gt3m }, +/* perserver.requests.size.unknown */ + { wl_requestSize, (__psint_t)wl_unknownSize }, +/* perserver.requests.client.total */ + { wl_serverAggregate, (__psint_t)2 }, +/* perserver.requests.cached.total */ + { wl_serverAggregate, (__psint_t)3 }, +/* perserver.requests.cached.size.zero */ + { wl_requestCachedSize, (__psint_t)wl_zero }, +/* perserver.requests.cached.size.le3k */ + { wl_requestCachedSize, (__psint_t)wl_le3k }, +/* perserver.requests.cached.size.le10k */ + { wl_requestCachedSize, (__psint_t)wl_le10k }, +/* perserver.requests.cached.size.le30k */ + { wl_requestCachedSize, (__psint_t)wl_le30k }, +/* perserver.requests.cached.size.le100k */ + { wl_requestCachedSize, (__psint_t)wl_le100k }, +/* perserver.requests.cached.size.le300k */ + { wl_requestCachedSize, (__psint_t)wl_le300k }, +/* perserver.requests.cached.size.le1m */ + { wl_requestCachedSize, (__psint_t)wl_le1m }, +/* perserver.requests.cached.size.le3m */ + { wl_requestCachedSize, (__psint_t)wl_le3m }, +/* perserver.requests.cached.size.gt3m */ + { wl_requestCachedSize, (__psint_t)wl_gt3m }, +/* perserver.requests.cached.size.unknown */ + { wl_requestCachedSize, (__psint_t)wl_unknownSize }, +/* perserver.requests.uncached.total */ + { wl_serverAggregate, (__psint_t)4 }, +/* perserver.requests.uncached.size.zero */ + { wl_requestUncachedSize, (__psint_t)wl_zero }, +/* perserver.requests.uncached.size.le3k */ + { wl_requestUncachedSize, (__psint_t)wl_le3k }, +/* perserver.requests.uncached.size.le10k */ + { wl_requestUncachedSize, (__psint_t)wl_le10k }, +/* perserver.requests.uncached.size.le30k */ + { wl_requestUncachedSize, (__psint_t)wl_le30k }, +/* perserver.requests.uncached.size.le100k */ + { wl_requestUncachedSize, (__psint_t)wl_le100k }, +/* perserver.requests.uncached.size.le300k */ + { wl_requestUncachedSize, (__psint_t)wl_le300k }, +/* perserver.requests.uncached.size.le1m */ + { wl_requestUncachedSize, (__psint_t)wl_le1m }, +/* perserver.requests.uncached.size.le3m */ + { wl_requestUncachedSize, (__psint_t)wl_le3m }, +/* perserver.requests.uncached.size.gt3m */ + { wl_requestUncachedSize, (__psint_t)wl_gt3m }, +/* perserver.requests.uncached.size.unknown */ + { wl_requestUncachedSize, (__psint_t)wl_unknownSize }, +/* perserver.bytes.size.zero */ + { wl_bytesSize, (__psint_t)wl_zero }, +/* perserver.bytes.size.le3k */ + { wl_bytesSize, (__psint_t)wl_le3k }, +/* perserver.bytes.size.le10k */ + { wl_bytesSize, (__psint_t)wl_le10k }, +/* perserver.bytes.size.le30k */ + { wl_bytesSize, (__psint_t)wl_le30k }, +/* perserver.bytes.size.le100k */ + { wl_bytesSize, (__psint_t)wl_le100k }, +/* perserver.bytes.size.le300k */ + { wl_bytesSize, (__psint_t)wl_le300k }, +/* perserver.bytes.size.le1m */ + { wl_bytesSize, (__psint_t)wl_le1m }, +/* perserver.bytes.size.le3m */ + { wl_bytesSize, (__psint_t)wl_le3m }, +/* perserver.bytes.size.gt3m */ + { wl_bytesSize, (__psint_t)wl_gt3m }, +/* perserver.bytes.cached.total */ + { wl_serverAggregate, (__psint_t)5 }, +/* perserver.bytes.cached.size.zero */ + { wl_bytesCachedSize, (__psint_t)wl_zero }, +/* perserver.bytes.cached.size.le3k */ + { wl_bytesCachedSize, (__psint_t)wl_le3k }, +/* perserver.bytes.cached.size.le10k */ + { wl_bytesCachedSize, (__psint_t)wl_le10k }, +/* perserver.bytes.cached.size.le30k */ + { wl_bytesCachedSize, (__psint_t)wl_le30k }, +/* perserver.bytes.cached.size.le100k */ + { wl_bytesCachedSize, (__psint_t)wl_le100k }, +/* perserver.bytes.cached.size.le300k */ + { wl_bytesCachedSize, (__psint_t)wl_le300k }, +/* perserver.bytes.cached.size.le1m */ + { wl_bytesCachedSize, (__psint_t)wl_le1m }, +/* perserver.bytes.cached.size.le3m */ + { wl_bytesCachedSize, (__psint_t)wl_le3m }, +/* perserver.bytes.cached.size.gt3m */ + { wl_bytesCachedSize, (__psint_t)wl_gt3m }, +/* perserver.bytes.uncached.total */ + { wl_serverAggregate, (__psint_t)6 }, +/* perserver.bytes.uncached.size.zero */ + { wl_bytesUncachedSize, (__psint_t)wl_zero }, +/* perserver.bytes.uncached.size.le3k */ + { wl_bytesUncachedSize, (__psint_t)wl_le3k }, +/* perserver.bytes.uncached.size.le10k */ + { wl_bytesUncachedSize, (__psint_t)wl_le10k }, +/* perserver.bytes.uncached.size.le30k */ + { wl_bytesUncachedSize, (__psint_t)wl_le30k }, +/* perserver.bytes.uncached.size.le100k */ + { wl_bytesUncachedSize, (__psint_t)wl_le100k }, +/* perserver.bytes.uncached.size.le300k */ + { wl_bytesUncachedSize, (__psint_t)wl_le300k }, +/* perserver.bytes.uncached.size.le1m */ + { wl_bytesUncachedSize, (__psint_t)wl_le1m }, +/* perserver.bytes.uncached.size.le3m */ + { wl_bytesUncachedSize, (__psint_t)wl_le3m }, +/* perserver.bytes.uncached.size.gt3m */ + { wl_bytesUncachedSize, (__psint_t)wl_gt3m }, +/* perserver.logidletime */ + { wl_offset32, (__psint_t)&dummyCount.modTime }, +}; + +/* + * all metrics supported in this PMDA - one table entry for each + */ + +static pmdaMetric wl_metrics[] = { + +/* + * web.config + */ + +/* config.numservers */ +{ (void *)0, + { PMDA_PMID(0,0), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(0, 0, 1, 0, 0, 0) } }, + +/* config.catchup */ +{ (void *)0, + { PMDA_PMID(0,1), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(0, 1, 0, 0, PM_TIME_SEC, 0) } }, + +/* config.catchuptime */ +{ (void *)0, + { PMDA_PMID(0,2), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(0, 1, 0, 0, PM_TIME_SEC, 0) } }, + +/* config.check */ +{ (void *)0, + { PMDA_PMID(0,3), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(0, 1, 0, 0, PM_TIME_SEC, 0) } }, + +/* + * web.allservers + */ + +/* allserves.numwatched */ +{ (void *)0, + { PMDA_PMID(0,4), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(0, 0, 1, 0, 0, 0) } }, + +/* allserves.numalive */ +{ (void *)0, + { PMDA_PMID(0,5), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(0, 0, 1, 0, 0, 0) } }, + +/* allservers.errors */ +{ (void *)0, + { PMDA_PMID(1,6), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* allservers.requests.total */ +{ (void *)0, + { PMDA_PMID(1,7), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* allservers.requests.get */ +{ (void *)0, + { PMDA_PMID(1,8), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* allservers.requests.head */ +{ (void *)0, + { PMDA_PMID(1,9), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* allservers.requests.post */ +{ (void *)0, + { PMDA_PMID(1,10), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* allservers.requests.other */ +{ (void *)0, + { PMDA_PMID(1,11), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* allservers.bytes.total */ +{ (void *)0, + { PMDA_PMID(1,12), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) } }, + +/* allservers.bytes.get */ +{ (void *)0, + { PMDA_PMID(1,13), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) } }, + +/* allservers.bytes.head */ +{ (void *)0, + { PMDA_PMID(1,14), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) } }, + +/* allservers.bytes.post */ +{ (void *)0, + { PMDA_PMID(1,15), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) } }, + +/* allservers.bytes.other */ +{ (void *)0, + { PMDA_PMID(1,16), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) } }, + +/* allservers.requests.size.zero */ +{ (void *)0, + { PMDA_PMID(1,17), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* allservers.requests.size.le3k */ +{ (void *)0, + { PMDA_PMID(1,18), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* allservers.requests.size.le10k */ +{ (void *)0, + { PMDA_PMID(1,19), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* allservers.requests.size.le30k */ +{ (void *)0, + { PMDA_PMID(1,20), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* allservers.requests.size.le100k */ +{ (void *)0, + { PMDA_PMID(1,21), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* allservers.requests.size.le300k */ +{ (void *)0, + { PMDA_PMID(1,22), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* allservers.requests.size.le1m */ +{ (void *)0, + { PMDA_PMID(1,23), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* allservers.requests.size.le3m */ +{ (void *)0, + { PMDA_PMID(1,24), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* allservers.requests.size.gt3m */ +{ (void *)0, + { PMDA_PMID(1,25), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* allservers.requests.size.unknown */ +{ (void *)0, + { PMDA_PMID(1,66), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* allservers.requests.client.total */ +{ (void *)0, + { PMDA_PMID(3,1), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* allservers.requests.cached.total */ +{ (void *)0, + { PMDA_PMID(3,11), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* allservers.requests.cached.size.zero */ +{ (void *)0, + { PMDA_PMID(3,12), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* allservers.requests.cached.size.le3k */ +{ (void *)0, + { PMDA_PMID(3,13), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* allservers.requests.cached.size.le10k */ +{ (void *)0, + { PMDA_PMID(3,14), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* allservers.requests.cached.size.le30k */ +{ (void *)0, + { PMDA_PMID(3,15), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* allservers.requests.cached.size.le100k */ +{ (void *)0, + { PMDA_PMID(3,16), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* allservers.requests.cached.size.le300k */ +{ (void *)0, + { PMDA_PMID(3,17), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* allservers.requests.cached.size.le1m */ +{ (void *)0, + { PMDA_PMID(3,18), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* allservers.requests.cached.size.le3m */ +{ (void *)0, + { PMDA_PMID(3,19), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* allservers.requests.cached.size.gt3m */ +{ (void *)0, + { PMDA_PMID(3,20), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* allservers.requests.cached.size.unknown */ +{ (void *)0, + { PMDA_PMID(3,21), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* allservers.requests.uncached.total */ +{ (void *)0, + { PMDA_PMID(3,31), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* allservers.requests.uncached.size.zero */ +{ (void *)0, + { PMDA_PMID(3,32), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* allservers.requests.uncached.size.le3k */ +{ (void *)0, + { PMDA_PMID(3,33), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* allservers.requests.uncached.size.le10k */ +{ (void *)0, + { PMDA_PMID(3,34), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* allservers.requests.uncached.size.le30k */ +{ (void *)0, + { PMDA_PMID(3,35), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* allservers.requests.uncached.size.le100k */ +{ (void *)0, + { PMDA_PMID(3,36), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* allservers.requests.uncached.size.le300k */ +{ (void *)0, + { PMDA_PMID(3,37), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* allservers.requests.uncached.size.le1m */ +{ (void *)0, + { PMDA_PMID(3,38), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* allservers.requests.uncached.size.le3m */ +{ (void *)0, + { PMDA_PMID(3,39), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* allservers.requests.uncached.size.gt3m */ +{ (void *)0, + { PMDA_PMID(3,40), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* allservers.requests.uncached.size.unknown */ +{ (void *)0, + { PMDA_PMID(3,41), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* allservers.bytes.size.zero */ +{ (void *)0, + { PMDA_PMID(1,26), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) } }, + +/* allservers.bytes.size.le3k */ +{ (void *)0, + { PMDA_PMID(1,27), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) } }, + +/* allservers.bytes.size.le10k */ +{ (void *)0, + { PMDA_PMID(1,28), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) } }, + +/* allservers.bytes.size.le30k */ +{ (void *)0, + { PMDA_PMID(1,29), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) } }, + +/* allservers.bytes.size.le100k */ +{ (void *)0, + { PMDA_PMID(1,30), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) } }, + +/* allservers.bytes.size.le300k */ +{ (void *)0, + { PMDA_PMID(1,31), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) } }, + +/* allservers.bytes.size.le1m */ +{ (void *)0, + { PMDA_PMID(1,32), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) } }, + +/* allservers.bytes.size.le3m */ +{ (void *)0, + { PMDA_PMID(1,33), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) } }, + +/* allservers.bytes.size.gt3m */ +{ (void *)0, + { PMDA_PMID(1,34), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) } }, + +/* allservers.bytes.cached.total */ +{ (void *)0, + { PMDA_PMID(3,51), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) } }, + +/* allservers.bytes.cached.size.zero */ +{ (void *)0, + { PMDA_PMID(3,52), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) } }, + +/* allservers.bytes.cached.size.le3k */ +{ (void *)0, + { PMDA_PMID(3,53), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) } }, + +/* allservers.bytes.cached.size.le10k */ +{ (void *)0, + { PMDA_PMID(3,54), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) } }, + +/* allservers.bytes.cached.size.le30k */ +{ (void *)0, + { PMDA_PMID(3,55), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) } }, + +/* allservers.bytes.cached.size.le100k */ +{ (void *)0, + { PMDA_PMID(3,56), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) } }, + +/* allservers.bytes.cached.size.le300k */ +{ (void *)0, + { PMDA_PMID(3,57), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) } }, + +/* allservers.bytes.cached.size.le1m */ +{ (void *)0, + { PMDA_PMID(3,58), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) } }, + +/* allservers.bytes.cached.size.le3m */ +{ (void *)0, + { PMDA_PMID(3,59), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) } }, + +/* allservers.bytes.cached.size.gt3m */ +{ (void *)0, + { PMDA_PMID(3,60), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) } }, + +/* allservers.bytes.uncached.total */ +{ (void *)0, + { PMDA_PMID(3,71), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) } }, + +/* allservers.bytes.uncached.size.zero */ +{ (void *)0, + { PMDA_PMID(3,72), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) } }, + +/* allservers.bytes.uncached.size.le3k */ +{ (void *)0, + { PMDA_PMID(3,73), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) } }, + +/* allservers.bytes.uncached.size.le10k */ +{ (void *)0, + { PMDA_PMID(3,74), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) } }, + +/* allservers.bytes.uncached.size.le30k */ +{ (void *)0, + { PMDA_PMID(3,75), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) } }, + +/* allservers.bytes.uncached.size.le100k */ +{ (void *)0, + { PMDA_PMID(3,76), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) } }, + +/* allservers.bytes.uncached.size.le300k */ +{ (void *)0, + { PMDA_PMID(3,77), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) } }, + +/* allservers.bytes.uncached.size.le1m */ +{ (void *)0, + { PMDA_PMID(3,78), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) } }, + +/* allservers.bytes.uncached.size.le3m */ +{ (void *)0, + { PMDA_PMID(3,79), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) } }, + +/* allservers.bytes.uncached.size.gt3m */ +{ (void *)0, + { PMDA_PMID(3,80), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) } }, + +/* + * web.perserver + */ + +/* perserver.watched */ +{ (void *)0, + { PMDA_PMID(0,35), PM_TYPE_U32, WEBLOG_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) } }, + +/* perserver.numlogs */ +{ (void *)0, + { PMDA_PMID(2,36), PM_TYPE_U32, WEBLOG_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* perserver.errors */ +{ (void *)0, + { PMDA_PMID(2,37), PM_TYPE_U32, WEBLOG_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* perserver.requests.total */ +{ (void *)0, + { PMDA_PMID(2,38), PM_TYPE_U32, WEBLOG_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* perserver.requests.get */ +{ (void *)0, + { PMDA_PMID(2,39), PM_TYPE_U32, WEBLOG_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* perserver.requests.head */ +{ (void *)0, + { PMDA_PMID(2,40), PM_TYPE_U32, WEBLOG_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* perserver.requests.post */ +{ (void *)0, + { PMDA_PMID(2,41), PM_TYPE_U32, WEBLOG_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* perserver.requests.other */ +{ (void *)0, + { PMDA_PMID(2,42), PM_TYPE_U32, WEBLOG_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* perserver.bytes.total */ +{ (void *)0, + { PMDA_PMID(2,43), PM_TYPE_U64, WEBLOG_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) } }, + +/* perserver.bytes.get */ +{ (void *)0, + { PMDA_PMID(2,44), PM_TYPE_U64, WEBLOG_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) } }, + +/* perserver.bytes.head */ +{ (void *)0, + { PMDA_PMID(2,45), PM_TYPE_U64, WEBLOG_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) } }, + +/* perserver.bytes.post */ +{ (void *)0, + { PMDA_PMID(2,46), PM_TYPE_U64, WEBLOG_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) } }, + +/* perserver.bytes.other */ +{ (void *)0, + { PMDA_PMID(2,47), PM_TYPE_U64, WEBLOG_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) } }, + +/* perserver.requests.size.zero */ +{ (void *)0, + { PMDA_PMID(2,48), PM_TYPE_U32, WEBLOG_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* perserver.requests.size.le3k */ +{ (void *)0, + { PMDA_PMID(2,49), PM_TYPE_U32, WEBLOG_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* perserver.requests.size.le10k */ +{ (void *)0, + { PMDA_PMID(2,50), PM_TYPE_U32, WEBLOG_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* perserver.requests.size.le30k */ +{ (void *)0, + { PMDA_PMID(2,51), PM_TYPE_U32, WEBLOG_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* perserver.requests.size.le100k */ +{ (void *)0, + { PMDA_PMID(2,52), PM_TYPE_U32, WEBLOG_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* perserver.requests.size.le300k */ +{ (void *)0, + { PMDA_PMID(2,53), PM_TYPE_U32, WEBLOG_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* perserver.requests.size.le1m */ +{ (void *)0, + { PMDA_PMID(2,54), PM_TYPE_U32, WEBLOG_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* perserver.requests.size.le3m */ +{ (void *)0, + { PMDA_PMID(2,55), PM_TYPE_U32, WEBLOG_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* perserver.requests.size.gt3m */ +{ (void *)0, + { PMDA_PMID(2,56), PM_TYPE_U32, WEBLOG_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* perserver.requests.size.unknown */ +{ (void *)0, + { PMDA_PMID(2,67), PM_TYPE_U32, WEBLOG_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* perserver.requests.client.total */ +{ (void *)0, + { PMDA_PMID(4,1), PM_TYPE_U32, WEBLOG_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* perserver.requests.cached.total */ +{ (void *)0, + { PMDA_PMID(4,11), PM_TYPE_U32, WEBLOG_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* perserver.requests.cached.size.zero */ +{ (void *)0, + { PMDA_PMID(4,12), PM_TYPE_U32, WEBLOG_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* perserver.requests.cached.size.le3k */ +{ (void *)0, + { PMDA_PMID(4,13), PM_TYPE_U32, WEBLOG_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* perserver.requests.cached.size.le10k */ +{ (void *)0, + { PMDA_PMID(4,14), PM_TYPE_U32, WEBLOG_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* perserver.requests.cached.size.le30k */ +{ (void *)0, + { PMDA_PMID(4,15), PM_TYPE_U32, WEBLOG_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* perserver.requests.cached.size.le100k */ +{ (void *)0, + { PMDA_PMID(4,16), PM_TYPE_U32, WEBLOG_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* perserver.requests.cached.size.le300k */ +{ (void *)0, + { PMDA_PMID(4,17), PM_TYPE_U32, WEBLOG_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* perserver.requests.cached.size.le1m */ +{ (void *)0, + { PMDA_PMID(4,18), PM_TYPE_U32, WEBLOG_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* perserver.requests.cached.size.le3m */ +{ (void *)0, + { PMDA_PMID(4,19), PM_TYPE_U32, WEBLOG_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* perserver.requests.cached.size.gt3m */ +{ (void *)0, + { PMDA_PMID(4,20), PM_TYPE_U32, WEBLOG_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* perserver.requests.cached.size.unknown */ +{ (void *)0, + { PMDA_PMID(4,21), PM_TYPE_U32, WEBLOG_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* perserver.requests.uncached.total */ +{ (void *)0, + { PMDA_PMID(4,31), PM_TYPE_U32, WEBLOG_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* perserver.requests.uncached.size.zero */ +{ (void *)0, + { PMDA_PMID(4,32), PM_TYPE_U32, WEBLOG_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* perserver.requests.uncached.size.le3k */ +{ (void *)0, + { PMDA_PMID(4,33), PM_TYPE_U32, WEBLOG_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* perserver.requests.uncached.size.le10k */ +{ (void *)0, + { PMDA_PMID(4,34), PM_TYPE_U32, WEBLOG_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* perserver.requests.uncached.size.le30k */ +{ (void *)0, + { PMDA_PMID(4,35), PM_TYPE_U32, WEBLOG_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* perserver.requests.uncached.size.le100k */ +{ (void *)0, + { PMDA_PMID(4,36), PM_TYPE_U32, WEBLOG_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* perserver.requests.uncached.size.le300k */ +{ (void *)0, + { PMDA_PMID(4,37), PM_TYPE_U32, WEBLOG_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* perserver.requests.uncached.size.le1m */ +{ (void *)0, + { PMDA_PMID(4,38), PM_TYPE_U32, WEBLOG_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* perserver.requests.uncached.size.le3m */ +{ (void *)0, + { PMDA_PMID(4,39), PM_TYPE_U32, WEBLOG_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* perserver.requests.uncached.size.gt3m */ +{ (void *)0, + { PMDA_PMID(4,40), PM_TYPE_U32, WEBLOG_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* perserver.requests.uncached.size.unknown */ +{ (void *)0, + { PMDA_PMID(4,41), PM_TYPE_U32, WEBLOG_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* perserver.bytes.size.zero */ +{ (void *)0, + { PMDA_PMID(2,57), PM_TYPE_U64, WEBLOG_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) } }, + +/* perserver.bytes.size.le3k */ +{ (void *)0, + { PMDA_PMID(2,58), PM_TYPE_U64, WEBLOG_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) } }, + +/* perserver.bytes.size.le10k */ +{ (void *)0, + { PMDA_PMID(2,59), PM_TYPE_U64, WEBLOG_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) } }, + +/* perserver.bytes.size.le30k */ +{ (void *)0, + { PMDA_PMID(2,60), PM_TYPE_U64, WEBLOG_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) } }, + +/* perserver.bytes.size.le100k */ +{ (void *)0, + { PMDA_PMID(2,61), PM_TYPE_U64, WEBLOG_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) } }, + +/* perserver.bytes.size.le300k */ +{ (void *)0, + { PMDA_PMID(2,62), PM_TYPE_U64, WEBLOG_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) } }, + +/* perserver.bytes.size.le1m */ +{ (void *)0, + { PMDA_PMID(2,63), PM_TYPE_U64, WEBLOG_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) } }, + +/* perserver.bytes.size.le3m */ +{ (void *)0, + { PMDA_PMID(2,64), PM_TYPE_U64, WEBLOG_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) } }, + +/* perserver.bytes.size.gt3m */ +{ (void *)0, + { PMDA_PMID(2,65), PM_TYPE_U64, WEBLOG_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) } }, + +/* perserver.bytes.cached.total */ +{ (void *)0, + { PMDA_PMID(4,51), PM_TYPE_U64, WEBLOG_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) } }, + +/* perserver.bytes.cached.size.zero */ +{ (void *)0, + { PMDA_PMID(4,52), PM_TYPE_U64, WEBLOG_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) } }, + +/* perserver.bytes.cached.size.le3k */ +{ (void *)0, + { PMDA_PMID(4,53), PM_TYPE_U64, WEBLOG_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) } }, + +/* perserver.bytes.cached.size.le10k */ +{ (void *)0, + { PMDA_PMID(4,54), PM_TYPE_U64, WEBLOG_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) } }, + +/* perserver.bytes.cached.size.le30k */ +{ (void *)0, + { PMDA_PMID(4,55), PM_TYPE_U64, WEBLOG_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) } }, + +/* perserver.bytes.cached.size.le100k */ +{ (void *)0, + { PMDA_PMID(4,56), PM_TYPE_U64, WEBLOG_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) } }, + +/* perserver.bytes.cached.size.le300k */ +{ (void *)0, + { PMDA_PMID(4,57), PM_TYPE_U64, WEBLOG_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) } }, + +/* perserver.bytes.cached.size.le1m */ +{ (void *)0, + { PMDA_PMID(4,58), PM_TYPE_U64, WEBLOG_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) } }, + +/* perserver.bytes.cached.size.le3m */ +{ (void *)0, + { PMDA_PMID(4,59), PM_TYPE_U64, WEBLOG_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) } }, + +/* perserver.bytes.cached.size.gt3m */ +{ (void *)0, + { PMDA_PMID(4,60), PM_TYPE_U64, WEBLOG_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) } }, + +/* perserver.bytes.uncached.total */ +{ (void *)0, + { PMDA_PMID(4,71), PM_TYPE_U64, WEBLOG_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) } }, + +/* perserver.bytes.uncached.size.zero */ +{ (void *)0, + { PMDA_PMID(4,72), PM_TYPE_U64, WEBLOG_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) } }, + +/* perserver.bytes.uncached.size.le3k */ +{ (void *)0, + { PMDA_PMID(4,73), PM_TYPE_U64, WEBLOG_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) } }, + +/* perserver.bytes.uncached.size.le10k */ +{ (void *)0, + { PMDA_PMID(4,74), PM_TYPE_U64, WEBLOG_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) } }, + +/* perserver.bytes.uncached.size.le30k */ +{ (void *)0, + { PMDA_PMID(4,75), PM_TYPE_U64, WEBLOG_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) } }, + +/* perserver.bytes.uncached.size.le100k */ +{ (void *)0, + { PMDA_PMID(4,76), PM_TYPE_U64, WEBLOG_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) } }, + +/* perserver.bytes.uncached.size.le300k */ +{ (void *)0, + { PMDA_PMID(4,77), PM_TYPE_U64, WEBLOG_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) } }, + +/* perserver.bytes.uncached.size.le1m */ +{ (void *)0, + { PMDA_PMID(4,78), PM_TYPE_U64, WEBLOG_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) } }, + +/* perserver.bytes.uncached.size.le3m */ +{ (void *)0, + { PMDA_PMID(4,79), PM_TYPE_U64, WEBLOG_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) } }, + +/* perserver.bytes.uncached.size.gt3m */ +{ (void *)0, + { PMDA_PMID(4,80), PM_TYPE_U64, WEBLOG_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) } }, +/* + * Added in PCPWEB 1.1.1 + */ + +/* perserver.logidletime */ +{ (void *)0, + { PMDA_PMID(2,68), PM_TYPE_U32, WEBLOG_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(0, 1, 0, 0, PM_TIME_SEC, 0) } }, + +}; + +/* number of metrics */ +static int numMetrics = (sizeof(wl_metrics)/sizeof(wl_metrics[0])); + +/* number of instance domains */ +static int numIndoms = sizeof(wl_indomTable)/sizeof(wl_indomTable[0]); + +/* mask to get the cluster from a PMID */ +static int _clusterMask = (1<<22) - (1<<10); + +/* refresh all logs if wl_updateAll != 0 */ +int wl_updateAll = 0; + +/* time in milliseconds to update all the logs */ +__uint32_t wl_catchupTime = 0; + +/* time log refresh was started */ +time_t wl_timeOfRefresh = 0; + +/* flag to indicate DSO or Daemon */ +int wl_isDSO = 0; + +/* request size categories */ +long wl_sizes[] = { + 0, 3*1024, 10*1024, 30*1024, 100*1024, 300*1024, 1024*1024, 3*1024*1024, 0 +}; + +#define BUFFER_LEN 2048 + +static pmdaExt *extp; /* set in web_init() */ + +#ifdef HAVE_SIGHUP +/* + * Signal handler for an sproc receiving TERM (probably from parent) + */ +static void +onhup(int s) +{ + _exit(s != SIGHUP); +} +#endif + +/* + * Replacement for fgets using the FileInfo structure + */ + +int +wl_gets(FileInfo *fip, char **line) +{ + char *p; + int nch; + int sts; + + if (fip->filePtr < 0) { + return -1; + } + + p = fip->bp; + +more: + while (p < fip->bend) { + if (*p == '\n') { + /* newline, we are done */ + *p++ = '\0'; + *line = fip->bp; + fip->bp = p; + return p - *line; + } + p++; + } + + /* out the end of the buffer, and no newline */ + nch = fip->bend - fip->bp; + if (nch == FIBUFSIZE) { + /* buffer full, and no newline! ... truncate and return */ + fip->buf[FIBUFSIZE-1] = '\n'; + p = &fip->buf[FIBUFSIZE-1]; + goto more; + } + if (nch) + /* shuffle partial line to start of buffer */ + memcpy(fip->buf, fip->bp, nch); + fip->bp = fip->buf; + fip->bend = &fip->buf[nch]; + + /* refill */ + sts = read(fip->filePtr, fip->bend, FIBUFSIZE-nch); + if (sts <= 0) { + /* no more, either terminate last line, or really return status */ + if (nch) { + *fip->bend = '\n'; + sts = 1; + } + else { + return sts; + } + } + p = fip->bend; + fip->bend = &fip->bend[sts]; + goto more; +} + +/* + * Open a log file and seek to the end + */ + +int +openLogFile(FileInfo *theFile) +{ + int diff = theFile->filePtr; + char *line = (char *)0; + + theFile->filePtr = open(theFile->fileName, O_RDONLY); + + if (theFile->filePtr == -1) { + if (theFile->filePtr != diff) { + logmessage(LOG_ERR, "openLogFile: open %s: %s\n", + theFile->fileName, osstrerror()); + } + return -1; + } + + if (fstat(theFile->filePtr, &(theFile->fileStat)) < 0) { + logmessage(LOG_ERR, "openLogFile: stat for %s: %s\n", + theFile->fileName, osstrerror()); + wl_close(theFile->filePtr); + return -1; + } + + logmessage(LOG_INFO, "%s opened (fd=%d, inode=%d)\n", + theFile->fileName, + theFile->filePtr, + theFile->fileStat.st_ino); + + /* throw away last line in file */ + if (theFile->fileStat.st_size != 0) { + lseek(theFile->filePtr, -2L, SEEK_END); + wl_gets(theFile, &line); + } + + if (fstat(theFile->filePtr, &(theFile->fileStat)) < 0) { + logmessage(LOG_ERR, "openLogFile: update stat for %s: %s\n", + theFile->fileName, osstrerror()); + wl_close(theFile->filePtr); + return -1; + } + +/* +* Check and warn if a log file has not been modified in the last 24 hours, +* as this may indicate something is wrong with the PMDA's configuration, +* or the Web server's configuration +*/ + + diff = time((time_t*)0) - theFile->fileStat.st_mtime; + if (diff > DORMANT_WARN) { + logmessage(LOG_WARNING, + "log file %s has not been modified for at least %d days", + theFile->fileName, + diff / DORMANT_WARN); + } + + return 0; +} + +/* + * Check the log file is still the correct log file. + * + * If the file has not been modified in wl_chkDelay seconds, then reopen + * the file and compare the inodes. + * Otherwise the current inode and size of the file are checked. + * + * Returns a LogFileCode indicating the status of the log file. + */ + +static int +checkLogFile(FileInfo *theFile, + struct stat *tmpStat) +{ + int tmpFd = -1; + int result = wl_ok; + +/* + * File is closed, if enough time has elasped since last attempt, try to + * open it + */ + + if (theFile->filePtr < 0) + { + if (wl_timeOfRefresh - theFile->lastActive > wl_chkDelay) + { + theFile->lastActive = wl_timeOfRefresh; + if (openLogFile(theFile) < 0) + result = wl_unableToOpen; + else + result = wl_opened; + } + else + result = wl_closed; + } + +/* Get the file stat info on the open file */ + + if (theFile->filePtr >= 0) { + if (fstat(theFile->filePtr, tmpStat) < 0) { + logmessage(LOG_ERR, "checkLogFile: stat on open %s: %s\n", + theFile->fileName, osstrerror()); + wl_close(theFile->filePtr); + result = wl_unableToStat; + +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) { + logmessage(LOG_DEBUG, + "checkLogFile: could not stat %s\n", + theFile->fileName); + } +#endif + + } + } + +/* + * Check that we are dealing with a regular file. If is a character + * device or directory etc just ignore it. + */ + + if (result == wl_ok && !(theFile->fileStat.st_mode & S_IFREG)) { + +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL2) + logmessage(LOG_DEBUG, + "%s is not a regular file. Skipping...\n", + theFile->fileName); +#endif + + result = wl_irregularFile; + } + +/* + * Check that the size hasn't gotten any smaller e.g. from ftruncate(2) + */ + + if (result == wl_ok && tmpStat->st_size < theFile->fileStat.st_size) { + + logmessage(LOG_WARNING, + "%s stat - inode %d, size %d -> %d\n", + theFile->fileName, + theFile->fileStat.st_ino, + theFile->fileStat.st_size, + tmpStat->st_size); + + result = wl_reopened; + } + +/* + * File was already open, check to see if it hasn't been modified, and + * that the last time we checked it was > wl_chkDelay ago. + */ + + if (result == wl_ok && + tmpStat->st_mtime == theFile->fileStat.st_mtime && + wl_timeOfRefresh - theFile->lastActive > wl_chkDelay) { + + tmpFd = open(theFile->fileName, O_RDONLY); + + if (tmpFd < 0) { + logmessage(LOG_ERR, + "checkLogFile: 2nd open to %s: %s\n", + theFile->fileName, + osstrerror()); + wl_close(theFile->filePtr); + +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) { + logmessage(LOG_DEBUG, + "checkLogFile: could not check %s\n", + theFile->fileName); + } +#endif + + } + else if (fstat(tmpFd, tmpStat) < 0) { + logmessage(LOG_ERR, + "checkLogFile: stat on inactive %s: %s\n", + theFile->fileName, + osstrerror()); + wl_close(theFile->filePtr); + result = wl_unableToStat; + +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) { + logmessage(LOG_DEBUG, + "checkLogFile: could not stat inactive %s\n", + theFile->fileName); + } +#endif + + } + else if (tmpStat->st_ino != theFile->fileStat.st_ino) { + + logmessage(LOG_WARNING, + "%s inactive - inode %d -> %d\n", + theFile->fileName, + theFile->fileStat.st_ino, + tmpStat->st_ino); + + result = wl_reopened; + } + else + theFile->lastActive = wl_timeOfRefresh; + + + if (tmpFd >= 0) + close(tmpFd); + } + + +/* + * File needs to be reopened due to change in inode, smaller size, or lack + * of activity + */ + + if (result == wl_reopened) { + + theFile->lastActive = wl_timeOfRefresh; + + wl_close(theFile->filePtr); + + if (openLogFile(theFile) < 0) { + + logmessage(LOG_WARNING, + "checkLogFile: unable to reopen %s\n", + theFile->fileName); + result = wl_unableToOpen; + } + +/* update the stat information using new file desc */ + + else if (fstat(theFile->filePtr, tmpStat) < 0) { + logmessage(LOG_ERR, + "checkLogFile - stat on reopened %s: %s\n", + theFile->fileName, + osstrerror()); + wl_close(theFile->filePtr); + result = wl_unableToStat; + } + } + +/* if the size has increased in the logs, then change the lastActive time */ + + if (result == wl_ok && tmpStat->st_mtime > theFile->fileStat.st_mtime) { + +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL1) + logmessage(LOG_DEBUG, "%s grew %d bytes\n", + theFile->fileName, + tmpStat->st_size - theFile->fileStat.st_size); +#endif + theFile->lastActive = wl_timeOfRefresh; + } + + return result; +} + +/* + * Main function for sprocs. Contains an infinite loop selecting on the pipe from the + * main process. Anything on the pipe indicates a refresh is required. + */ + +void +sprocMain(void *sprocNum) +{ + int mySprocNum = *((int*)sprocNum); + int i = 0; + int sts = 0; + WebSproc *sprocData = &wl_sproc[mySprocNum]; + WebServer *server = (WebServer*)0; + + /* Pause a sec' so the output log doesn't get mucked up */ + sleep(1); + +#ifdef HAVE_SIGHUP + /* SIGHUP when the parent dies */ + signal(SIGHUP, onhup); +#endif + +#ifdef HAVE_PRCTL +#ifdef HAVE_PR_TERMCHILD + prctl(PR_TERMCHILD); +#elif HAVE_PR_SET_PDEATHSIG + prctl(PR_SET_PDEATHSIG, SIGHUP); +#endif +#endif + +/* close channel to pmcd */ + if (__pmSocketIPC(extp->e_infd)) + __pmCloseSocket(extp->e_infd); + else if (close(extp->e_infd) < 0) { + logmessage(LOG_ERR, "sprocMain: pmcd ch. close(fd=%d) failed: %s\n", + extp->e_infd, osstrerror()); + } + if (__pmSocketIPC(extp->e_outfd)) + __pmCloseSocket(extp->e_outfd); + else if (close(extp->e_outfd) < 0) { + logmessage(LOG_ERR, "sprocMain: pmcd ch. close(fd=%d) failed: %s\n", + extp->e_outfd, osstrerror()); + } + +/* close pipes to main process which are not to be used */ + + if(close(sprocData->inFD[1]) < 0) { + logmessage(LOG_ERR, "sprocMain[%d]: pipe close(fd=%d) failed: %s\n", + mySprocNum, sprocData->inFD[1], osstrerror()); + } + if(close(sprocData->outFD[0]) < 0) { + logmessage(LOG_ERR, "sprocMain[%d]: pipe close(fd=%d) failed: %s\n", + mySprocNum, sprocData->outFD[0], osstrerror()); + } + +/* open up all file descriptors */ + +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL2) + logmessage(LOG_DEBUG, "Sproc %d started for servers %d to %d\n", + mySprocNum, + sprocData->firstServer, + sprocData->lastServer); +#endif + + for (i=sprocData->firstServer; i<=sprocData->lastServer; i++) + { + server = &wl_servers[i]; + if (server->counts.active) { + openLogFile(&(server->access)); + openLogFile(&(server->error)); + } + } + +/* wait for message from pmda to probe files */ + + for (;;) { + sts = read(sprocData->inFD[0], &i, sizeof(i)); + if (sts <= 0) { + logmessage(LOG_ERR, "Sproc[%d] read(fd=%d) failed: %s\n", + mySprocNum, sprocData->inFD[0], osstrerror()); + exit(1); + } + refresh(sprocData); + sts = write(sprocData->outFD[1], &i, sizeof(i)); + if (sts <= 0) { + logmessage(LOG_ERR, "Sproc[%d] write(fd=%d) failed: %s\n", + sprocData->outFD[1], mySprocNum, osstrerror()); + exit(1); + } + } +} + +/* + * Refresh all the server log files that this process monitors. + * Any entries are parsed, categorised and added to the appropriate metrics. + */ + +void +refresh(WebSproc* proc) +{ + struct stat tmpStat; + + WebServer *server = (WebServer *)0; + WebCount *count = (WebCount *)0; + FileInfo *accessFile = (FileInfo *)0; + FileInfo *errorFile = (FileInfo *)0; + + char *line = (char *)0; + char *end = (char *)0; + int httpMethod = 0; + long size = 0; + int sizeIndex = 0; + int newLength = 0; + int i = 0; + int sts = 0; + int result = wl_ok; + int ok = 0; + time_t currentTime; + size_t nmatch = 5; + regmatch_t pmatch[5]; + + + currentTime = time((time_t*)0); + +/* iterate through each flagged server */ + + for (i=proc->firstServer; i<=proc->lastServer; i++) { + + server = &(wl_servers[i]); + accessFile = &(server->access); + errorFile = &(server->error); + + if ((server->update || wl_updateAll) && server->counts.active) { + + server->counts.numLogs = 0; + +/* check access log still exists */ + + result = checkLogFile(accessFile, &tmpStat); + +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL2) + logmessage(LOG_DEBUG, + "checkLogFile returned %d for server %d (access)\n", + result, + i); +#endif + +/* scan access log */ + + if (result == wl_ok || result == wl_reopened || + result == wl_opened) { + + server->counts.numLogs++; + server->counts.modTime = (__uint32_t)(currentTime - + tmpStat.st_mtime); + + while (accessFile->fileStat.st_size < tmpStat.st_size) { + + sts = wl_gets(accessFile, &line); + if (sts <= 0) { + +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) + logmessage(LOG_DEBUG, + "Short read of %s by %d bytes\n", + accessFile->fileName, + tmpStat.st_size - accessFile->fileStat.st_size); +#endif + + if (sts == 0) { + logmessage(LOG_WARNING, + "refresh %s: unexpected eof\n", + accessFile->fileName); + } + else { + logmessage(LOG_ERR, "refresh %s: %s\n", + accessFile->fileName, osstrerror()); + } + + wl_close(accessFile->filePtr); + accessFile->lastActive -= wl_chkDelay; + break; + } + + accessFile->fileStat.st_size += sts; + + if (proc->strLength == 0 || proc->strLength <= sts) + newLength = sts > 255 ? ((sts / 256) + 1) * 256 : 256; + else + newLength = proc->strLength; + + if (newLength > proc->strLength) + { +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL2) { + logmessage(LOG_DEBUG, + "Resizing strings from %d to %d bytes\n", + proc->strLength, + newLength); + } +#endif + proc->methodStr = (char*)realloc(proc->methodStr, + newLength * sizeof(char)); + proc->sizeStr = (char*)realloc(proc->sizeStr, + newLength * sizeof(char)); + proc->c_statusStr = (char*)realloc(proc->c_statusStr, + newLength * sizeof(char)); + proc->s_statusStr = (char*)realloc(proc->s_statusStr, + newLength * sizeof(char)); + proc->strLength = newLength; + } + + if (proc->methodStr == (char *)0 || + proc->sizeStr == (char *)0 || + proc->c_statusStr == (char *)0 || + proc->s_statusStr == (char *)0 ) { + logmessage(LOG_ERR, + "Unable to allocate %d bytes to strings", + newLength); + proc->strLength = 0; + if (proc->methodStr != (char *)0) + free(proc->methodStr); + if (proc->sizeStr != (char *)0) + free(proc->sizeStr); + if (proc->c_statusStr != (char *)0) + free(proc->c_statusStr); + if (proc->s_statusStr != (char *)0) + free(proc->s_statusStr); + + break; + } + + ok = 0; + + if (wl_regexTable[accessFile->format].posix_regexp) { + if (regexec(wl_regexTable[accessFile->format].regex, + line, nmatch, pmatch, 0) == 0) { + + if(pmatch[1].rm_so < 0 || pmatch[2].rm_so < 0) { + logmessage(LOG_ERR, + "failed to match method and size: %s\n", + line); + continue; + } + + if(server->counts.extendedp) { + if(pmatch[3].rm_so < 0 || pmatch[4].rm_so < 0) { + logmessage(LOG_ERR, + "failed to match status codes: %s\n", + line); + continue; + } + } + + line[pmatch[wl_regexTable[accessFile->format].methodPos].rm_eo] = '\0'; + strncpy(proc->methodStr, &line[pmatch[wl_regexTable[accessFile->format].methodPos].rm_so], + (pmatch[wl_regexTable[accessFile->format].methodPos].rm_eo - + pmatch[wl_regexTable[accessFile->format].methodPos].rm_so) + 1); + + line[pmatch[wl_regexTable[accessFile->format].sizePos].rm_eo] = '\0'; + strncpy(proc->sizeStr, &line[pmatch[wl_regexTable[accessFile->format].sizePos].rm_so], + (pmatch[wl_regexTable[accessFile->format].sizePos].rm_eo - + pmatch[wl_regexTable[accessFile->format].sizePos].rm_so) + 1); + + if(server->counts.extendedp) { + line[pmatch[wl_regexTable[accessFile->format].c_statusPos].rm_eo] = '\0'; + strncpy(proc->c_statusStr, &line[pmatch[wl_regexTable[accessFile->format].c_statusPos].rm_so], + (pmatch[wl_regexTable[accessFile->format].c_statusPos].rm_eo - + pmatch[wl_regexTable[accessFile->format].c_statusPos].rm_so) + 1); + + line[pmatch[wl_regexTable[accessFile->format].s_statusPos].rm_eo] = '\0'; + strncpy(proc->s_statusStr, &line[pmatch[wl_regexTable[accessFile->format].s_statusPos].rm_so], + (pmatch[wl_regexTable[accessFile->format].s_statusPos].rm_eo - + pmatch[wl_regexTable[accessFile->format].s_statusPos].rm_so) + 1); + } else { + proc->c_statusStr[0] = '\0'; + proc->s_statusStr[0] = '\0'; + } + ok = 1; + } +#ifdef PCP_DEBUG + else if (pmDebug & DBG_TRACE_APPL2) + logmessage(LOG_DEBUG, "Regex failed on %s\n", line); +#endif + } +#ifdef NON_POSIX_REGEX + else if (regex(wl_regexTable[accessFile->format].np_regex, + line, proc->methodStr, proc->sizeStr, proc->c_statusStr, proc->s_statusStr) != NULL) { + ok = 1; + } +#ifdef PCP_DEBUG + else if (pmDebug & DBG_TRACE_APPL2) + logmessage(LOG_DEBUG, "Regex failed on %s\n", line); +#endif +#endif + if ( ok ) { + + for (line = proc->methodStr; *line; line++) + *line = toupper((int)*line); + + httpMethod = wl_httpOther; + switch(proc->methodStr[0]) { + case 'G': + if(strcmp(proc->methodStr, "GET") == 0) { + httpMethod = wl_httpGet; + } + break; + case 'O': + if(strcmp(proc->methodStr, "O") == 0) { + httpMethod = wl_httpGet; + } + break; + case 'H': + if(strcmp(proc->methodStr, "HEAD") == 0) { + httpMethod = wl_httpHead; + } + break; + case 'P': + if(strcmp(proc->methodStr, "POST") == 0 || + strcmp(proc->methodStr, "PUT") == 0) { + httpMethod = wl_httpPost; + } + break; + case 'I': + if(strcmp(proc->methodStr, "I") == 0) { + httpMethod = wl_httpPost; + } + break; + } + + if (strcmp(proc->sizeStr, "-") == 0 || + strcmp(proc->sizeStr, " ") == 0) { + size = 0; + sizeIndex = wl_unknownSize; + } + else { + size = strtol(proc->sizeStr, &end, 10); + if (*end != '\0') { + logmessage(LOG_ERR, "Bad size (%s) @ %s", + proc->sizeStr, + line); + continue; + } + + for (sizeIndex = 0; + sizeIndex < wl_gt3m && size > wl_sizes[sizeIndex]; + sizeIndex++); + } + + count = &(server->counts); + count->methodReq[httpMethod]++; + count->methodBytes[httpMethod] += size; + + count->sizeReq[sizeIndex]++; + count->sizeBytes[sizeIndex] += size; + + count->sumReq++; + count->sumBytes += size; + + if(server->counts.extendedp == 1) { + /* common extended format */ +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL2) { + logmessage(LOG_DEBUG, + "Access: Server=%d, line=%s [CEF]\n M: %s S: %s CS: %s, SS: %s", + i, + line, + proc->methodStr, + proc->sizeStr, + proc->c_statusStr, + proc->s_statusStr); + } +#endif + + /* + * requested page is not in client/browser cache, nor in the + * server's cache so it has been fetched from the remote server + */ + if(strcmp(proc->c_statusStr, "200") == 0 && + strcmp(proc->s_statusStr, "200") == 0) { +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL2) { + logmessage(LOG_DEBUG, + "Access: Server=%d, REMOTE fetch: of %.0f bytes\n", + i, + atof(proc->sizeStr)); + } +#endif + /* + * now bucket the size + */ + if (strcmp(proc->sizeStr, "-") == 0 || + strcmp(proc->sizeStr, " ") == 0) { + size = 0; + sizeIndex = wl_unknownSize; + } + else { + size = strtol(proc->sizeStr, &end, 10); + if (*end != '\0') { + logmessage(LOG_ERR, "Bad size (%s) @ %s", + proc->sizeStr, + line); + continue; + } + + for (sizeIndex = 0; + sizeIndex < wl_gt3m && size > wl_sizes[sizeIndex]; + sizeIndex++); + } + count->uncached_sumReq++; + count->uncached_sumBytes += size; + count->uncached_sizeReq[sizeIndex]++; + count->uncached_sizeBytes[sizeIndex] += size; + + } + + /* + * requested page is not in client/browser cache, but is in the + * server's cache so it is just returned to the client (a cache hit) + */ + if(strcmp(proc->c_statusStr, "200") == 0 && + (strcmp(proc->s_statusStr, "304") == 0 || + strcmp(proc->s_statusStr, "-") == 0)) { +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL2) { + logmessage(LOG_DEBUG, + "Access: Server=%d, CACHE return: of %.0f bytes\n", + i, + atof(proc->sizeStr)); + } +#endif + /* + * now bucket the size + */ + if (strcmp(proc->sizeStr, "-") == 0 || + strcmp(proc->sizeStr, " ") == 0) { + size = 0; + sizeIndex = wl_unknownSize; + } + else { + size = strtol(proc->sizeStr, &end, 10); + if (*end != '\0') { + logmessage(LOG_ERR, "Bad size (%s) @ %s", + proc->sizeStr, + line); + continue; + } + + for (sizeIndex = 0; + sizeIndex < wl_gt3m && size > wl_sizes[sizeIndex]; + sizeIndex++); + } + count->cached_sumReq++; + count->cached_sumBytes += size; + count->cached_sizeReq[sizeIndex]++; + count->cached_sizeBytes[sizeIndex] += size; + + } + + /* + * requested page is in client/browser cache + */ + if(strcmp(proc->c_statusStr, "304") == 0 && + (strcmp(proc->s_statusStr, "304") == 0 || + strcmp(proc->s_statusStr, "-") == 0)) { +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL2) { + logmessage(LOG_DEBUG, + "Access: Server=%d, CLIENT hit\n", + i); + } +#endif + count->client_sumReq++; + } + } else if(server->counts.extendedp == 2) { + /* default squid format */ +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL2) { + logmessage(LOG_DEBUG, + "Access: Server=%d, line=%s [squid]\n M: %s S: %s CS: %s, SS: %s", + i, + line, + proc->methodStr, + proc->sizeStr, + proc->c_statusStr, + proc->s_statusStr); + } +#endif + + /* + * requested page is not in client/browser cache, nor in the + * server's cache so it has been fetched from the remote server + */ + if(strcmp(proc->c_statusStr, "200") == 0 && + (strstr(proc->s_statusStr, "_MISS") != NULL || + strstr(proc->s_statusStr, "_CLIENT_REFRESH") != NULL || + strstr(proc->s_statusStr, "_SWAPFAIL") != NULL)) { +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL2) { + logmessage(LOG_DEBUG, + "Access: Server=%d, REMOTE fetch: of %.0f bytes\n", + i, + atof(proc->sizeStr)); + } +#endif + /* + * now bucket the size + */ + if (strcmp(proc->sizeStr, "-") == 0 || + strcmp(proc->sizeStr, " ") == 0) { + size = 0; + sizeIndex = wl_unknownSize; + } + else { + size = strtol(proc->sizeStr, &end, 10); + if (*end != '\0') { + logmessage(LOG_ERR, "Bad size (%s) @ %s", + proc->sizeStr, + line); + continue; + } + + for (sizeIndex = 0; + sizeIndex < wl_gt3m && size > wl_sizes[sizeIndex]; + sizeIndex++); + } + count->uncached_sumReq++; + count->uncached_sumBytes += size; + count->uncached_sizeReq[sizeIndex]++; + count->uncached_sizeBytes[sizeIndex] += size; + + } + + /* + * requested page is not in client/browser cache, but is in the + * server's cache so it is just returned to the client (a cache hit) + */ + if(strcmp(proc->c_statusStr, "200") == 0 && + strstr(proc->s_statusStr, "_HIT") != NULL) { +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL2) { + logmessage(LOG_DEBUG, + "Access: Server=%d, CACHE return: of %.0f bytes\n", + i, + atof(proc->sizeStr)); + } +#endif + /* + * now bucket the size + */ + if (strcmp(proc->sizeStr, "-") == 0 || + strcmp(proc->sizeStr, " ") == 0) { + size = 0; + sizeIndex = wl_unknownSize; + } + else { + size = strtol(proc->sizeStr, &end, 10); + if (*end != '\0') { + logmessage(LOG_ERR, "Bad size (%s) @ %s", + proc->sizeStr, + line); + continue; + } + + for (sizeIndex = 0; + sizeIndex < wl_gt3m && size > wl_sizes[sizeIndex]; + sizeIndex++); + } + count->cached_sumReq++; + count->cached_sumBytes += size; + count->cached_sizeReq[sizeIndex]++; + count->cached_sizeBytes[sizeIndex] += size; + } + + /* + * requested page is in client/browser cache + */ + if(strcmp(proc->c_statusStr, "304") == 0) { +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL2) { + logmessage(LOG_DEBUG, + "Access: Server=%d, CLIENT hit\n", + i); + } +#endif + count->client_sumReq++; + } + } + +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL2) { + logmessage(LOG_DEBUG, + "Access: Server=%d, line=%s\n method=%s [%d], size=%s=%d [%d]\n", + i, + line, + proc->methodStr, + httpMethod, + proc->sizeStr, + size, + sizeIndex); + } +#endif + + } + } + accessFile->fileStat = tmpStat; + } + + result = checkLogFile(errorFile, &tmpStat); + +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL2) + logmessage(LOG_DEBUG, + "checkLogFile returned %d for server %d (error)\n", + result, + i); +#endif + + /* scan error log */ + + if (result == wl_ok || result == wl_reopened || + result == wl_opened) { + + server->counts.numLogs++; + + while (errorFile->fileStat.st_size < tmpStat.st_size) { + sts = wl_gets(errorFile, &line); + if (sts <= 0) { +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) + logmessage(LOG_DEBUG, "%s was %d bytes short\n", + errorFile->fileName, + tmpStat.st_size - + errorFile->fileStat.st_size); +#endif + + if (sts < 0) { + logmessage(LOG_ERR, "refresh %s: %s\n", + errorFile->fileName, osstrerror()); + } + else { + logmessage(LOG_WARNING, + "refresh %s: unexpected eof\n", + errorFile->fileName); + } + + wl_close(errorFile->filePtr); + errorFile->lastActive -= wl_chkDelay; + break; + } + + errorFile->fileStat.st_size += sts; + + if(wl_regexTable[errorFile->format].posix_regexp) { + if (regexec(wl_regexTable[errorFile->format].regex, + line, nmatch, pmatch, 0) == 0) { + server->counts.errors++; + } +#ifdef NON_POSIX_REGEX + } else { + if (regex(wl_regexTable[errorFile->format].np_regex, + line, proc->methodStr, proc->sizeStr) != NULL) { + server->counts.errors++; + } +#endif + } + } + errorFile->fileStat = tmpStat; + } + } + + /* check to see if a server is inactive but has a file open. It may + have just been deactivated */ + + else if ((server->update || wl_updateAll) && !server->counts.active) { + + if (accessFile->filePtr >= 0) { + + logmessage(LOG_WARNING, + "Closing inactive server %d access file: %s\n", + i, + accessFile->fileName); + + wl_close(accessFile->filePtr); + } + + if (errorFile->filePtr >= 0) { + + logmessage(LOG_WARNING, + "Closing inactive server %d error file: %s\n", + i, + errorFile->fileName); + + wl_close(errorFile->filePtr); + } + } + } +} + +/* + * Initialise the indom and meta tables + * Check that we can do direct mapping. + */ + +/* + * Mark servers that are required in the latest profile. + */ +static int +web_profile(__pmProfile *prof, pmdaExt *ext) +{ + pmdaIndom *idp = wl_indomTable; + int j; + + ext->e_prof = prof; + for (j = 0; j < idp->it_numinst; j++) { + if (__pmInProfile(idp->it_indom, prof, idp->it_set[j].i_inst)) + wl_servers[j].update = 1; + else + wl_servers[j].update = 0; + } + + return 0; +} + +/* + * Probe servers for log file changes. + * Only those servers that are marked will be requsted. Therefore, if an sproc does + * not have any marked servers, it will not be signalled. + * NOTE: The main process completes its refresh before signalling the other sprocs. + */ + +void +probe(void) +{ + int i = 0; + int j = 0; + int sts = 0; + int dummy = 1; + int sprocsUsed = 0; + int nfds = 0; + fd_set rfds; + fd_set tmprfds; + int thisFD; + WebSproc *sprocData = (WebSproc*)0; + struct timeval theTime; + + __pmtimevalNow(&theTime); + + wl_timeOfRefresh = theTime.tv_sec; + +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL1) + logmessage(LOG_DEBUG, "Starting probe at %d\n", wl_timeOfRefresh); +#endif + + FD_ZERO(&rfds); + +/* + * Determine which sprocs have servers that must be refreshed. + * Add those sprocs pipes to the file descriptor list. + */ + + for (i=1; i<=wl_numSprocs; i++) { + sprocData = &wl_sproc[i]; + + if (!wl_updateAll) { + for (j=sprocData->firstServer; j<=sprocData->lastServer; j++) + if (wl_servers[j].update) + break; + } + else { + for (j=sprocData->firstServer; j<=sprocData->lastServer; j++) + if (wl_servers[j].counts.active) + break; + } + + if (j > sprocData->lastServer) { +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL2) + logmessage(LOG_DEBUG, "Skipping sproc %d\n", i); +#endif + continue; + } + +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL1) + logmessage(LOG_DEBUG, + "Told sproc %d to probe for at least server %d\n", + i, + j); +#endif + + sprocsUsed++; + thisFD = sprocData->outFD[0]; + sts = write(sprocData->inFD[1], &dummy, sizeof(dummy)); + if (sts < 0) { + logmessage(LOG_ERR, "Error on fetch write(fd=%d): %s", + sprocData->inFD[1], osstrerror()); + exit(1); + } + + FD_SET(thisFD, &rfds); + nfds = nfds < (thisFD + 1) ? thisFD + 1 : nfds; + } + +/* + * Check that we have to update the main process servers + */ + + sprocData = &wl_sproc[0]; + if (!wl_updateAll) { + for (j=sprocData->firstServer; j<=sprocData->lastServer; j++) + if (wl_servers[j].update) + break; + } + else { + for (j=sprocData->firstServer; j<=sprocData->lastServer; j++) + if (wl_servers[j].counts.active) + break; + } + + if (j <= sprocData->lastServer) { + refresh(&wl_sproc[0]); +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL2) + logmessage(LOG_DEBUG, "Done probe for 0 to %d\n", + sprocData->lastServer); +#endif + } +#ifdef PCP_DEBUG + else if (pmDebug & DBG_TRACE_APPL2) + logmessage(LOG_DEBUG, "Skipping refresh of main process\n"); +#endif + +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL2) { + logmessage(LOG_DEBUG, "Waiting for reply from %d out of %d sprocs\n", + sprocsUsed, + wl_numSprocs); + } +#endif + +/* + * Wait for all sprocs to reply + * Note: This could get into a hard select loop if an sproc losses it + */ + + for (i=0; i<sprocsUsed;) { + memcpy(&tmprfds, &rfds, sizeof(tmprfds)); + sts = select(nfds, &tmprfds, (fd_set*)0, (fd_set*)0, + (struct timeval*)0); + if (sts < 0) { + logmessage(LOG_ERR, "Error on fetch select: %s", netstrerror()); + exit(1); + } + else if (sts == 0) + continue; + + i += sts; + for (j=1; j<=wl_numSprocs; j++) { + sprocData = &wl_sproc[j]; + thisFD = sprocData->outFD[0]; + + if (FD_ISSET(thisFD, &tmprfds)) { + FD_CLR(sprocData->outFD[0], &rfds); + sts = read(thisFD, &dummy, sizeof(dummy)); + if (sts < 0) { + logmessage(LOG_ERR, "Error on fetch read: %s", + osstrerror()); + exit(1); + } + } + } + } + +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL1) + logmessage(LOG_DEBUG, "Finished probe\n"); +#endif +} + +/* + * Refresh all servers + * Usually called if no fetches have been received after a set time + */ + +void +refreshAll(void) +{ + struct timeval before; + struct timeval after; + + wl_updateAll = 1; + +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL1) { + logmessage(LOG_DEBUG, "Starting a refreshAll\n"); + } +#endif + + __pmtimevalNow(&before); + probe(); + + __pmtimevalNow(&after); + wl_catchupTime = (after.tv_sec - before.tv_sec) * 1000; + wl_catchupTime += (after.tv_usec - before.tv_usec) / 1000; + + wl_updateAll = 0; + +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL1) + logmessage(LOG_DEBUG, "Probed all logs, took %d msec\n", + wl_catchupTime); +#endif + +} + +/* + * Build a pmResult table of the requested metrics + */ + +static int +web_fetch(int numpmid, pmID pmidlist[], pmResult **resp, pmdaExt *ext) +{ + int i; /* over pmidlist[] */ + int j; /* over vset->vlist[] */ + int s; /* over server */ + int sts; + int need; + int inst; + int numval; + static pmResult *res = (pmResult *)0; + static int maxnpmids = 0; + pmValueSet *vset = (pmValueSet *)0; + pmDesc *dp = (pmDesc *)0; + __pmID_int *pmidp; + pmAtomValue atom; + int haveValue = 0; + int type; + __psint_t m_offset = 0; /* initialize to pander to gcc */ + int m_type = 0; /* initialize to pander to gcc */ + int cluster; + __uint32_t tmp32; + __uint64_t tmp64; + +/* determine if the total aggregates are required, which forces a refresh + of all servers, and if a probe is require at all */ + + j = 0; + for (i = 0; i < numpmid; i++) { + pmidp = (__pmID_int *)&pmidlist[i]; + if (pmidp->cluster == 1) + break; + else + j += pmidp->cluster; + } + + if (i < numpmid) { +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL1) + logmessage(LOG_DEBUG, "web_fetch: refreshAll\n"); +#endif + refreshAll(); + } + else if (j) { +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL1) + logmessage(LOG_DEBUG, "web_fetch: probe\n"); +#endif + probe(); + } +#ifdef PCP_DEBUG + else if (pmDebug & DBG_TRACE_APPL1) + logmessage(LOG_DEBUG, "web_fetch: no probes required\n"); +#endif + + + if (numpmid > maxnpmids) { + if (res != (pmResult *)0) + free(res); + +/* (numpmid - 1) because there's room for one valueSet in a pmResult */ + + need = sizeof(pmResult) + (numpmid - 1) * sizeof(pmValueSet *); + if ((res = (pmResult *) malloc(need)) == (pmResult *)0) + return -oserror(); + maxnpmids = numpmid; + } + + res->timestamp.tv_sec = 0; + res->timestamp.tv_usec = 0; + res->numpmid = numpmid; + +/* + * Get each corresponding metric from the meta table. + * Check that the metric has the correct cluster + */ + + for (i = 0; i < numpmid; i++) { + + pmidp = (__pmID_int*)&pmidlist[i]; + dp = (pmDesc *)0; + + if (ext->e_direct) { + + if (pmidp->item < numMetrics && + pmidlist[i] == wl_metrics[pmidp->item].m_desc.pmid) { + + dp = &wl_metrics[pmidp->item].m_desc; + m_offset = wl_metricInfo[pmidp->item].m_offset; + m_type = wl_metricInfo[pmidp->item].m_type; + } + } + else { + for (j = 0; j<numMetrics; j++) { + if (wl_metrics[j].m_desc.pmid == pmidlist[i]) { + + dp = &wl_metrics[j].m_desc; + m_offset = wl_metricInfo[j].m_offset; + m_type = wl_metricInfo[j].m_type; + break; + } + } + } + +/* + * count the number of instances in profile + */ + + if (dp == (pmDesc *)0) + numval = PM_ERR_PMID; + else { + if (dp->indom != PM_INDOM_NULL) { + numval = 0; + __pmdaStartInst(dp->indom, ext); + while(__pmdaNextInst(&inst, ext)) { + numval++; + } + } + else { + numval = 1; + } + } + + + /* Must use individual malloc()s because of pmFreeResult() */ + + if (numval >= 1) + res->vset[i] = vset = (pmValueSet *)malloc(sizeof(pmValueSet) + + (numval - 1)*sizeof(pmValue)); + else + res->vset[i] = vset = (pmValueSet*)malloc(sizeof(pmValueSet) - + sizeof(pmValue)); + + if (vset == (pmValueSet *)0) { + if (i) { + res->numpmid = i; + __pmFreeResultValues(res); + } + return -oserror(); + } + + vset->pmid = pmidlist[i]; + vset->numval = numval; + vset->valfmt = PM_VAL_INSITU; + if (vset->numval <= 0) + continue; + + if (dp->indom == PM_INDOM_NULL) + inst = PM_IN_NULL; + else { + __pmdaStartInst(dp->indom, ext); + __pmdaNextInst(&inst, ext); + } + + type = dp->type; + pmidp = (__pmID_int *)&pmidlist[i]; + j = 0; + + do { + if (j == numval) { + + /* more instances than expected! */ + + numval++; + res->vset[i] = vset = (pmValueSet *)realloc(vset, + sizeof(pmValueSet) + (numval - 1)*sizeof(pmValue)); + if (vset == (pmValueSet *)0) { + if (i) { + res->numpmid = i; + __pmFreeResultValues(res); + } + return -oserror(); + } + } + + vset->vlist[j].inst = inst; + + cluster = (dp->pmid & _clusterMask) >> 10; + haveValue = 1; + + switch(m_type) { + case wl_globalPtr: + atom.ul = *(__uint32_t *)(m_offset); + break; + + case wl_offset32: + if (wl_servers[inst].counts.active) + atom.ul = *((__uint32_t *)(((__psint_t)(&(wl_servers[inst].counts))) + m_offset)); + else + haveValue = 0; + break; + + case wl_offset64: + if (wl_servers[inst].counts.active) + atom.ull = *((__uint64_t *)(((__psint_t)(&(wl_servers[inst].counts))) + m_offset)); + else + haveValue = 0; + break; + + case wl_totalAggregate: + + if (wl_numActive == 0) + haveValue = 0; + else { + switch(m_offset) { + case 0: + /* errors */ + tmp32 = 0; + for (s = 0; s < wl_numServers; s++) { + if (wl_servers[s].counts.active) { + tmp32 += wl_servers[s].counts.errors; + } + } + atom.ul = tmp32; + break; + case 1: + /* requests */ + tmp32 = 0; + for (s = 0; s < wl_numServers; s++) { + if (wl_servers[s].counts.active) { + tmp32 += wl_servers[s].counts.sumReq; + } + } + atom.ul = tmp32; + break; + case 2: + /* bytes */ + tmp64 = 0; + for (s = 0; s < wl_numServers; s++) { + if (wl_servers[s].counts.active) { + tmp64 += wl_servers[s].counts.sumBytes; + } + } + atom.ull = tmp64; + break; + case 3: + /* client hit requests */ + tmp32 = haveValue = 0; + for (s = 0; s < wl_numServers; s++) { + if (wl_servers[s].counts.active && + wl_servers[s].counts.extendedp) { + haveValue = 1; + tmp32 += wl_servers[s].counts.client_sumReq; + } + } + atom.ul = tmp32; + break; + case 4: + /* cached hit requests */ + tmp32 = haveValue = 0; + for (s = 0; s < wl_numServers; s++) { + if (wl_servers[s].counts.active && + wl_servers[s].counts.extendedp) { + haveValue = 1; + tmp32 += wl_servers[s].counts.cached_sumReq; + } + } + atom.ul = tmp32; + break; + case 5: + /* cached hit requests */ + tmp32 = haveValue = 0; + for (s = 0; s < wl_numServers; s++) { + if (wl_servers[s].counts.active && + wl_servers[s].counts.extendedp) { + haveValue = 1; + tmp32 += wl_servers[s].counts.uncached_sumReq; + } + } + atom.ul = tmp32; + break; + case 6: + /* cached hit bytes */ + tmp64 = haveValue = 0; + for (s = 0; s < wl_numServers; s++) { + if (wl_servers[s].counts.active && + wl_servers[s].counts.extendedp) { + haveValue = 1; + tmp64 += wl_servers[s].counts.cached_sumBytes; + } + } + atom.ull = tmp64; + break; + case 7: + /* uncached bytes */ + tmp64 = haveValue = 0; + for (s = 0; s < wl_numServers; s++) { + if (wl_servers[s].counts.active && + wl_servers[s].counts.extendedp) { + haveValue = 1; + tmp64 += wl_servers[s].counts.uncached_sumBytes; + } + } + atom.ull = tmp64; + break; + default: + break; + } + + } + break; + + case wl_serverAggregate: + + if (wl_servers[inst].counts.active == 0) + haveValue = 0; + else switch(m_offset) { + case 0: + /* requests */ + atom.ul = wl_servers[inst].counts.sumReq; + break; + case 1: + /* bytes */ + atom.ull = wl_servers[inst].counts.sumBytes; + break; + case 2: + /* client hit requests */ + if( wl_servers[inst].counts.extendedp ) { + atom.ul = wl_servers[inst].counts.client_sumReq; + } else + haveValue = 0; + break; + case 3: + /* cache hit requests */ + if( wl_servers[inst].counts.extendedp ) { + atom.ul = wl_servers[inst].counts.cached_sumReq; + } else + haveValue = 0; + break; + case 4: + /* uncached requests */ + if( wl_servers[inst].counts.extendedp ) { + atom.ul = wl_servers[inst].counts.uncached_sumReq; + } else + haveValue = 0; + break; + case 5: + /* cached bytes */ + if( wl_servers[inst].counts.extendedp ) { + atom.ull = wl_servers[inst].counts.cached_sumBytes; + } else + haveValue = 0; + break; + case 6: + /* uncached bytes */ + if( wl_servers[inst].counts.extendedp ) { + atom.ull = wl_servers[inst].counts.uncached_sumBytes; + } else + haveValue = 0; + default: + break; + } + break; + + case wl_requestMethod: + + if (cluster == 1) { /* all servers */ + if (wl_numActive == 0) + haveValue = 0; + else { + tmp32 = 0; + for (s = 0; s < wl_numServers; s++) + if (wl_servers[s].counts.active) + tmp32 += wl_servers[s].counts.methodReq[m_offset]; + atom.ul = tmp32; + } + } + else if (wl_servers[inst].counts.active) + atom.ul = wl_servers[inst].counts.methodReq[m_offset]; + else + haveValue = 0; + + break; + + case wl_bytesMethod: + if (cluster == 1) { /* all servers */ + if (wl_numActive == 0) + haveValue = 0; + else { + tmp64 = 0; + for (s = 0; s < wl_numServers; s++) + if (wl_servers[s].counts.active) + tmp64 += wl_servers[s].counts.methodBytes[m_offset]; + atom.ull = tmp64; + } + } + else if (wl_servers[inst].counts.active) + atom.ull = wl_servers[inst].counts.methodBytes[m_offset]; + else + haveValue = 0; + break; + + case wl_requestSize: + if (cluster == 1) { /* all servers */ + if (wl_numActive == 0) + haveValue = 0; + else { + tmp32 = 0; + for (s = 0; s < wl_numServers; s++) + if (wl_servers[s].counts.active) + tmp32 += wl_servers[s].counts.sizeReq[m_offset]; + atom.ul = tmp32; + } + } + else if (wl_servers[inst].counts.active) + atom.ul = wl_servers[inst].counts.sizeReq[m_offset]; + else + haveValue = 0; + break; + + case wl_bytesSize: + if (cluster == 1) { /* all servers */ + if (wl_numActive == 0) + haveValue = 0; + else { + tmp64 = 0; + for (s = 0; s < wl_numServers; s++) + if (wl_servers[s].counts.active) + tmp64 += wl_servers[s].counts.sizeBytes[m_offset]; + atom.ull = tmp64; + } + } + else if (wl_servers[inst].counts.active) + atom.ull = wl_servers[inst].counts.sizeBytes[m_offset]; + else + haveValue = 0; + break; + + case wl_requestCachedSize: + haveValue = 0; + if (cluster == 3) { /* all servers */ + tmp32 = 0; + for (s = 0; s < wl_numServers; s++) + if (wl_servers[s].counts.active && + wl_servers[s].counts.extendedp) { + haveValue = 1; + tmp32 += wl_servers[s].counts.cached_sizeReq[m_offset]; + } + atom.ul = tmp32; + } + else if (wl_servers[inst].counts.active && + wl_servers[inst].counts.extendedp) { + haveValue = 1; + atom.ul = wl_servers[inst].counts.cached_sizeReq[m_offset]; + } + break; + + case wl_bytesCachedSize: + haveValue = 0; + if (cluster == 3) { /* all servers */ + tmp64 = 0; + for (s = 0; s < wl_numServers; s++) { + if (wl_servers[s].counts.active && + wl_servers[s].counts.extendedp) { + haveValue = 1; + tmp64 += wl_servers[s].counts.cached_sizeBytes[m_offset]; + } + } + atom.ull = tmp64; + } + else if (wl_servers[inst].counts.active && + wl_servers[inst].counts.extendedp) { + haveValue = 1; + atom.ull = wl_servers[inst].counts.cached_sizeBytes[m_offset]; + } + break; + + case wl_requestUncachedSize: + haveValue = 0; + if (cluster == 3) { /* all servers */ + tmp32 = 0; + for (s = 0; s < wl_numServers; s++) { + if (wl_servers[s].counts.active && + wl_servers[s].counts.extendedp ) { + haveValue = 1; + tmp32 += wl_servers[s].counts.uncached_sizeReq[m_offset]; + } + } + atom.ul = tmp32; + } + else if (wl_servers[inst].counts.active && + wl_servers[inst].counts.extendedp) { + haveValue = 1; + atom.ul = wl_servers[inst].counts.uncached_sizeReq[m_offset]; + } + break; + + case wl_bytesUncachedSize: + haveValue = 0; + if (cluster == 3) { /* all servers */ + tmp64 = 0; + for (s = 0; s < wl_numServers; s++) { + if (wl_servers[s].counts.active && + wl_servers[s].counts.extendedp) { + haveValue = 1; + tmp64 += wl_servers[s].counts.uncached_sizeBytes[m_offset]; + } + } + atom.ull = tmp64; + } + else if (wl_servers[inst].counts.active && + wl_servers[inst].counts.extendedp) { + haveValue = 1; + atom.ull = wl_servers[inst].counts.uncached_sizeBytes[m_offset]; + } + break; + + case wl_watched: + atom.ul = *((__uint32_t *)(((__psint_t)(&(wl_servers[inst].counts))) + m_offset)); + break; + case wl_numAlive: + tmp32 = 0; + for (s = 0; s < wl_numServers; s++) { + if (wl_servers[s].counts.active) + tmp32 += wl_servers[s].counts.numLogs > 0 ?1:0; + } + atom.ul = tmp32; + break; + + case wl_nosupport: + haveValue = 0; + break; + + default: + logmessage(LOG_CRIT, + "Illegal Meta Type (%d) for metric %d\n", + m_type, + pmidp->item); + exit(1); + } + + if (haveValue) { + sts = __pmStuffValue(&atom, &vset->vlist[j], type); + if (sts < 0) { + __pmFreeResultValues(res); + return sts; + } + + vset->valfmt = sts; + j++; /* next element in vlist[] for next instance */ + } + + } while (dp->indom != PM_INDOM_NULL && __pmdaNextInst(&inst, ext)); + + vset->numval = j; + } + *resp = res; + return 0; +} + +/* + * Store into one of three metrics: + * web.activity.config.catchup, web.activity.config.check and web.activity.server.watched + */ + +static int +web_store(pmResult *result, pmdaExt *ext) +{ + int i; + int j; + pmValueSet *vsp; + int sts = 0; + __pmID_int *pmidp; + WebServer *server = (WebServer*)0; + + for (i = 0; i < result->numpmid; i++) { + vsp = result->vset[i]; + pmidp = (__pmID_int *)&vsp->pmid; + if (pmidp->cluster == 0) { + if (pmidp->item == 1) { /* web.activity.config.catchup */ + int val = vsp->vlist[0].value.lval; + if (val < 0) { + sts = PM_ERR_SIGN; + val = 20; + } + wl_refreshDelay = val; + } + else if (pmidp->item == 3) {/* web.activity.config.check */ + int val = vsp->vlist[0].value.lval; + if (val < 0) { + sts = PM_ERR_SIGN; + val = 20; + } + wl_chkDelay = val; + } + else if (pmidp->item == 35) {/* web.activity.server.watched */ + for (j = 0; j < vsp->numval; j++) { + int val = vsp->vlist[j].value.lval; + if (val < 0) { + sts = PM_ERR_SIGN; + val = 1; + } + + server = &wl_servers[vsp->vlist[j].inst]; + if (val > 0 && server->counts.active == 0) { + wl_numActive++; + server->counts.active = 1; + } + else if (val == 0 && server->counts.active > 0){ + wl_numActive--; + server->counts.active = 0; + } + } + } + else { + sts = PM_ERR_PMID; + break; + } + } + else { + /* not one of the metrics we are willing to change */ + sts = PM_ERR_PMID; + break; + } + } + return sts; +} + +/* + * Initialise the callback table, and open the help text. + * This also calls the routine that to initialise the indom and meta tables. + */ + +void +web_init(pmdaInterface *dp) +{ + int m; + int type; + + extp = dp->version.two.ext; + + if (wl_isDSO) + pmdaDSO(dp, PMDA_INTERFACE_2, "weblog DSO", wl_helpFile); + + if (dp->status != 0) + return; + + dp->version.two.profile = web_profile; + dp->version.two.fetch = web_fetch; + dp->version.two.store = web_store; + + if (numMetrics != (sizeof(wl_metricInfo)/sizeof(wl_metricInfo[0]))) { + logmessage(LOG_CRIT, + "Metric and Metric Info tables are not the same size\n"); + dp->status = -1; + return; + } + + pmdaInit(dp, wl_indomTable, numIndoms, wl_metrics, numMetrics); + + for (m = 0; m < numMetrics; m++) { + type = wl_metricInfo[m].m_type; + if (type == wl_offset32 || type == wl_offset64 || + type == wl_watched) { + wl_metricInfo[m].m_offset -= (__psint_t)&dummyCount; + } + } + + return; +} + diff --git a/src/pmdas/weblog/weblog.h b/src/pmdas/weblog/weblog.h new file mode 100644 index 0000000..a2c99dd --- /dev/null +++ b/src/pmdas/weblog/weblog.h @@ -0,0 +1,140 @@ + +/* + * Copyright (c) 2000 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef _WEBLOG_H +#define _WEBLOG_H + +#include "pmapi.h" +#include "impl.h" +#include "pmda.h" +#include <regex.h> +#include <sys/stat.h> + +enum HTTP_Methods { + wl_httpGet, wl_httpHead, wl_httpPost, wl_httpOther, wl_numMethods +}; + +enum HistSizes { + wl_zero, wl_le3k, wl_le10k, wl_le30k, wl_le100k, wl_le300k, wl_le1m, + wl_le3m, wl_gt3m, wl_unknownSize, wl_numSizes +}; + +#define FIBUFSIZE 16*1024 +#define DORMANT_WARN 86400 + +typedef struct { + char* fileName; + int filePtr; + struct stat fileStat; + char buf[FIBUFSIZE]; + char *bp; + char *bend; + u_int format; /* index into regex for parsing file */ + time_t lastActive; /* time in sec when last active */ +} FileInfo; + +typedef struct { + __uint32_t methodReq[wl_numMethods]; + __uint64_t methodBytes[wl_numMethods]; + __uint32_t sizeReq[wl_numSizes]; + __uint64_t sizeBytes[wl_numSizes]; + __uint32_t cached_sizeReq[wl_numSizes]; + __uint64_t cached_sizeBytes[wl_numSizes]; + __uint32_t uncached_sizeReq[wl_numSizes]; + __uint64_t uncached_sizeBytes[wl_numSizes]; + __uint32_t sumReq; + __uint32_t client_sumReq; + __uint32_t cached_sumReq; + __uint32_t uncached_sumReq; + __uint64_t sumBytes; + __uint64_t cached_sumBytes; + __uint64_t uncached_sumBytes; + __uint32_t errors; + __uint32_t active; + __uint32_t numLogs; + __uint32_t modTime; + __uint32_t extendedp; +} WebCount; + +typedef struct { + int update; /* flag for updating this server */ + WebCount counts; + FileInfo access; + FileInfo error; +} WebServer; + +typedef struct { + int id; + pid_t pid; + int firstServer; + int lastServer; + int inFD[2]; + int outFD[2]; + char *methodStr; + char *sizeStr; + char *c_statusStr; + char *s_statusStr; + int strLength; +} WebSproc; + +typedef struct { + char* name; +#ifdef NON_POSIX_REGEX + char *np_regex; +#endif + regex_t* regex; + int methodPos; + int sizePos; + int c_statusPos; + int s_statusPos; + int posix_regexp; +} WebRegex; + +extern WebServer *wl_servers; +extern WebSproc *wl_sproc; +extern WebRegex *wl_regexTable; +extern pmdaInstid *wl_serverInst; +extern pmdaIndom wl_indomTable[]; + +extern __uint32_t wl_numServers; +extern __uint32_t wl_numActive; +extern __uint32_t wl_refreshDelay; +extern __uint32_t wl_chkDelay; +extern __uint32_t wl_sprocThresh; +extern __uint32_t wl_numSprocs; +extern __uint32_t wl_catchupTime; +extern __uint32_t wl_numRegex; + +extern int wl_updateAll; +extern time_t wl_timeOfProbe; +extern char *wl_logFile; +extern char wl_helpFile[]; +extern int wl_isDSO; + +int openLogFile(FileInfo*); +void probe(void); +void refresh(WebSproc*); +void refreshAll(void); +void sprocMain(void*); +void web_init(pmdaInterface*); +void logmessage(int, const char *, ...); + +#define wl_close(fd) do { if (fd >= 0) close(fd); fd = -1; } while (0) + +#endif /* _WEBLOG_H */ diff --git a/src/pmdas/weblog/weblogconv.sh b/src/pmdas/weblog/weblogconv.sh new file mode 100755 index 0000000..d62d2d2 --- /dev/null +++ b/src/pmdas/weblog/weblogconv.sh @@ -0,0 +1,62 @@ +#! /bin/sh +# +# Copyright (c) 2000,2004 Silicon Graphics, Inc. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License 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. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +# + +# +# weblogconv: convert weblog.conf files to posix regex +# + +# only needed on Linux + +progname=$0 + +if [ $# -gt 0 -a "$1" = "-?" ]; then + echo "Usage: $progname infile [outfile]" + exit 0 +fi + +if [ $# -gt 2 ]; then + echo "$progname: Too many arguments." + exit 1 +fi + +if [ $# -lt 1 ] ; then + infile="" +else + infile=$1 +fi + +if [ $# -lt 2 ]; then + outfile="" +else + outfile="> $2" +fi + +if [ -n "$infile" -a ! -r "$infile" ]; then + echo "$progname: cannot read $infile" + exit 1 +fi + +sed \ + -e '/)\$[01]/!s/^regex[ \t][ \t]*\([^ \t][^ \t]*\)[ \t][ \t]*/regex_posix \1 - /' \ + -e 's/^regex[ \t][ \t]*\([^ \t][^ \t]*\)[ \t][ \t]*\(.*$0.*$1.*\)/regex_posix \1 method,size \2/' \ + -e 's/^regex[ \t][ \t]*\([^ \t][^ \t]*\)[ \t][ \t]*\(.*$1.*$0.*\)/regex_posix \1 size,method \2/' \ + -e 's/)$0/)/g' \ + -e 's/)$1/)/g' $infile | eval cat $outfile + +exit 0 |