summaryrefslogtreecommitdiff
path: root/src/pmieconf
diff options
context:
space:
mode:
authorIgor Pashev <pashev.igor@gmail.com>2014-10-26 12:33:50 +0400
committerIgor Pashev <pashev.igor@gmail.com>2014-10-26 12:33:50 +0400
commit47e6e7c84f008a53061e661f31ae96629bc694ef (patch)
tree648a07f3b5b9d67ce19b0fd72e8caa1175c98f1a /src/pmieconf
downloadpcp-debian.tar.gz
Debian 3.9.10debian/3.9.10debian
Diffstat (limited to 'src/pmieconf')
-rw-r--r--src/pmieconf/GNUmakefile81
-rw-r--r--src/pmieconf/GNUmakefile.rules55
-rwxr-xr-xsrc/pmieconf/check-rules82
-rw-r--r--src/pmieconf/cpu/context_switch59
-rw-r--r--src/pmieconf/cpu/excess_fpe73
-rw-r--r--src/pmieconf/cpu/load_average73
-rw-r--r--src/pmieconf/cpu/localdefs49
-rw-r--r--src/pmieconf/cpu/low_util69
-rw-r--r--src/pmieconf/cpu/syscall69
-rw-r--r--src/pmieconf/cpu/system73
-rw-r--r--src/pmieconf/cpu/util62
-rw-r--r--src/pmieconf/filesys/buffer_cache80
-rw-r--r--src/pmieconf/filesys/dnlc_miss72
-rw-r--r--src/pmieconf/filesys/filling99
-rw-r--r--src/pmieconf/filesys/localdefs51
-rw-r--r--src/pmieconf/global/enln_actions42
-rw-r--r--src/pmieconf/global/localdefs7
-rw-r--r--src/pmieconf/global/ov_actions53
-rw-r--r--src/pmieconf/global/parameters35
-rw-r--r--src/pmieconf/global/pcp_actions89
-rw-r--r--src/pmieconf/global/tngfw_actions45
-rw-r--r--src/pmieconf/global/web_parameters35
-rw-r--r--src/pmieconf/io.c229
-rw-r--r--src/pmieconf/memory/exhausted81
-rw-r--r--src/pmieconf/memory/localdefs29
-rw-r--r--src/pmieconf/memory/swap_low78
-rw-r--r--src/pmieconf/pcp_web21
-rw-r--r--src/pmieconf/percpu/context_switch70
-rw-r--r--src/pmieconf/percpu/localdefs45
-rw-r--r--src/pmieconf/percpu/many_util85
-rw-r--r--src/pmieconf/percpu/some_util83
-rw-r--r--src/pmieconf/percpu/syscall80
-rw-r--r--src/pmieconf/percpu/system84
-rw-r--r--src/pmieconf/pernetif/collisions76
-rw-r--r--src/pmieconf/pernetif/errors72
-rw-r--r--src/pmieconf/pernetif/localdefs22
-rw-r--r--src/pmieconf/pernetif/packets83
-rw-r--r--src/pmieconf/pernetif/util75
-rwxr-xr-xsrc/pmieconf/pmie_email65
-rw-r--r--src/pmieconf/pmieconf.c852
-rw-r--r--src/pmieconf/rate-syscalls.c140
-rw-r--r--src/pmieconf/rules.c2335
-rw-r--r--src/pmieconf/rules.h154
-rw-r--r--src/pmieconf/web/errors60
-rw-r--r--src/pmieconf/web/high_requests50
-rw-r--r--src/pmieconf/web/low_requests50
-rw-r--r--src/pmieconf/webping/connect_errors50
-rw-r--r--src/pmieconf/webping/html_errors50
-rw-r--r--src/pmieconf/webping/http_errors50
-rw-r--r--src/pmieconf/webping/no_response56
-rw-r--r--src/pmieconf/webping/other_errors51
-rw-r--r--src/pmieconf/webping/slow_response56
-rwxr-xr-xsrc/pmieconf/xtractnames70
53 files changed, 6655 insertions, 0 deletions
diff --git a/src/pmieconf/GNUmakefile b/src/pmieconf/GNUmakefile
new file mode 100644
index 0000000..9f9c639
--- /dev/null
+++ b/src/pmieconf/GNUmakefile
@@ -0,0 +1,81 @@
+#
+# Copyright (c) 2013 Red Hat.
+# 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.
+#
+
+TOPDIR = ../..
+include $(TOPDIR)/src/include/builddefs
+
+MKFILE_SUBDIRS = cpu filesys memory percpu pernetif global
+
+SUBDIRS = $(MKFILE_SUBDIRS)
+
+CMDTARGET = pmieconf$(EXECSUFFIX)
+CFILES = pmieconf.c rules.c io.c
+HFILES = rules.h
+
+PMLOGCONF_TOOLS = $(PCP_VAR_DIR)/config/pmlogconf/tools
+
+LSRCFILES = GNUmakefile.rules check-rules pmie_email xtractnames $(RFILES)
+
+LLDLIBS = $(PCPLIB)
+LCFLAGS = -I$(TOPDIR)/src/pmie/src
+LDIRT = local $(CMDTARGET) rate-syscalls \
+ pmlogconf.tmp pmlogconf \
+ cpu/GNUmakefile filesys/GNUmakefile memory/GNUmakefile \
+ percpu/GNUmakefile pernetif/GNUmakefile
+LDIRDIRT = rules .pcp
+
+default: $(CMDTARGET) makefiles local pmlogconf
+
+# for src-link-pcp target from buildrules
+$(SUBDIRS): makefiles
+
+$(CMDTARGET): $(OBJECTS)
+
+pmieconf.o rules.o: rules.h
+
+.NOTPARALLEL:
+makefiles:
+ @for d in $(MKFILE_SUBDIRS); do \
+ rm -f $$d/GNUmakefile; \
+ cd $$d; \
+ $(LN_S) ../GNUmakefile.rules GNUmakefile; \
+ cd ..; \
+ done
+
+local: $(SUBDIRS)
+ @rm -fr rules; mkdir rules
+ $(SUBDIRS_MAKERULE)
+ $(RUN_IN_BUILD_ENV) ./$(CMDTARGET) -F -r rules -f local
+
+pmlogconf: $(SUBDIRS)
+ @rm -f pmlogconf
+ @echo "#pmlogconf-setup 2.0" >pmlogconf
+ @echo "ident metrics used by pmie(1) rules from the pmieconf(1) command" >>pmlogconf
+ @echo "force available" >>pmlogconf
+ $(SUBDIRS_MAKERULE) | grep -v '===' >pmlogconf.tmp
+ @$(PCP_SORT_PROG) -u pmlogconf.tmp | sed -e 's/^/ /' >>pmlogconf
+
+install: default $(SUBDIRS)
+ $(SUBDIRS_MAKERULE)
+ $(INSTALL) -m 755 $(CMDTARGET) $(PCP_BIN_DIR)/$(CMDTARGET)
+ $(INSTALL) -m 755 pmie_email $(PCP_BINADM_DIR)/pmie_email
+ $(INSTALL) -m 644 pmlogconf $(PMLOGCONF_TOOLS)/pmieconf
+
+include $(BUILDRULES)
+
+default_pcp: default
+
+install_pcp: install
+
diff --git a/src/pmieconf/GNUmakefile.rules b/src/pmieconf/GNUmakefile.rules
new file mode 100644
index 0000000..6a12eba
--- /dev/null
+++ b/src/pmieconf/GNUmakefile.rules
@@ -0,0 +1,55 @@
+# 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
+
+# localdefs needs to set ALL_RULES (all the rules files to be included
+# in the builds and tar balls) and set LOCAL_RULES (the subset of the
+# rules that will work on the TARGET_OS platform)
+#
+include localdefs
+
+WORKDIR := $(shell pwd)
+GROUP := $(shell basename $(WORKDIR))
+RULESDIR = $(PCP_VAR_DIR)/config/pmieconf
+
+LDIRT = GNUmakefile
+
+LSRCFILES = localdefs $(ALL_RULES)
+
+CONFIGS = $(subst "./","",$(LOCAL_RULES))
+
+default_pcp: $(LOCAL_RULES)
+
+install_pcp: install
+
+install: default_pcp
+ $(INSTALL) -d $(RULESDIR)/$(GROUP)
+ @for f in $(CONFIGS); do \
+ $(INSTALL) -m 644 $$f $(RULESDIR)/$(GROUP)/$$f; \
+ done
+
+local:
+ @test ! -d ../rules/$(GROUP) && mkdir -p ../rules/$(GROUP); exit 0
+ @rm -f ../rules/$(GROUP)/*
+ @for f in IGNORE_DUMMY_RULE $(LOCAL_RULES); do \
+ [ $$f = IGNORE_DUMMY_RULE ] && continue; \
+ cp ../$(GROUP)/$$f ../rules/$(GROUP)/$$f; \
+ sed -e's|/usr/pcp/lib/pmie_email|$(PCP_BINADM_DIR)/pmie_email|' \
+ -e's|/usr/pcp/bin/pmpost|$(PCP_BINADM_DIR)/pmpost|' <$$f \
+ >../rules/$(GROUP)/$$f; \
+ done
+
+pmlogconf:
+ @if [ -n "$(CONFIGS)" ]; then sh ../xtractnames $(CONFIGS); fi
+
+include $(BUILDRULES)
diff --git a/src/pmieconf/check-rules b/src/pmieconf/check-rules
new file mode 100755
index 0000000..b61421d
--- /dev/null
+++ b/src/pmieconf/check-rules
@@ -0,0 +1,82 @@
+#!/bin/sh
+#
+# 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
+
+
+# Once the "rules" directory is populated, extract all the PCP metric
+# names and make sure they are available on the current host or the
+# host specified by -h hostname
+#
+
+_usage()
+{
+ echo "Usage: $0 [-h hostname]"
+ exit 1
+}
+
+source=''
+while getopts "h:?" c
+do
+ case $c
+ in
+ h)
+ source="-h $OPTARG"
+ ;;
+ ?)
+ _usage
+ # NOTREACHED
+ ;;
+ esac
+done
+shift `expr $OPTIND - 1`
+if [ $# -ne 0 ]
+then
+ _usage
+ # NOTREACHED
+fi
+
+if [ ! -d rules ]
+then
+ echo "$0: cannot find \"rules\" directory!"
+ exit 1
+fi
+
+find rules -follow -type f -print \
+| LC_COLLATE=POSIX sort \
+| while read rule
+do
+ badrule=false
+ ./xtractnames <$rule \
+ | while read metric
+ do
+
+ probe=`pmprobe $source $metric`
+ numval=`echo $probe | awk '{print $2}'`
+ if [ $numval -lt 0 ]
+ then
+ if $badrule
+ then
+ :
+ else
+ echo
+ echo "$rule"
+ echo "#"
+ echo "# rule: `basename $rule`"
+ badrule=true
+ fi
+ echo "# $probe"
+ fi
+ done
+done
diff --git a/src/pmieconf/cpu/context_switch b/src/pmieconf/cpu/context_switch
new file mode 100644
index 0000000..fc2b8ac
--- /dev/null
+++ b/src/pmieconf/cpu/context_switch
@@ -0,0 +1,59 @@
+#pmieconf-rules 1
+# --- DO NOT MODIFY THIS FILE --- see pmieconf(4)
+#
+
+rule cpu.context_switch
+ summary = "$rule$"
+ predicate =
+"some_host (
+ kernel.all.pswitch $hosts$
+ > hinv.ncpu $hosts$ * $threshold$ count/sec
+)"
+ enabled = yes
+ version = 1
+ help =
+"Average number of context switches per CPU per second exceeded
+threshold over the past sample interval.";
+
+string rule
+ default = "High aggregate context switch rate"
+ modify = no
+ display = no;
+
+double threshold
+ default = 4000
+ help =
+"The threshold number of process context switches per second.";
+
+string action_expand
+ default = %vctxsw/s@%h
+ display = no
+ modify = no;
+
+string email_expand
+ default = "host: %h aggregate context switches: %v/sec"
+ display = no
+ modify = no;
+
+
+# Configuration info specific to non-PCP tools follows...
+#
+
+# for SGI Embedded Support Partner integration:
+string esp_type
+ default = "0x20005C"
+ display = no
+ modify = no;
+
+# for EnlightenDSM integration:
+string enln_test
+ default = cpu.context_switch
+ display = no
+ modify = no;
+string enln_units
+ default = ctxsw/s
+ display = no
+ modify = no;
+
+#
+# --- DO NOT MODIFY THIS FILE --- see pmieconf(4)
diff --git a/src/pmieconf/cpu/excess_fpe b/src/pmieconf/cpu/excess_fpe
new file mode 100644
index 0000000..90b62dc
--- /dev/null
+++ b/src/pmieconf/cpu/excess_fpe
@@ -0,0 +1,73 @@
+#pmieconf-rules 1
+# --- DO NOT MODIFY THIS FILE --- see pmieconf(4)
+#
+
+rule cpu.excess_fpe
+ summary = "$rule$"
+ enumerate = hosts
+ predicate =
+"some_host (
+ some_inst (
+ ( 100 * kernel.percpu.cpu.sys $hosts$ > $systime_util$ )
+ && kernel.percpu.syscall $hosts$ < $syscall_rate$
+ )
+)"
+ enabled = no
+ version = 1
+ help =
+"This predicate attempts to detect processes generating very large
+numbers of floating point exceptions (FPEs). Characteristic of
+this situation is heavy system time coupled with low system call
+rates (exceptions are delivered through the kernel to the process,
+taking some system time, but no system call is serviced on the
+application's behalf).";
+
+string rule
+ default = "Possible high floating point exception rate"
+ modify = no
+ display = no;
+
+percent systime_util
+ default = 50
+ help =
+"Threshold percentage for kernel CPU utilization, in the range 0
+(idle) to 100 (completely busy)";
+
+double syscall_rate
+ default = 100
+ help =
+"Threshold system call rate (calls per second) below which something
+is deemed amiss.";
+
+string action_expand
+ default = %v%sys[%i]@%h
+ display = no
+ modify = no;
+
+string email_expand
+ default = "host: %h CPU: %i system mode: %v% and low syscall rate"
+ display = no
+ modify = no;
+
+
+# Configuration info specific to non-PCP tools follows...
+#
+
+# for SGI Embedded Support Partner integration:
+string esp_type
+ default = "0x200041"
+ display = no
+ modify = no;
+
+# for EnlightenDSM integration:
+string enln_test
+ default = cpu.excess_fpe
+ display = no
+ modify = no;
+string enln_units
+ default = %sys[%i]
+ display = no
+ modify = no;
+
+#
+# --- DO NOT MODIFY THIS FILE --- see pmieconf(4)
diff --git a/src/pmieconf/cpu/load_average b/src/pmieconf/cpu/load_average
new file mode 100644
index 0000000..95b4c5d
--- /dev/null
+++ b/src/pmieconf/cpu/load_average
@@ -0,0 +1,73 @@
+#pmieconf-rules 1
+# --- DO NOT MODIFY THIS FILE --- see pmieconf(4)
+#
+
+rule cpu.load_average
+ summary = "$rule$"
+ predicate =
+"some_host (
+ // threshold scales with the number of CPUs (works better for
+ // large systems) and there is an absolute lower bound,
+ // especially for small systems
+ kernel.all.load $hosts$ #'1 minute' > hinv.ncpu $hosts$ * $per_cpu_load$
+ && kernel.all.load $hosts$ #'1 minute' > $min_load$
+)"
+ enabled = yes
+ version = 1
+ help =
+"The current 1-minute load average is higher than the larger of
+min_load and ( per_cpu_load times the number of CPUs ).
+The load average measures the number of processes that are running,
+runnable or soon to be runnable (i.e. in short term sleep).";
+
+string rule
+ default = "High 1-minute load average"
+ modify = no
+ display = no;
+
+double per_cpu_load
+ default = 3
+ help =
+"The multiplier per CPU for the minimum load to make the rule true,
+when expressed as a function of the number of CPUs. Typically in
+the range 1.0 (very light load) to 8.0 (very heavy load ).";
+
+double min_load
+ default = 4
+ help =
+"The minimum load average before the rule is true. Most useful for
+single-processor systems or where the desired threshold is
+absolute, rather than a function of the number of CPUs.";
+
+string action_expand
+ default = %vload@%h
+ display = no
+ modify = no;
+
+string email_expand
+ default = "host: %h load average: %v"
+ display = no
+ modify = no;
+
+
+# Configuration info specific to non-PCP tools follows...
+#
+
+# for SGI Embedded Support Partner integration:
+string esp_type
+ default = "0x200042"
+ display = no
+ modify = no;
+
+# for EnlightenDSM integration:
+string enln_test
+ default = cpu.load_average
+ display = no
+ modify = no;
+string enln_units
+ default = load
+ display = no
+ modify = no;
+
+#
+# --- DO NOT MODIFY THIS FILE --- see pmieconf(4)
diff --git a/src/pmieconf/cpu/localdefs b/src/pmieconf/cpu/localdefs
new file mode 100644
index 0000000..dbe7c39
--- /dev/null
+++ b/src/pmieconf/cpu/localdefs
@@ -0,0 +1,49 @@
+ALL_RULES = util syscall context_switch system load_average \
+ excess_fpe low_util
+
+LOCAL_RULES = $(ALL_RULES)
+
+# Metrics missing from Linux
+#
+# rule: excess_fpe
+# kernel.percpu.syscall -12357 Unknown metric name
+#
+# rule: syscall
+# kernel.all.syscall -12357 Unknown metric name
+#
+ifeq ($(TARGET_OS), linux)
+LOCAL_RULES = util context_switch system load_average low_util
+endif
+
+# Metrics missing from Mac OS X
+#
+# rule: util
+# kernel.all.cpu.intr -12357 Unknown metric name
+#
+# rule: syscall
+# kernel.all.syscall -12357 Unknown metric name
+#
+# rule: context_switch
+# kernel.all.pswitch -12357 Unknown metric name
+#
+# rule: excess_fpe
+# kernel.percpu.syscall -12357 Unknown metric name
+#
+# rule: low_util
+# kernel.all.cpu.intr -12357 Unknown metric name
+#
+ifeq ($(TARGET_OS), darwin)
+LOCAL_RULES = system load_average
+endif
+
+# Metrics missing from Solaris
+#
+# rule: low_util
+# kernel.all.cpu.intr -12357 Unknown metric name
+#
+# rule: util
+# kernel.all.cpu.intr -12357 Unknown metric name
+#
+ifeq ($(TARGET_OS), solaris)
+LOCAL_RULES = syscall context_switch system load_average excess_fpe
+endif
diff --git a/src/pmieconf/cpu/low_util b/src/pmieconf/cpu/low_util
new file mode 100644
index 0000000..de6c0da
--- /dev/null
+++ b/src/pmieconf/cpu/low_util
@@ -0,0 +1,69 @@
+#pmieconf-rules 1
+# --- DO NOT MODIFY THIS FILE --- see pmieconf(4)
+#
+
+rule cpu.low_util
+ summary = "$rule$"
+ predicate =
+"some_host (
+ // kernel.all.cpu metrics count up to 1 second of CPU time
+ // per second per CPU
+ 100 * ( kernel.all.cpu.user $hosts$ +
+ kernel.all.cpu.sys $hosts$ +
+ kernel.all.cpu.intr $hosts$ ) / hinv.ncpu $hosts$
+ < $threshold$
+)"
+ enabled = no
+ version = 1
+ help =
+"The average processor utilization over all CPUs was below threshold
+percent during the last sample interval.
+This rule is effectively the opposite of cpu.util and is disabled by
+default - it is only useful in specialized environments where, for
+example, processing is batch oriented and low processor utilization
+is indicative of poor use of system resources. In such a situation
+the cpu.low_util rule should be enabled, and cpu.util disabled.";
+
+string rule
+ default = "Low average processor utilization"
+ modify = no
+ display = no;
+
+percent threshold
+ default = 25
+ help =
+"Lower bound percentage for CPU utilization, in the range 0 (idle)
+to 100 (completely busy), independent of the number of CPUs.";
+
+string action_expand
+ default = %v%util@%h
+ display = no
+ modify = no;
+
+string email_expand
+ default = "host: %h average CPU utilization: %v%"
+ display = no
+ modify = no;
+
+
+# Configuration info specific to non-PCP tools follows...
+#
+
+# for SGI Embedded Support Partner integration:
+string esp_type
+ default = "0x20005E"
+ display = no
+ modify = no;
+
+# for EnlightenDSM integration:
+string enln_test
+ default = cpu.low_util
+ display = no
+ modify = no;
+string enln_units
+ default = %util
+ display = no
+ modify = no;
+
+#
+# --- DO NOT MODIFY THIS FILE --- see pmieconf(4)
diff --git a/src/pmieconf/cpu/syscall b/src/pmieconf/cpu/syscall
new file mode 100644
index 0000000..4dcf074
--- /dev/null
+++ b/src/pmieconf/cpu/syscall
@@ -0,0 +1,69 @@
+#pmieconf-rules 1
+# --- DO NOT MODIFY THIS FILE --- see pmieconf(4)
+#
+
+rule cpu.syscall
+ summary = "$rule$"
+ predicate =
+"some_host (
+ kernel.all.syscall $hosts$
+ > hinv.ncpu $hosts$ * $threshold$ count/sec
+)"
+ enabled = yes
+ version = 1
+ help =
+"Average number of system calls per CPU per second exceeded
+threshold over the past sample interval.";
+
+string rule
+ default = "High aggregate system call rate"
+ modify = no
+ display = no;
+
+double threshold
+ default = 10000
+ help =
+"The threshold of system calls per second per CPU. The appropriate
+value here is a function of the processor type and the workload, but
+here are some indicative figures of sustained system call rates for a
+single process:
+ getpid() - 380000 syscalls/sec
+ lseek() to start of file - 280000 syscalls/sec
+ gettimeofday() - 200000 syscalls/sec
+ read() at end of file - 83000 syscalls/sec
+ file creat() and close() - 65000 syscalls/sec
+ socket(), connect() and close() - 7000 syscalls/sec
+(generated using an otherwise idle system with 180MHz R10000 processors).";
+
+string action_expand
+ default = %vscall/s@%h
+ display = no
+ modify = no;
+
+string email_expand
+ default = "host: %h aggregate syscalls/sec: %v"
+ display = no
+ modify = no;
+
+
+# Configuration info specific to non-PCP tools follows...
+#
+
+# for SGI Embedded Support Partner integration:
+string esp_type
+ default = "0x200043"
+ display = no
+ modify = no;
+
+# for EnlightenDSM integration:
+string enln_test
+ default = cpu.syscall
+ display = no
+ modify = no;
+string enln_units
+ default = scall/s
+ display = no
+ modify = no;
+
+#
+# --- DO NOT MODIFY THIS FILE --- see pmieconf(4)
diff --git a/src/pmieconf/cpu/system b/src/pmieconf/cpu/system
new file mode 100644
index 0000000..04d61a5
--- /dev/null
+++ b/src/pmieconf/cpu/system
@@ -0,0 +1,73 @@
+#pmieconf-rules 1
+# --- DO NOT MODIFY THIS FILE --- see pmieconf(4)
+#
+
+rule cpu.system
+ summary = "$rule$"
+ predicate =
+"some_host (
+ // first term is always true, but provides %v for actions ...
+ ( 100 * kernel.all.cpu.sys $hosts$ / hinv.ncpu $hosts$ ) > 0
+ && 100 * ( kernel.all.cpu.user $hosts$ + kernel.all.cpu.sys $hosts$ )
+ > $busy$ * hinv.ncpu $hosts$
+ && 100 * kernel.all.cpu.sys $hosts$ /
+ ( kernel.all.cpu.user $hosts$ + kernel.all.cpu.sys $hosts$ )
+ > $threshold$
+)"
+ enabled = yes
+ version = 1
+ help =
+"Over the last sample interval, the average utilization per CPU was
+busy percent or more, and the ratio of system time to busy time
+exceeded threshold percent.";
+
+string rule
+ default = "Busy executing in system mode"
+ modify = no
+ display = no;
+
+percent busy
+ default = 70
+ help =
+"Busy percentage for average CPU utilization, in the range 0 (idle)
+to 100 (completely busy), independent of the number of CPUs.";
+
+percent threshold
+ default = 75
+ help =
+"Threshold percentage for system time as a fraction of the non-idle
+CPU time, in the range 0 (no system time) to 100 (all system time),
+independent of the number of CPUs.";
+
+string action_expand
+ default = %v%sys@%h
+ display = no
+ modify = no;
+
+string email_expand
+ default = "host: %h system mode: %v%"
+ display = no
+ modify = no;
+
+
+# Configuration info specific to non-PCP tools follows...
+#
+
+# for SGI Embedded Support Partner integration:
+string esp_type
+ default = "0x200044"
+ display = no
+ modify = no;
+
+# for EnlightenDSM integration:
+string enln_test
+ default = cpu.system
+ display = no
+ modify = no;
+string enln_units
+ default = %sys
+ display = no
+ modify = no;
+
+#
+# --- DO NOT MODIFY THIS FILE --- see pmieconf(4)
diff --git a/src/pmieconf/cpu/util b/src/pmieconf/cpu/util
new file mode 100644
index 0000000..be21b5c
--- /dev/null
+++ b/src/pmieconf/cpu/util
@@ -0,0 +1,62 @@
+#pmieconf-rules 1
+# --- DO NOT MODIFY THIS FILE --- see pmieconf(4)
+#
+
+rule cpu.util
+ summary = "$rule$"
+ predicate =
+"some_host (
+ 100 * ( kernel.all.cpu.user $hosts$ +
+ kernel.all.cpu.sys $hosts$ +
+ kernel.all.cpu.intr $hosts$ ) / hinv.ncpu $hosts$
+ > $threshold$
+)"
+ enabled = yes
+ version = 1
+ help =
+"The average processor utilization over all CPUs exceeded threshold
+percent during the last sample interval.";
+
+string rule
+ default = "High average processor utilization"
+ modify = no
+ display = no;
+
+percent threshold
+ default = 90
+ help =
+"Threshold percentage for CPU saturation, in the range 0 (idle)
+to 100 (completely busy), independent of the number of CPUs.";
+
+string action_expand
+ default = %v%util@%h
+ display = no
+ modify = no;
+
+string email_expand
+ default = "host: %h average CPU utilization: %v%"
+ display = no
+ modify = no;
+
+
+# Configuration info specific to non-PCP tools follows...
+#
+
+# for SGI Embedded Support Partner integration:
+string esp_type
+ default = "0x200045"
+ display = no
+ modify = no;
+
+# for EnlightenDSM integration:
+string enln_test
+ default = cpu.util
+ display = no
+ modify = no;
+string enln_units
+ default = %util
+ display = no
+ modify = no;
+
+#
+# --- DO NOT MODIFY THIS FILE --- see pmieconf(4)
diff --git a/src/pmieconf/filesys/buffer_cache b/src/pmieconf/filesys/buffer_cache
new file mode 100644
index 0000000..47878b4
--- /dev/null
+++ b/src/pmieconf/filesys/buffer_cache
@@ -0,0 +1,80 @@
+#pmieconf-rules 1
+# --- DO NOT MODIFY THIS FILE --- see pmieconf(4)
+#
+
+rule filesys.buffer_cache
+ summary = "$rule$"
+ predicate =
+"some_host (
+ 100 * ( kernel.all.io.lread $hosts$ -
+ kernel.all.io.bread $hosts$
+ / kernel.all.io.lread $hosts$ ) < $threshold$
+ && kernel.all.io.lread $hosts$ > $min_lread$ Kbytes/sec
+)"
+ enabled = yes
+ version = 1
+ help =
+"Some filesystem read activity (at least min_lread Kbytes per
+second of logical reads), and the read hit ratio in the buffer
+cache is below threshold percent.
+Note: It is possible for the read hit ratio to be negative
+(more phsical reads than logical reads) - this can be as a
+result of:
+ o XLV striped volumes, where blocks span stripe boundaries;
+ o very large files, where the disk controller has to read
+ blocks indirectly (multiple block reads to find a single
+ data block result);
+ o file system read-ahead pre-fetching blocks which are not
+ subsequently read.";
+
+string rule
+ default = "Low buffer cache read hit ratio"
+ modify = no
+ display = no;
+
+percent threshold
+ default = 50
+ help =
+"The minimum acceptable buffer cache read hit ratio, expressed as a
+percentage. Values may be in the range 0 (nothing is read from the
+cache and poor performance is expected) to 100 (all reads come from
+the cache, no disk I/O required and good performance expected).";
+
+double min_lread
+ default = 512
+ help =
+"Unless at least min_lread Kbytes per second are passing across the
+logical filesystem read interface the rule will not be true.";
+
+string action_expand
+ default = %v%rcach@%h
+ display = no
+ modify = no;
+
+string email_expand
+ default = "host: %h buffer cache read hit ratio: %v%"
+ display = no
+ modify = no;
+
+
+# Configuration info specific to non-PCP tools follows...
+#
+
+# for SGI Embedded Support Partner integration:
+string esp_type
+ default = "0x200049"
+ display = no
+ modify = no;
+
+# for EnlightenDSM integration:
+string enln_test
+ default = filesys.buffer_cache
+ display = no
+ modify = no;
+string enln_units
+ default = %rcach
+ display = no
+ modify = no;
+
+#
+# --- DO NOT MODIFY THIS FILE --- see pmieconf(4)
diff --git a/src/pmieconf/filesys/dnlc_miss b/src/pmieconf/filesys/dnlc_miss
new file mode 100644
index 0000000..59264de
--- /dev/null
+++ b/src/pmieconf/filesys/dnlc_miss
@@ -0,0 +1,72 @@
+#pmieconf-rules 1
+# --- DO NOT MODIFY THIS FILE --- see pmieconf(4)
+#
+
+rule filesys.dnlc_miss
+ default = "$rule$"
+ predicate =
+"some_host (
+ 100 * ( name_cache.misses $hosts$ +
+ name_cache.enters $hosts$ +
+ name_cache.removes $hosts$ )
+ / name_cache.searches $hosts$ > $threshold$
+ && name_cache.searches $hosts$
+ > $min_lookup$ count/sec
+)"
+ enabled = no
+ version = 1
+ help =
+"With at least min_lookup directory name cache (DNLC) lookups per
+second being performed, threshold percent of lookups result in
+cache misses.";
+
+string rule
+ default = "High directory name cache miss rate"
+ modify = no
+ display = no;
+
+percent threshold
+ default = 90
+ help =
+"Threshold percent of total directory name cache lookups are resulting
+in cache misses, in the range 0 (all accesses are satisified in the
+cache) to 100 (no accesses are satisifed in the cache).";
+
+double min_lookup
+ default = 100
+ help =
+"Minimum number of name cache lookups per second before considering
+whether these lookups are stressing the cache or not.";
+
+string action_expand
+ default = "%v%miss@%h"
+ display = no
+ modify = no;
+
+string email_expand
+ default = "host: %h name cache misses: %v%"
+ display = no
+ modify = no;
+
+
+# Configuration info specific to non-PCP tools follows...
+#
+
+# for SGI Embedded Support Partner integration:
+string esp_type
+ default = "0x200040"
+ display = no
+ modify = no;
+
+# for EnlightenDSM integration:
+string enln_test
+ default = filesys.dnlc_miss
+ display = no
+ modify = no;
+string enln_units
+ default = %miss
+ display = no
+ modify = no;
+
+#
+# --- DO NOT MODIFY THIS FILE --- see pmieconf(4)
diff --git a/src/pmieconf/filesys/filling b/src/pmieconf/filesys/filling
new file mode 100644
index 0000000..b54b308
--- /dev/null
+++ b/src/pmieconf/filesys/filling
@@ -0,0 +1,99 @@
+#pmieconf-rules 1
+# --- DO NOT MODIFY THIS FILE --- see pmieconf(4)
+#
+
+rule filesys.filling
+ summary = "$rule$"
+ enumerate = hosts
+ predicate =
+"some_host (
+ some_inst (
+ ( 100 * filesys.used $hosts$ / filesys.capacity $hosts$ ) > $threshold$
+ && filesys.used $hosts$ +
+ $lead_time$ * ( rate filesys.used $hosts$ ) >
+ filesys.capacity $hosts$
+ )
+)"
+ enabled = yes
+ version = 1
+ help =
+"Filesystem is at least threshold percent full and the used space
+is growing at a rate that would see the filesystem full within
+lead_time.";
+
+string rule
+ default = "File system is filling up"
+ modify = no
+ display = no;
+
+percent threshold
+ default = 95
+ help =
+"The threshold of filesystem fullness expressed as a percentage,
+usually in the range 90 to 99.";
+
+string lead_time
+ default = "20 min"
+ help =
+"The rule is true if the filesystem would be full within this time
+given the recent rate of growth. Normally requires a scale such as
+\"sec\", \"min\" or \"hr\", otherwise the value is interpreted as
+meaning seconds.";
+
+string delta
+ default = "4 mins"
+ help =
+"Sample interval between evaluations of this rule. The calculation
+of the projected rate of growth is sensitive to variations in the
+observed fullness of the filesystem. Adjust this parameter to be
+smaller if the filesystems' fullness are very stable and you want
+earlier warning of impending filling. Else make the parameter
+larger to avoid false warnings if the filesystems are close to full
+in the normal state and subject to bursts of file creation and
+deletion.";
+
+string action_expand
+ default = %v%used[%i]@%h
+ display = no
+ modify = no;
+
+string email_expand
+ default = "host: %h filesystem: %i used: %v%"
+ display = no
+ modify = no;
+
+
+# Configuration info specific to non-PCP tools follows...
+#
+
+# for SGI Embedded Support Partner integration:
+string esp_type
+ default = "0x20004A"
+ display = no
+ modify = no;
+
+# for HP OpenView integration:
+string ov_severity
+ display = no
+ default = "Critical";
+
+# for CA/Unicenter TNG integration:
+string tngfw_color
+ display = no
+ default = "Red";
+
+# for EnlightenDSM integration:
+string enln_test
+ default = filesys.filling
+ display = no
+ modify = no;
+string enln_units
+ default = %used[%i]
+ display = no
+ modify = no;
+unsigned enln_severity
+ display = no
+ default = 5;
+
+#
+# --- DO NOT MODIFY THIS FILE --- see pmieconf(4)
diff --git a/src/pmieconf/filesys/localdefs b/src/pmieconf/filesys/localdefs
new file mode 100644
index 0000000..3945214
--- /dev/null
+++ b/src/pmieconf/filesys/localdefs
@@ -0,0 +1,51 @@
+ALL_RULES = buffer_cache dnlc_miss filling
+
+LOCAL_RULES = $(ALL_RULES)
+
+# Metrics missing from Linux
+#
+# rule: buffer_cache
+# kernel.all.io.bread -12357 Unknown metric name
+# kernel.all.io.lread -12357 Unknown metric name
+#
+# rule: dnlc_miss
+# name_cache.enters -12357 Unknown metric name
+# name_cache.misses -12357 Unknown metric name
+# name_cache.removes -12357 Unknown metric name
+# name_cache.searches -12357 Unknown metric name
+#
+ifeq ($(TARGET_OS), linux)
+LOCAL_RULES = filling
+endif
+
+# Metrics missing from Mac OS X
+#
+# rule: buffer_cache
+# kernel.all.io.bread -12357 Unknown metric name
+# kernel.all.io.lread -12357 Unknown metric name
+#
+# rule: dnlc_miss
+# name_cache.enters -12357 Unknown metric name
+# name_cache.misses -12357 Unknown metric name
+# name_cache.removes -12357 Unknown metric name
+# name_cache.searches -12357 Unknown metric name
+#
+ifeq ($(TARGET_OS), darwin)
+LOCAL_RULES = filling
+endif
+
+# Metrics missing from Solaris
+#
+# rule: dnlc_miss
+# name_cache.enters -12357 Unknown metric name
+# name_cache.misses -12357 Unknown metric name
+# name_cache.removes -12357 Unknown metric name
+# name_cache.searches -12357 Unknown metric name
+#
+# rule: filling
+# filesys.capacity -12357 Unknown metric name
+# filesys.used -12357 Unknown metric name
+#
+ifeq ($(TARGET_OS), solaris)
+LOCAL_RULES = buffer_cache
+endif
diff --git a/src/pmieconf/global/enln_actions b/src/pmieconf/global/enln_actions
new file mode 100644
index 0000000..4d48bd1
--- /dev/null
+++ b/src/pmieconf/global/enln_actions
@@ -0,0 +1,42 @@
+#pmieconf-rules 1
+# --- DO NOT MODIFY THIS FILE --- see pmieconf(4)
+#
+
+shell global.enln_action
+ enabled = no
+ default = "$enln_bin$/EventsCli -q -s '$enln_severity$' -S N/A -T N/A -n 'pmie/$enln_test$ - $rule$' -h '%h' -v '%v' -u '$enln_units$'"
+ help =
+"Provides a mechanism for sending local pmie-generated events into
+the local Enlighten DSM management framework.
+This action requires the \"EventsCli\" program which is part of the
+Enlighten DSM framework (refer to the enln_bin variable description
+also).";
+
+string global.enln_bin
+ default = "/opt/enlighten/bin"
+ help =
+"The full path to the Enlighten DSM \"EventsCli\" program, which is used
+to propagate external events into the Enlighten DSM Framework.";
+
+unsigned global.enln_severity
+ default = 2
+ display = no
+ help =
+"The severity is a number between a low of 1 and a high of 5 (1=OK,
+2=Informational, 3=Warning, 4=Error, 5=Severe). The default value is 2.";
+
+string global.enln_test
+ default = "Unknown"
+ display = no
+ help =
+"The brief name of the pmie event being sent to Enlighten DSM, typically
+the name of an individual rule.";
+
+string global.enln_units
+ default = "N/A"
+ display = no
+ help =
+"The units value specifies the unit of measure for the value measured.";
+
+#
+# --- DO NOT MODIFY THIS FILE --- see pmieconf(4)
diff --git a/src/pmieconf/global/localdefs b/src/pmieconf/global/localdefs
new file mode 100644
index 0000000..cbebc2c
--- /dev/null
+++ b/src/pmieconf/global/localdefs
@@ -0,0 +1,7 @@
+ALL_RULES = enln_actions tngfw_actions parameters ov_actions pcp_actions
+
+LOCAL_RULES = $(ALL_RULES)
+
+ifneq ($(TARGET_OS), irix)
+LOCAL_RULES = parameters pcp_actions
+endif
diff --git a/src/pmieconf/global/ov_actions b/src/pmieconf/global/ov_actions
new file mode 100644
index 0000000..dc0652d
--- /dev/null
+++ b/src/pmieconf/global/ov_actions
@@ -0,0 +1,53 @@
+#pmieconf-rules 1
+# --- DO NOT MODIFY THIS FILE --- see pmieconf(4)
+#
+
+shell global.ov_action
+ enabled = no
+ default = "OID=.1.3.6.1.4.1.11.2.17; export OID; $ov_bin$/ovevent -c '$ov_category$' -s '$ov_severity$' '$ov_node$' ^\\\\${OID}.1.0.58916872 \\\\${OID}.2.1.0 Integer 0 \\\\${OID}.2.2.0 OctetString '%h' \\\\${OID}.2.4.0 OctetString '$rule$^ $action_expand$^'"
+ # 4 backquotes gives a shell variable - '$' is a special character
+ # to pmieconf, pmie, and the shell - so need to backquote it up the
+ # whazoo to get this to come out right!
+ #
+ help =
+"The HP OpenView Network Node Manager event subsystem on ov_node will
+receive an OV_Message event when the rule condition is true.
+This action requires the ovevent(1) program which is part of the HP
+OpenView package (refer to the ov_bin variable description also).";
+
+string global.ov_bin
+ default = "/opt/OV/bin"
+ help =
+"The full path to the HP OpenView ovevent(1) program, which is used to
+propagate external events into the OpenView framework.";
+
+string global.ov_node
+ default = ""
+ help =
+"The node on which the HP OpenView pmd(1M) daemon is running, which
+will reliably broadcast the pmie event to all interested HP OpenView
+processes.
+The node can be either an Internet address or host name (see hosts(4)),
+and is usually the local host except when run from a management console
+(client host). An empty string is equivalent to the local host.";
+
+string global.ov_severity
+ default = "Warning"
+ display = no
+ help =
+"Severity with which an event will be reported to the HP OpenView node
+manager on ov_node. Valid values are \"Critical\", \"Major\", \"Minor\",
+\"Warning\" and \"Normal\".";
+
+string global.ov_category
+ default = "Threshold Events"
+ display = no
+ help =
+"Category with which an event will be reported to the HP OpenView node
+manager on ov_node. The category must be one of the existing event
+categories; the default categories defined in trapd.conf(4) are:
+\"IGNORE\", \"LOGONLY\", \"Error Events\", \"Threshold Events\", \"Status
+Events\", \"Configuration Events\", and \"Application Alert Events\".";
+
+#
+# --- DO NOT MODIFY THIS FILE --- see pmieconf(4)
diff --git a/src/pmieconf/global/parameters b/src/pmieconf/global/parameters
new file mode 100644
index 0000000..38b16c7
--- /dev/null
+++ b/src/pmieconf/global/parameters
@@ -0,0 +1,35 @@
+#pmieconf-rules 1
+# --- DO NOT MODIFY THIS FILE --- see pmieconf(4)
+#
+
+# variable definitions applicable to all rules
+# (unless overridden at the level of an individual rule or group)
+#
+
+string global.delta
+ default = "2 min"
+ help =
+"Sample interval between evaluations of this rule. Default units
+are seconds and common units are \"second\", \"sec\", \"minute\",
+\"min\" and \"hour\".";
+
+string global.holdoff
+ default = "10 min"
+ help =
+"Once the predicate is true and the action is executed, this
+variable allows suppression of further action execution until the
+specified interval has elapsed. A value of zero enables execution
+of the action if the rule predicate is true at the next sample.
+Default units are seconds and common units are \"second\", \"sec\",
+\"minute\", \"min\" and \"hour\".";
+
+hostlist global.hosts
+ default = ""
+ help =
+"May be set to a list of host names for which the rules will be
+evaluated. Multiple hostnames should be separated by white space.
+If the list is empty, the host will be the host named in the -h
+option to pmie(1) if specified, else the local host.";
+
+#
+# --- DO NOT MODIFY THIS FILE --- see pmieconf(4)
diff --git a/src/pmieconf/global/pcp_actions b/src/pmieconf/global/pcp_actions
new file mode 100644
index 0000000..104543c
--- /dev/null
+++ b/src/pmieconf/global/pcp_actions
@@ -0,0 +1,89 @@
+#pmieconf-rules 1
+# --- DO NOT MODIFY THIS FILE --- see pmieconf(4)
+#
+
+# action definitions applicable to all rules follow
+#
+# o $rule$ defined locally & contains the "message" to be propagated
+#
+# o $*_expand$ often overridden locally & contains the part of the
+# action string which is rule-semantics specific and will be expanded
+# possibly multiple times on truthful evaluation of the predicate to
+# contain values/instances/hosts which matched,e.g:
+# "%v@%h" might expand to "1.1@moomba 1.7@rattle 4.5@wobbly" if all
+# the rule is true for all of the hosts moomba, rattle and wobbly.
+#
+# some common alternatives:
+# %i@%h (inst@host)
+# %h (host)
+# %v[%i]@%h (value:inst@host)
+# %v%@%h (value%@host)
+#
+
+string global.action_expand
+ display = no
+ modify = no
+ default = "%v@%h"; # (value@host)
+
+string global.email_expand
+ display = no
+ modify = no
+ default = "%v@%h"; # (value@host)
+
+
+shell global.user_action
+ enabled = no
+ default = "$user_command$"
+ help =
+"Execute \"user_command\" when the rule condition is true";
+
+string global.user_command
+ default = "echo $rule$^ $action_expand$"
+ help =
+"Shell (sh(1)) command line to execute when rule condition is true
+and \"user_action\" is enabled.";
+
+
+shell global.email_action
+ enabled = no
+ default = "pmie_email '$email_recipients$|$rule$^|$email_expand$^'"
+ help =
+"A mail message will be sent to \"email_recipients\" when the rule
+condition is true.";
+
+string global.email_recipients
+ default = "root"
+ help =
+"Space separated list of e-mail addresses for notification from the
+\"email_action\" when it is enabled";
+
+
+shell global.pcplog_action
+ enabled = no
+ default = "pmpost pmie: $rule$^ $action_expand$"
+ help =
+"The PCP notices file $PCP_LOG_DIR/NOTICES will be updated when
+the rule condition is true.";
+
+
+syslog global.syslog_action
+ enabled = yes
+ default = "$syslog_prefix$$rule$^ $action_expand$"
+ help =
+"The system log file (usually /var/log/messages) will be updated
+when the rule condition is true.";
+
+string global.syslog_prefix
+ display = no
+ modify = no
+ default = "";
+
+# for SGI Embedded Support Partner integration, use:
+# $ pmieconf modify global syslog_prefix '$esp_prefix$'
+string global.esp_prefix
+ display = no
+ modify = no
+ default = "|\\\\$($esp_type$)";
+
+#
+# --- DO NOT MODIFY THIS FILE --- see pmieconf(4)
diff --git a/src/pmieconf/global/tngfw_actions b/src/pmieconf/global/tngfw_actions
new file mode 100644
index 0000000..36ade07
--- /dev/null
+++ b/src/pmieconf/global/tngfw_actions
@@ -0,0 +1,45 @@
+#pmieconf-rules 1
+# --- DO NOT MODIFY THIS FILE --- see pmieconf(4)
+#
+
+shell global.tngfw_action
+ enabled = no
+ default = "$tngfw_bin$/cawto -n '$tngfw_node$' -c '$tngfw_color$' -g $tngfw_category$ -s `/usr/bsd/hostname` '$rule$^ $action_expand$^'"
+ help =
+"The CA Unicenter TNG console node at tngfw_node will be notified when
+the rule condition is true.
+This action requires the \"cawto\" program which is part of the CA
+Unicenter TNG Framework (refer to the tngfw_bin variable description
+also).";
+
+string global.tngfw_bin
+ default = "/usr/TNGFW/bin"
+ help =
+"The full path to the TNG Framework \"cawto\" program, which is used to
+propagate external events into the Unicenter TNG Framework.";
+
+string global.tngfw_node
+ default = ""
+ help =
+"The node on which the CA Unicenter TNG monitoring software is running.
+The node can be either an Internet address or host name (see hosts(4)),
+and is usually the local host. An empty string is equivalent to the
+local host.";
+
+string global.tngfw_color
+ default = "default"
+ display = no
+ help =
+"The color that the CA Unicenter TNG event console on tngfw_node will
+use to display the event message string. Valid values are \"default\",
+\"Red\", \"Orange\", \"yellow\", \"green\", \"blue\", \"pink\" or \"purple\".";
+
+string global.tngfw_category
+ default = "Performance"
+ display = no
+ help =
+"The category with which the CA Unicenter TNG event console on
+tngfw_node will associate each event.";
+
+#
+# --- DO NOT MODIFY THIS FILE --- see pmieconf(4)
diff --git a/src/pmieconf/global/web_parameters b/src/pmieconf/global/web_parameters
new file mode 100644
index 0000000..1d4482d
--- /dev/null
+++ b/src/pmieconf/global/web_parameters
@@ -0,0 +1,35 @@
+#pmieconf-rules 1
+# --- DO NOT MODIFY THIS FILE --- see pmieconf(4)
+#
+
+instlist global.webservers
+ default = ""
+ help =
+"List of web server names over which the rules will be evaluated.
+Multiple server names should be separated by white space, e.g.
+ \"ncsa-gonzo 'ftpd'\"
+To discover the names of the available web servers, execute the
+following command on the target host:
+ $ pminfo -f web.perserver.requests.total
+the double-quoted instance names are the web server names.
+If the list is empty, all web servers on the target host will be
+checked.";
+
+instlist global.urls
+ default = ""
+ help =
+"May be set to a list of URLs names to limit those which the rules
+will be evaluated, as a subset of those used by the webping agent.
+Each URL should be in the form used by the webping agent, be enclosed
+in single quotes, and multiple entries separated by white space, e.g.
+'GET_http://boing/index.html' 'GET_http://far.away.com/planning.html'
+
+To discover the names of the available webping URLs, execute the
+following command on the target host:
+ $ pminfo -f webping.perurl.kbytes
+the (double) quoted instance names are the required URLs.
+
+If the list is empty, all webping URLs will be checked.";
+
+#
+# --- DO NOT MODIFY THIS FILE --- see pmieconf(4)
diff --git a/src/pmieconf/io.c b/src/pmieconf/io.c
new file mode 100644
index 0000000..86a8025
--- /dev/null
+++ b/src/pmieconf/io.c
@@ -0,0 +1,229 @@
+/*
+ * Copyright 1998, 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 <stdarg.h>
+#include "pmapi.h"
+#include "impl.h"
+#ifdef HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif
+#ifdef HAVE_TERMIOS_H
+#include <termios.h>
+#endif
+#ifdef HAVE_TERMIO_H
+#include <termio.h>
+#endif
+
+#define MINCOLS 80
+#define MINROWS 24
+
+static int neols = -1;
+static int needresize;
+static int needscroll;
+static int skiprest;
+static int nrows;
+static int ncols;
+static char shortmsg[] = "more? (h=help) ";
+static char longmsg[] = \
+ "[q or n to stop, y or <space> to go on, <enter> to step] more? ";
+#ifdef HAVE_TERMIO_H
+static struct termio otty;
+#endif
+
+void setio(int reset) { neols = 0; skiprest = reset; }
+void setscroll(void) { needscroll = 1; }
+int resized(void) { return needresize; }
+
+/* looks after window resizing for the printing routine */
+void
+onwinch(int dummy)
+{
+#ifdef SIGWINCH
+ __pmSetSignalHandler(SIGWINCH, onwinch);
+#endif
+ needresize = 1;
+}
+
+/* in interactive mode scrolling, if no more wanted skiprest is set */
+static void
+promptformore(void)
+{
+ int i;
+ int ch;
+ int sts = 1;
+ char c;
+ char *prompt;
+#ifdef HAVE_TERMIO_H
+ static int first = 1;
+ struct termio ntty;
+#endif
+
+#ifdef HAVE_TERMIO_H
+ if (first) {
+ if (ioctl(0, TCGETA, &otty) < 0) {
+ fprintf(stderr, "%s: TCGETA ioctl failed: %s\n", pmProgname,
+ osstrerror());
+ exit(1);
+ }
+ first = 0;
+ }
+
+ /* put terminal into raw mode so we can read input immediately */
+ memcpy(&ntty, &otty, sizeof(struct termio));
+ ntty.c_cc[VMIN] = 1;
+ ntty.c_cc[VTIME] = 1;
+ ntty.c_lflag &= ~(ICANON | ECHO);
+ if (ioctl(0, TCSETAW, &ntty) < 0) {
+ fprintf(stderr, "%s: TCSETAW ioctl failed: %s\n", pmProgname,
+ osstrerror());
+ exit(1);
+ }
+#endif
+
+ prompt = shortmsg;
+ while (sts == 1) {
+ putchar('\r');
+ for (i = 0; i < ncols-1; i++)
+ putchar(' ');
+ putchar('\r');
+ printf("%s", prompt);
+ fflush(stdout);
+
+ if (read(0, &c, 1) != 1) {
+ sts = 1;
+ goto reset_tty;
+ }
+ ch = (int)c;
+
+ switch(ch) {
+ case 'n': /* stop */
+ case 'q':
+ setio(1);
+ sts = 0;
+ break;
+ case 'y': /* page down */
+ case ' ':
+ neols = sts = 0;
+ break;
+ case '\n': /* step down */
+ neols = nrows;
+ sts = 0;
+ break;
+ default:
+ prompt = longmsg;
+ }
+ }
+
+reset_tty:
+#ifdef HAVE_TERMIO_H
+ if (ioctl(0, TCSETAW, &otty) < 0) {
+ fprintf(stderr, "%s: reset TCSETAW ioctl failed: %s\n", pmProgname,
+ osstrerror());
+ exit(1);
+ }
+#endif
+
+ putchar('\r');
+ for (i = 0; i < ncols-1; i++)
+ putchar(' ');
+ putchar('\r');
+ fflush(stdout);
+}
+
+/*
+ * generic printing routine which can pause at end of a screenful.
+ * if this returns 1, the user has requested an end to this info,
+ * so the caller must always observe the pprintf return value.
+ */
+void
+pprintf(char *format, ...)
+{
+ char *p;
+ va_list args;
+#ifdef TIOCGWINSZ
+ struct winsize geom;
+#endif
+ static int first = 1;
+
+ if (first == 1) { /* first time thru */
+ first = 0;
+#ifdef TIOCGWINSZ
+ ioctl(0, TIOCGWINSZ, &geom);
+ nrows = (geom.ws_row < MINROWS? MINROWS : geom.ws_row);
+ ncols = (geom.ws_col < MINCOLS? MINCOLS : geom.ws_col);
+#else
+ nrows = MINROWS;
+ ncols = MINCOLS;
+#endif
+ }
+
+ if (skiprest)
+ return;
+
+ va_start(args, format);
+ if (needscroll) {
+ /*
+ * use the fact that i know we never print more than MINROWS at once
+ * to figure out how many lines we've done before doing the vfprintf
+ */
+ if (neols >= nrows-1) {
+ promptformore();
+ if (skiprest) {
+ va_end(args);
+ return;
+ }
+ }
+ for (p = format; *p != '\0'; p++)
+ if (*p == '\n') neols++;
+ vfprintf(stdout, format, args);
+ }
+ else
+ vfprintf(stdout, format, args);
+ va_end(args);
+ if (needresize) {
+#ifdef HAVE_TIOCGWINSZ
+ ioctl(0, TIOCGWINSZ, &geom);
+ nrows = (geom.ws_row < MINROWS? MINROWS : geom.ws_row);
+ ncols = (geom.ws_col < MINCOLS? MINCOLS : geom.ws_col);
+#ifdef PMIECONF_DEBUG
+ printf("debug - reset size: cols=%d rows=%d\n", ncols, nrows);
+#endif
+#endif
+ needresize = 0;
+ }
+}
+
+/* general error printing routine */
+void
+error(char *format, ...)
+{
+ va_list args;
+ FILE *f;
+
+ if (skiprest)
+ return;
+ va_start(args, format);
+ if (needscroll) {
+ f = stdout;
+ fprintf(f, " Error - ");
+ }
+ else {
+ f = stderr;
+ fprintf(f, "%s: error - ", pmProgname);
+ }
+ vfprintf(f, format, args);
+ fprintf(f, "\n");
+ neols++;
+ va_end(args);
+}
diff --git a/src/pmieconf/memory/exhausted b/src/pmieconf/memory/exhausted
new file mode 100644
index 0000000..9e430a6
--- /dev/null
+++ b/src/pmieconf/memory/exhausted
@@ -0,0 +1,81 @@
+#pmieconf-rules 1
+# --- DO NOT MODIFY THIS FILE --- see pmieconf(4)
+#
+
+rule memory.exhausted
+ summary = "$rule$"
+ # first conjunct for %v, second is real condition...
+ predicate =
+"some_host (
+ ( avg_sample ( swap.pagesout $hosts$ @0..9 ) ) > 0 &&
+ $pct$ %_sample swap.pagesout $hosts$ @0..9 >= $threshold$
+)"
+ enabled = yes
+ version = 1
+ help =
+"The system is swapping modified pages out of main memory to the
+swap partitions, and has been doing this at the rate of at least
+threshold pages swapped out per second for at least pct of the last
+10 samples, ie. sustained page out activity.";
+
+double threshold
+ default = 5
+ help =
+"Threshold rate of pages swapped out per second.";
+
+percent pct
+ default = 30
+ help =
+"Percentage of the last 10 observations with at least threshold
+pages swapped out per second required to make the rule true.";
+
+string rule
+ default = "Severe demand for real memory"
+ modify = no
+ display = no;
+
+string action_expand
+ default = "%vpgsout/s@%h"
+ modify = no
+ display = no;
+
+string email_expand
+ default = "host: %h recent average: %v pageouts/sec"
+ display = no
+ modify = no;
+
+
+# Configuration info specific to non-PCP tools follows...
+#
+
+# for SGI Embedded Support Partner integration:
+string esp_type
+ default = "0x20004B"
+ display = no
+ modify = no;
+
+# for HP OpenView integration:
+string ov_severity
+ display = no
+ default = "Critical";
+
+# for CA/Unicenter TNG integration:
+string tngfw_color
+ display = no
+ default = "Red";
+
+# for EnlightenDSM integration:
+string enln_test
+ default = memory.exhausted
+ display = no
+ modify = no;
+string enln_units
+ default = pgsout/s
+ display = no
+ modify = no;
+unsigned enln_severity
+ display = no
+ default = 5;
+
+#
+# --- DO NOT MODIFY THIS FILE --- see pmieconf(4)
diff --git a/src/pmieconf/memory/localdefs b/src/pmieconf/memory/localdefs
new file mode 100644
index 0000000..a74d0c6
--- /dev/null
+++ b/src/pmieconf/memory/localdefs
@@ -0,0 +1,29 @@
+ALL_RULES = swap_low exhausted
+
+LOCAL_RULES = $(ALL_RULES)
+
+# Metrics missing from Mac OS X
+#
+# rule: swap_low
+# swap.free -12357 Unknown metric name
+# swap.length -12357 Unknown metric name
+#
+# rule: exhausted
+# swap.pagesout -12357 Unknown metric name
+#
+ifeq ($(TARGET_OS), darwin)
+LOCAL_RULES =
+endif
+
+# Metrics missing from Solaris
+#
+# rule: exhausted
+# swap.pagesout -12357 Unknown metric name
+#
+# rule: swap_low
+# swap.free -12357 Unknown metric name
+# swap.length -12357 Unknown metric name
+#
+ifeq ($(TARGET_OS), solaris)
+LOCAL_RULES =
+endif
diff --git a/src/pmieconf/memory/swap_low b/src/pmieconf/memory/swap_low
new file mode 100644
index 0000000..d69866d
--- /dev/null
+++ b/src/pmieconf/memory/swap_low
@@ -0,0 +1,78 @@
+#pmieconf-rules 1
+# --- DO NOT MODIFY THIS FILE --- see pmieconf(4)
+#
+# Based on a rule originally developed by Kevin Wang at Silicon Graphics
+#
+
+rule memory.swap_low
+ summary = "$rule$"
+ predicate =
+"some_host (
+ ( 100 * ( swap.free $hosts$ / swap.length $hosts$ ) )
+ < $threshold$
+ && swap.length $hosts$ > 0 // ensure swap in use
+)"
+ enabled = no
+ version = 1
+ help =
+"There is only threshold percent swap space remaining - the system
+may soon run out of virtual memory. Reduce the number and size of
+the running programs or add more swap(1) space before it completely
+runs out.";
+
+percent threshold
+ default = 10
+ help =
+"Threshold percent of total swap space which is free, in the range
+0 (none free) to 100 (all swap is unused).";
+
+string rule
+ default = "Low free swap space"
+ modify = no
+ display = no;
+
+string action_expand
+ default = "%v%free@%h"
+ modify = no
+ display = no;
+
+string email_expand
+ default = "host: %h free swap space: %v%"
+ display = no
+ modify = no;
+
+
+# Configuration info specific to non-PCP tools follows...
+#
+
+# for SGI Embedded Support Partner integration:
+string esp_type
+ default = "0x20004C"
+ display = no
+ modify = no;
+
+# for HP OpenView integration:
+string ov_severity
+ display = no
+ default = "Critical";
+
+# for CA/Unicenter TNG integration:
+string tngfw_color
+ display = no
+ default = "Red";
+
+# for EnlightenDSM integration:
+string enln_test
+ default = memory.swap_low
+ display = no
+ modify = no;
+string enln_units
+ default = %free
+ display = no
+ modify = no;
+unsigned enln_severity
+ display = no
+ default = 5;
+
+#
+# --- DO NOT MODIFY THIS FILE --- see pmieconf(4)
diff --git a/src/pmieconf/pcp_web b/src/pmieconf/pcp_web
new file mode 100644
index 0000000..9de8479
--- /dev/null
+++ b/src/pmieconf/pcp_web
@@ -0,0 +1,21 @@
+#
+# Performance events generated by pmie rules in pcp_web
+# range: 0x200040 - 0x20008F -> pcp_eoe
+# range: 0x200090 - 0x20009F -> pcp
+# range: 0x2000A0 - 0x2000AF -> pcp_web
+# range: 0x2000B0 - 0x2000B8 -> pcp_hpc
+# range: 0x2000B9 - 0x2000BF -> pcp_fsafe
+# range: 0x2000C0 - 0x2000FF -> (unused)
+#
+
+4001:Performance:0x2000A0:Excessive individual Web server errors:1:1:0:1:1
+4001:Performance:0x2000A1:High rate of Web requests per server:1:1:0:1:1
+4001:Performance:0x2000A2:Web server appears idle:1:1:0:1:1
+4001:Performance:0x2000A3:Excessive aggregate Web server errors:1:1:0:1:1
+4001:Performance:0x2000A4:High aggregate rate of Web requests:1:1:0:1:1
+4001:Performance:0x2000A5:Low aggregate rate of Web requests:1:1:0:1:1
+4001:Performance:0x2000A6:High webping connection error rate:1:1:0:1:1
+4001:Performance:0x2000A7:High webping HTML error rate:1:1:0:1:1
+4001:Performance:0x2000A8:High webping HTTP error rate:1:1:0:1:1
+4001:Performance:0x2000A9:High webping error rate, cause unknown:1:1:0:1:1
+4001:Performance:0x2000AA:Slow response to some webping requests:1:1:0:1:1
diff --git a/src/pmieconf/percpu/context_switch b/src/pmieconf/percpu/context_switch
new file mode 100644
index 0000000..fdee5cd
--- /dev/null
+++ b/src/pmieconf/percpu/context_switch
@@ -0,0 +1,70 @@
+#pmieconf-rules 1
+# --- DO NOT MODIFY THIS FILE --- see pmieconf(4)
+#
+
+rule per_cpu.context_switch
+ summary = "$rule$"
+ enumerate = "hosts"
+ predicate =
+"some_host (
+ some_inst (
+ kernel.percpu.pswitch $hosts$ > $threshold$ count/sec
+ )
+ && hinv.ncpu $hosts$ > 1
+)"
+ enabled = yes
+ version = 1
+ help =
+"The number of context switches per second for at least one CPU
+exceeded $threshold$ over the past sample interval.
+
+This rule only applies to multi-processor systems, for
+single-processor systems refer to the cpu.context_switch rule.
+
+For Origin 200 and Origin 2000 systems, use the command
+ $ pminfo -f hinv.map.cpu
+to discover the abbreviated PCP names of the installed CPUs and
+their corresponding full names in the /hw file system.";
+
+string rule
+ default = "High per CPU context switch rate"
+ modify = no
+ display = no;
+
+double threshold
+ default = 5000
+ help =
+"The threshold number of context switches per second per CPU.";
+
+string action_expand
+ default = %vctxsw/s[%i]@%h
+ display = no
+ modify = no;
+
+string email_expand
+ default = "host: %h CPU: %i context switches: %v/sec"
+ display = no
+ modify = no;
+
+
+# Configuration info specific to non-PCP tools follows...
+#
+
+# for SGI Embedded Support Partner integration:
+string esp_type
+ default = "0x200056"
+ display = no
+ modify = no;
+
+# for EnlightenDSM integration:
+string enln_test
+ default = per_cpu.context_switch
+ display = no
+ modify = no;
+string enln_units
+ default = ctxsw/s[%i]
+ display = no
+ modify = no;
+
+#
+# --- DO NOT MODIFY THIS FILE --- see pmieconf(4)
diff --git a/src/pmieconf/percpu/localdefs b/src/pmieconf/percpu/localdefs
new file mode 100644
index 0000000..7bb0c69
--- /dev/null
+++ b/src/pmieconf/percpu/localdefs
@@ -0,0 +1,45 @@
+ALL_RULES = syscall some_util many_util context_switch system
+
+LOCAL_RULES = $(ALL_RULES)
+
+# Metrics missing from Linux
+#
+# rule: context_switch
+# kernel.percpu.pswitch -12357 Unknown metric name
+#
+# rule: syscall
+# kernel.percpu.syscall -12357 Unknown metric name
+#
+ifeq ($(TARGET_OS), linux)
+LOCAL_RULES = some_util many_util system
+endif
+
+# Metrics missing from Mac OS X
+#
+# rule: syscall
+# kernel.percpu.syscall -12357 Unknown metric name
+#
+# rule: some_util
+# kernel.percpu.cpu.intr -12357 Unknown metric name
+#
+# rule: many_util
+# kernel.percpu.cpu.intr -12357 Unknown metric name
+#
+# rule: context_switch
+# kernel.percpu.pswitch -12357 Unknown metric name
+#
+ifeq ($(TARGET_OS), darwin)
+LOCAL_RULES = system
+endif
+
+# Metrics missing from Solaris
+#
+# rule: many_util
+# kernel.percpu.cpu.intr -12357 Unknown metric name
+#
+# rule: some_util
+# kernel.percpu.cpu.intr -12357 Unknown metric name
+#
+ifeq ($(TARGET_OS), solaris)
+LOCAL_RULES = syscall context_switch system
+endif
diff --git a/src/pmieconf/percpu/many_util b/src/pmieconf/percpu/many_util
new file mode 100644
index 0000000..9fe2f08
--- /dev/null
+++ b/src/pmieconf/percpu/many_util
@@ -0,0 +1,85 @@
+#pmieconf-rules 1
+# --- DO NOT MODIFY THIS FILE --- see pmieconf(4)
+#
+
+rule per_cpu.many_util
+ summary = "$rule$"
+ enumerate = hosts
+ predicate =
+"some_host (
+ $pct$ %_inst (
+ 100 * ( kernel.percpu.cpu.user $hosts$ +
+ kernel.percpu.cpu.sys $hosts$ +
+ kernel.percpu.cpu.intr $hosts$ )
+ > $threshold$
+ )
+ && hinv.ncpu $hosts$ > $min_cpu_count$
+)"
+ enabled = yes
+ version = 1
+ help =
+"The processor utilization for at least pct percent of the CPUs
+exceeded threshold percent during the last sample interval. Only
+applies to multi-processor systems having more than min_cpu_count
+processors - for single-processor systems refer to the cpu.util
+rule, for multi-processor systems with less than min_cpu_count
+processors refer to the per_cpu.some_util rule.";
+
+string rule
+ default = "High number of saturated processors"
+ modify = no
+ display = no;
+
+percent threshold
+ default = 95
+ help =
+"Threshold percentage for CPU saturation, in the range 0 (idle)
+to 100 (completely busy)";
+
+percent pct
+ default = 80
+ help =
+"Percentage of the processors which must be utilized greater
+than threshold percent, in the range 0 (no processors utilized)
+to 100 (all processors).";
+
+unsigned min_cpu_count
+ default = 4
+ help =
+"Lower limit on number of processors for this rule - this rule will
+apply to configurations of greater than min_cpu_count CPUs.
+For smaller processor counts, the per_cpu.some_util rule may be more
+appropriate.";
+
+string action_expand
+ default = "\\\\>$pct$%cpus@%h"
+ display = no
+ modify = no;
+
+string email_expand
+ default = "host: %h more than $pct$% of the processors are saturated"
+ display = no
+ modify = no;
+
+
+# Configuration info specific to non-PCP tools follows...
+#
+
+# for SGI Embedded Support Partner integration:
+string esp_type
+ default = "0x20005F"
+ display = no
+ modify = no;
+
+# for EnlightenDSM integration:
+string enln_test
+ default = per_cpu.many_util
+ display = no
+ modify = no;
+string enln_units
+ default = "busy_CPUs"
+ display = no
+ modify = no;
+
+#
+# --- DO NOT MODIFY THIS FILE --- see pmieconf(4)
diff --git a/src/pmieconf/percpu/some_util b/src/pmieconf/percpu/some_util
new file mode 100644
index 0000000..a065226
--- /dev/null
+++ b/src/pmieconf/percpu/some_util
@@ -0,0 +1,83 @@
+#pmieconf-rules 1
+# --- DO NOT MODIFY THIS FILE --- see pmieconf(4)
+#
+
+rule per_cpu.some_util
+ summary = "$rule$"
+ enumerate = hosts
+ predicate =
+"some_host (
+ some_inst (
+ ( 100 * ( kernel.percpu.cpu.user $hosts$ +
+ kernel.percpu.cpu.sys $hosts$ +
+ kernel.percpu.cpu.intr $hosts$ ) )
+ > $threshold$
+ )
+ && hinv.ncpu $hosts$ > 1
+ && hinv.ncpu $hosts$ <= $max_cpu_count$
+)"
+ enabled = yes
+ version = 1
+ help =
+"The processor utilization for at least one CPU exceeded threshold
+percent during the last sample interval. Only applies to
+multi-processor systems with less than max_cpu_count processors -
+for single-processor systems refer to the cpu.util rule, and for
+multi-processor systems with more than max_cpu_count processors
+refer to the cpu.many_util rule.
+For Origin 200 and Origin 2000 systems, use the command
+ $ pminfo -f hinv.map.cpu
+to discover the abbreviated PCP names of the installed CPUs and
+their corresponding full names in the /hw file system.";
+
+string rule
+ default = "High per CPU processor utilization"
+ modify = no
+ display = no;
+
+percent threshold
+ default = 95
+ help =
+"Threshold percentage for CPU saturation, in the range 0 (idle)
+to 100 (completely busy)";
+
+unsigned max_cpu_count
+ default = 4
+ help =
+"Upper limit on number of processors for this rule - this rule will
+apply to configurations of between two and max_cpu_count CPUs.
+For larger processor counts, the per_cpu.many_util rule may be more
+appropriate.";
+
+string action_expand
+ default = %v%util[%i]@%h
+ display = no
+ modify = no;
+
+string email_expand
+ default = "host: %h CPU: %i utilization: %v%"
+ display = no
+ modify = no;
+
+
+# Configuration info specific to non-PCP tools follows...
+#
+
+# for SGI Embedded Support Partner integration:
+string esp_type
+ default = "0x200059"
+ display = no
+ modify = no;
+
+# for EnlightenDSM integration:
+string enln_test
+ default = per_cpu.some_util
+ display = no
+ modify = no;
+string enln_units
+ default = %util[%i]
+ display = no
+ modify = no;
+
+#
+# --- DO NOT MODIFY THIS FILE --- see pmieconf(4)
diff --git a/src/pmieconf/percpu/syscall b/src/pmieconf/percpu/syscall
new file mode 100644
index 0000000..fbfbeee
--- /dev/null
+++ b/src/pmieconf/percpu/syscall
@@ -0,0 +1,80 @@
+#pmieconf-rules 1
+# --- DO NOT MODIFY THIS FILE --- see pmieconf(4)
+#
+
+rule per_cpu.syscall
+ summary = "$rule$"
+ enumerate = hosts
+ predicate =
+"some_host (
+ some_inst (
+ kernel.percpu.syscall $hosts$ > $threshold$ count/sec
+ )
+ && hinv.ncpu $hosts$ > 1
+)"
+ enabled = yes
+ version = 1
+ help =
+"The number of system calls per second for at least one CPU
+exceeded threshold over the past sample interval.
+
+This rule only applies to multi-processor systems, for
+single-processor systems refer to the cpu.syscall rule.
+
+For Origin 200 and Origin 2000 systems, use the command
+ $ pminfo -f hinv.map.cpu
+to discover the abbreviated PCP names of the installed CPUs and
+their corresponding full names in the /hw file system.";
+
+string rule
+ default = "High per CPU system call rate"
+ modify = no
+ display = no;
+
+double threshold
+ default = 12000
+ help =
+"The threshold of system calls per second per CPU. The appropriate
+value here is a function of the processor type and the workload, but
+here are some indicative figures of sustained system call rates for a
+single process:
+ getpid() - 380000 syscalls/sec
+ lseek() to start of file - 280000 syscalls/sec
+ gettimeofday() - 200000 syscalls/sec
+ read() at end of file - 83000 syscalls/sec
+ file creat() and close() - 65000 syscalls/sec
+ socket(), connect() and close() - 7000 syscalls/sec
+(generated using an otherwise idle system with 180MHz R10000 processors).";
+
+string action_expand
+ default = %vscall/s[%i]@%h
+ display = no
+ modify = no;
+
+string email_expand
+ default = "host: %h CPU: %i syscalls/sec: %v"
+ display = no
+ modify = no;
+
+
+# Configuration info specific to non-PCP tools follows...
+#
+
+# for SGI Embedded Support Partner integration:
+string esp_type
+ default = "0x200057"
+ display = no
+ modify = no;
+
+# for EnlightenDSM integration:
+string enln_test
+ default = per_cpu.syscall
+ display = no
+ modify = no;
+string enln_units
+ default = scall/s[%i]
+ display = no
+ modify = no;
+
+#
+# --- DO NOT MODIFY THIS FILE --- see pmieconf(4)
diff --git a/src/pmieconf/percpu/system b/src/pmieconf/percpu/system
new file mode 100644
index 0000000..223692c
--- /dev/null
+++ b/src/pmieconf/percpu/system
@@ -0,0 +1,84 @@
+#pmieconf-rules 1
+# --- DO NOT MODIFY THIS FILE --- see pmieconf(4)
+#
+
+rule per_cpu.system
+ summary = "$rule$"
+ enumerate = hosts
+ # need first conjunct to get %v in actions...
+ predicate =
+"some_host (
+ some_inst (
+ // first term is always true, but provides %v for actions ...
+ ( 100 * kernel.percpu.cpu.sys $hosts$ ) > 0
+ && 100 * ( kernel.percpu.cpu.user $hosts$ +
+ kernel.percpu.cpu.sys $hosts$ ) > $busy$
+ && 100 * kernel.percpu.cpu.sys $hosts$ /
+ ( kernel.percpu.cpu.user $hosts$ + kernel.percpu.cpu.sys $hosts$ )
+ > $threshold$
+ )
+ && hinv.ncpu $hosts$ > 1
+)"
+ enabled = yes
+ version = 1
+ help =
+"Over the last sample interval, at least one CPU was active for
+busy percent or more, and the ratio of system time to busy time
+exceeded threshold percent. Only applies to multi-processor
+systems, for single-processor systems refer to the cpu.system
+rule.
+For Origin 200 and Origin 2000 systems, use the command
+ $ pminfo -f hinv.map.cpu
+to discover the abbreviated PCP names of the installed CPUs and
+their corresponding full names in the /hw file system.";
+
+string rule
+ default = "Some CPU busy executing in system mode"
+ modify = no
+ display = no;
+
+percent busy
+ default = 75
+ help =
+"Busy percentage for average CPU utilization, in the range 0 (idle)
+to 100 (completely busy), independent of the number of CPUs.";
+
+percent threshold
+ default = 80
+ help =
+"Threshold percentage for system time as a fraction of the non-idle
+CPU time, in the range 0 (no system time) to 100 (all system time),
+independent of the number of CPUs.";
+
+string action_expand
+ default = %v%sys[%i]@%h
+ display = no
+ modify = no;
+
+string email_expand
+ default = "host: %h CPU: %i system mode: %v%"
+ display = no
+ modify = no;
+
+
+# Configuration info specific to non-PCP tools follows...
+#
+
+# for SGI Embedded Support Partner integration:
+string esp_type
+ default = "0x200058"
+ display = no
+ modify = no;
+
+# for EnlightenDSM integration:
+string enln_test
+ default = per_cpu.system
+ display = no
+ modify = no;
+string enln_units
+ default = %sys[%i]
+ display = no
+ modify = no;
+
+#
+# --- DO NOT MODIFY THIS FILE --- see pmieconf(4)
diff --git a/src/pmieconf/pernetif/collisions b/src/pmieconf/pernetif/collisions
new file mode 100644
index 0000000..9df73ee
--- /dev/null
+++ b/src/pmieconf/pernetif/collisions
@@ -0,0 +1,76 @@
+#pmieconf-rules 1
+# --- DO NOT MODIFY THIS FILE --- see pmieconf(4)
+#
+
+rule per_netif.collisions
+ default = "$rule$"
+ enumerate = hosts
+ predicate =
+"some_inst (
+ 100 * network.interface.collisions $hosts$
+ / ( network.interface.collisions $hosts$
+ + network.interface.out.packets $hosts$ )
+ > $threshold$ &&
+ network.interface.out.packets $hosts$
+ > $packet_rate$ count/second
+)"
+ enabled = yes
+ version = 1
+ help =
+"More than threshold percent of the packets being sent across an
+interface are causing a collision, and packets are being sent
+across the interface at packet_rate packets per second.
+Ethernet interfaces expect a certain number of packet collisions,
+but a high ratio of collisions to packet sends is indicitive of a
+saturated network.";
+
+string rule
+ default = "High collision rate in packet sends"
+ modify = no
+ display = no;
+
+percent threshold
+ default = 40
+ help =
+"Threshold percent of transmitted packets are colliding with other
+packet sending attempts before being successfully transmitted.";
+
+double packet_rate
+ default = 10
+ help =
+"Rate at which packets are being transmitted on an interface (number
+of packets per second) before considering the number of collisions as
+significant.";
+
+string action_expand
+ default = "%v%collisions[%i]@%h"
+ display = no
+ modify = no;
+
+string email_expand
+ default = "host: %h interface: %i collision rate: %v%"
+ display = no
+ modify = no;
+
+
+# Configuration info specific to non-PCP tools follows...
+#
+
+# for SGI Embedded Support Partner integration:
+string esp_type
+ default = "0x20004E"
+ display = no
+ modify = no;
+
+# for EnlightenDSM integration:
+string enln_test
+ default = per_netif.collisions
+ display = no
+ modify = no;
+string enln_units
+ default = %collide[%i]
+ display = no
+ modify = no;
+
+#
+# --- DO NOT MODIFY THIS FILE --- see pmieconf(4)
diff --git a/src/pmieconf/pernetif/errors b/src/pmieconf/pernetif/errors
new file mode 100644
index 0000000..61c9e38
--- /dev/null
+++ b/src/pmieconf/pernetif/errors
@@ -0,0 +1,72 @@
+#pmieconf-rules 1
+# --- DO NOT MODIFY THIS FILE --- see pmieconf(4)
+#
+
+rule per_netif.errors
+ default = "$rule$"
+ enumerate = hosts
+ predicate =
+"some_inst (
+ network.interface.total.errors $hosts$ $interfaces$
+ > $threshold$
+)"
+ enabled = yes
+ version = 1
+ help =
+"For at least one network interface, the error rate exceeded
+threshold errors per second during the last sample interval.";
+
+string rule
+ default = "High network interface error rate"
+ modify = no
+ display = no;
+
+double threshold
+ default = 15
+ help =
+"Threshold in units of errors per second per interface.";
+
+instlist interfaces
+ default = ""
+ help =
+"May be set to a list of network interfaces for which the rule will
+be evaluated, as a subset of configured network interfaces.
+Network interfaces should be separated by white space and may be
+enclosed in single quotes, eg. \"ec0 'xpi5' ec2\". Use the command:
+ $ pminfo -f network.interface.mtu
+to discover the names of the installed network interfaces.
+Setting this variable is most useful to remove the loopback, SLIP
+and PPP interfaces from the rule evaluations.";
+
+string action_expand
+ default = "%verr/s[%i]@%h"
+ display = no
+ modify = no;
+
+string email_expand
+ default = "host: %h interface: %i errors per sec: %v"
+ display = no
+ modify = no;
+
+
+# Configuration info specific to non-PCP tools follows...
+#
+
+# for SGI Embedded Support Partner integration:
+string esp_type
+ default = "0x20004F"
+ display = no
+ modify = no;
+
+# for EnlightenDSM integration:
+string enln_test
+ default = per_netif.errors
+ display = no
+ modify = no;
+string enln_units
+ default = err/s[%i]
+ display = no
+ modify = no;
+
+#
+# --- DO NOT MODIFY THIS FILE --- see pmieconf(4)
diff --git a/src/pmieconf/pernetif/localdefs b/src/pmieconf/pernetif/localdefs
new file mode 100644
index 0000000..e153d6d
--- /dev/null
+++ b/src/pmieconf/pernetif/localdefs
@@ -0,0 +1,22 @@
+ALL_RULES = collisions errors packets util
+
+LOCAL_RULES = $(ALL_RULES)
+
+# Metrics missing from Solaris
+#
+# rule: collisions
+# network.interface.collisions -12357 Unknown metric name
+#
+# rule: errors
+# network.interface.total.errors -12357 Unknown metric name
+#
+# rule: packets
+# network.interface.total.packets -12357 Unknown metric name
+#
+# rule: util
+# network.interface.baudrate -12357 Unknown metric name
+# network.interface.total.bytes -12357 Unknown metric name
+#
+ifeq ($(TARGET_OS), solaris)
+LOCAL_RULES =
+endif
diff --git a/src/pmieconf/pernetif/packets b/src/pmieconf/pernetif/packets
new file mode 100644
index 0000000..7b68553
--- /dev/null
+++ b/src/pmieconf/pernetif/packets
@@ -0,0 +1,83 @@
+#pmieconf-rules 1
+# --- DO NOT MODIFY THIS FILE --- see pmieconf(4)
+#
+
+rule per_netif.packets
+ default = "$rule$"
+ enumerate = hosts
+ predicate =
+"some_inst (
+ network.interface.total.packets $hosts$ $interfaces$ >
+ $threshold$ count/sec
+)"
+ enabled = no
+ version = 1
+ help =
+"For at least one network interface, the average rate of packet
+transfers (in and/or out) exceeded the threshold during the last
+sample interval.
+This rule is disabled by default because the per_netif.util rule
+is more generally useful as it takes into consideration each
+network interfaces' reported bandwidth. However, there are some
+situations in which this value is zero, in which case an absolute
+threshold-based rule like this one will make more sense (for this
+reason it should typically be applied to some network interfaces,
+but not others - use the \"interfaces\" variable to filter this).";
+
+string rule
+ default = "High network interface packet transfers"
+ modify = no
+ display = no;
+
+double threshold
+ default = 2000
+ help =
+"Threshold in units of packets (in or out) per second per interface.
+A tolerable value depends on the type of network interface and
+the packet size; some experimentation may be required to find
+an accpetable threshold.";
+
+instlist interfaces
+ default =""
+ help =
+"May be set to a list of network interfaces for which the rule will
+be evaluated, as a subset of configured network interfaces.
+Network interfaces should be separated by white space and may be
+enclosed in single quotes, eg. \"ec0 'xpi5' ec2\". Use the command:
+ $ pminfo -f network.interface.mtu
+to discover the names of the installed network interfaces.
+This is most useful to remove the loopback, SLIP and PPP interfaces
+from the rule evaluations.";
+
+string action_expand
+ default = "%vpkt/s[%i]@%h"
+ display = no
+ modify = no;
+
+string email_expand
+ default = "host: %h interface: %i packets/sec: %v"
+ display = no
+ modify = no;
+
+
+# Configuration info specific to non-PCP tools follows...
+#
+
+# for SGI Embedded Support Partner integration:
+string esp_type
+ default = "0x200050"
+ display = no
+ modify = no;
+
+# for EnlightenDSM integration:
+string enln_test
+ default = per_netif.packets
+ display = no
+ modify = no;
+string enln_units
+ default = pkt/s[%i]
+ display = no
+ modify = no;
+
+#
+# --- DO NOT MODIFY THIS FILE --- see pmieconf(4)
diff --git a/src/pmieconf/pernetif/util b/src/pmieconf/pernetif/util
new file mode 100644
index 0000000..c096cd3
--- /dev/null
+++ b/src/pmieconf/pernetif/util
@@ -0,0 +1,75 @@
+#pmieconf-rules 1
+# --- DO NOT MODIFY THIS FILE --- see pmieconf(4)
+#
+
+rule per_netif.util
+ summary = "$rule$"
+ enumerate = hosts
+ predicate =
+"some_inst (
+ ( 100 * network.interface.total.bytes $hosts$ $interfaces$ /
+ network.interface.baudrate $hosts$ $interfaces$ )
+ > $threshold$
+ && network.interface.baudrate $hosts$ $interfaces$ > 0
+)"
+ enabled = yes
+ version = 1
+ help =
+"For at least one network interface, the average transfer rate (in
+and/or out) exceeded threshold percent of the peak bandwidth of the
+interface during the last sample interval.";
+
+string rule
+ default = "High network interface utilization"
+ modify = no
+ display = no;
+
+double threshold
+ default = 85
+ help =
+"Threshold in percentage of bandwidth utilization.";
+
+instlist interfaces
+ default = ""
+ help =
+"May be set to a list of network interfaces for which the rule will
+be evaluated, as a subset of configured network interfaces.
+Network interfaces should be separated by white space and may be
+enclosed in single quotes, eg. \"ec0 'xpi5' ec2\". Use the command:
+ $ pminfo -f network.interface.mtu
+to discover the names of the installed network interfaces.
+Setting this variable is most useful to remove the loopback, SLIP
+and PPP interfaces from the rule evaluations.";
+
+string action_expand
+ default = "%v%util[%i]@%h"
+ display = no
+ modify = no;
+
+string email_expand
+ default = "host: %h interface: %i utilization: %v%"
+ display = no
+ modify = no;
+
+
+# Configuration info specific to non-PCP tools follows...
+#
+
+# for SGI Embedded Support Partner integration:
+string esp_type
+ default = "0x200051"
+ display = no
+ modify = no;
+
+# for EnlightenDSM integration:
+string enln_test
+ default = per_netif.util
+ display = no
+ modify = no;
+string enln_units
+ default = %util[%i]
+ display = no
+ modify = no;
+
+#
+# --- DO NOT MODIFY THIS FILE --- see pmieconf(4)
diff --git a/src/pmieconf/pmie_email b/src/pmieconf/pmie_email
new file mode 100755
index 0000000..710fd26
--- /dev/null
+++ b/src/pmieconf/pmie_email
@@ -0,0 +1,65 @@
+#!/bin/sh
+#
+# 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
+
+
+# pmie_email is intended for use in pmie actions to send e-mail.
+#
+# the one argument consists of a multi-line message, separated by
+# '|' characters ...
+#
+# "line" 1 - e-mail addressee, as passed to a mail program
+# "line" 2 - mail Subject: will be "pmie alert: " and then this
+# text
+# "line" 2,3,.. - body of the message [optional]
+
+# source the PCP configuration environment variables
+. /etc/pcp.env
+
+prog=`basename $0`
+
+if [ $# -ne 1 ]
+then
+ echo "Usage: $prog long|format|message|as|one|argument"
+ exit 1
+fi
+
+if [ -z "$PCP_MUA" ] ; then
+ for mua in Mail mailx; do
+ if which $mua > /dev/null 2>&1
+ then
+ PCP_MUA=`which $mua`
+ break
+ fi
+ done
+fi
+
+if [ -z "$PCP_MUA" ] ; then
+ echo "Cannot find a mail program"
+ exit 1
+fi
+
+cat <<End-of-File | ${PCP_AWK_PROG} -F\| '
+NF < 2 { print "echo '"'$prog"': need at least \"e-mail addr|subject\" in argument'"'"'"
+ exit 1
+ }
+ { printf "%s -s \"pmie alert: %s\" %s <<End-of-File\n", "'$PCP_MUA'", $2, $1
+ print ""
+ for (i = 3; i <= NF; i++)
+ print $i
+ print "End-of-File"
+ }' | /bin/sh
+$1
+End-of-File
diff --git a/src/pmieconf/pmieconf.c b/src/pmieconf/pmieconf.c
new file mode 100644
index 0000000..0de4ff6
--- /dev/null
+++ b/src/pmieconf/pmieconf.c
@@ -0,0 +1,852 @@
+/*
+ * Copyright (c) 2014 Red Hat.
+ * Copyright (c) 1998-2001, 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 <ctype.h>
+#include <sys/param.h>
+#include "pmapi.h"
+#include "impl.h"
+#include "rules.h"
+
+#define MAXSYMLEN (MAXPATHLEN+1)
+#define MAXVARLEN (512+1)
+#define MAXBUFLEN (1024+1)
+
+static int verbose;
+static int autocreate;
+static int interactive = 1;
+static int pmiefile_modified;
+static char warn[MAXBUFLEN]; /* buffer for any warning messages */
+
+static char help[] = \
+ " 1. help [ { . | all | global | <rule> | <group> } [<variable>] ]\n"
+ " descriptive text on one or more variables or rules\n"
+ " 2. rules [ enabled | disabled ]\n"
+ " list of available, enabled, or disabled rules\n"
+ " 3. groups list of available groups of rules\n"
+ " 4. status print status information\n"
+ " 5. enable { . | all | <rule> | <group> }\n"
+ " switch evaluation on for given rule(s)\n"
+ " 6. disable { . | all | <rule> | <group> }\n"
+ " switch evaluation off for given rule(s)\n"
+ " 7. list { . | all | global | <rule> | <group> } [<variable>]\n"
+ " print values of variables for given rule(s)\n"
+ " 8. modify { . | all | global | <rule> | <group> } <variable> <value>\n"
+ " change the value of a rule variable\n"
+ " 9. undo { . | all | global | <rule> | <group> } [<variable>]\n"
+ " revert to default value of rule variable\n"
+ " 10. verbose [ { on | off } ]\n"
+ " change amount of info displayed ('*' denotes global variables)\n"
+ " 11. quit save changes then exit\n"
+ " 12. abort discard changes then exit";
+
+#define MAXARGS 4
+static char inbuf[MAXARGS][MAXBUFLEN+1]; /* input buffer */
+static char previous[MAXVARLEN+1]; /* buffer for last rule name */
+
+static symbol_t commands[] = {
+#define COMMAND_HELP 1
+ { COMMAND_HELP, "help" }, { COMMAND_HELP, "h" }, { COMMAND_HELP, "?" },
+#define COMMAND_RULES 2
+ { COMMAND_RULES, "rules" }, { COMMAND_RULES, "r" },
+#define COMMAND_GROUPS 3
+ { COMMAND_GROUPS, "groups" }, { COMMAND_GROUPS, "g" },
+#define COMMAND_STATUS 4
+ { COMMAND_STATUS, "status" }, { COMMAND_STATUS, "s" },
+#define COMMAND_ENABLE 5
+ { COMMAND_ENABLE, "enable" }, { COMMAND_ENABLE, "e" },
+#define COMMAND_DISABLE 6
+ { COMMAND_DISABLE, "disable" }, { COMMAND_DISABLE, "d" },
+#define COMMAND_LIST 7
+ { COMMAND_LIST, "list" }, { COMMAND_LIST, "l" },
+#define COMMAND_MODIFY 8
+ { COMMAND_MODIFY, "modify" }, { COMMAND_MODIFY, "m" },
+#define COMMAND_UNDO 9
+ { COMMAND_UNDO, "undo" }, { COMMAND_UNDO, "u" },
+#define COMMAND_VERBOSE 10
+ { COMMAND_VERBOSE, "verbose" }, { COMMAND_VERBOSE, "v" },
+#define COMMAND_QUIT 11
+ { COMMAND_QUIT, "quit" }, { COMMAND_QUIT, "q" },
+#define COMMAND_ABORT 12
+ { COMMAND_ABORT, "abort" }, { COMMAND_ABORT, "a" },
+#define LAST_COMMAND 12
+};
+static int ncommands = (sizeof(commands)/sizeof(commands[0]));
+
+/* io-related stuff */
+extern void setio(int);
+extern void setscroll(void);
+extern void onwinch(int);
+extern int pprintf(char *, ...);
+extern void error(char *, ...);
+
+
+/*
+ * #### simple printing routines ###
+ */
+
+static void
+print_helpstring(char *value)
+{
+ char *s;
+ char *str;
+ int i = 0;
+
+ if (value == NULL) {
+ pprintf(" help: No help available.\n");
+ return;
+ }
+ if ((str = strdup(value)) == NULL) {
+ error("insufficient memory to display help");
+ exit(1);
+ }
+ s = strtok(str, "\n");
+ while (s != NULL) {
+ pprintf("%s%s\n", i++ == 0? " help: ":"\t", s);
+ s = strtok(NULL, "\n");
+ }
+ free(str);
+}
+
+static void
+print_predicatestring(char *value)
+{
+ char *s;
+ char *str;
+
+ if ((str = strdup(value)) == NULL) {
+ error("insufficient memory to display predicate");
+ exit(1);
+ }
+ s = strtok(str, "\n");
+ pprintf("\tpredicate = \n");
+ while (s != NULL) {
+ pprintf("\t %s\n", s);
+ s = strtok(NULL, "\n");
+ }
+ free(str);
+}
+
+/* prints help string for a parameter of a rule */
+void
+print_help(rule_t *rule, char *attrib)
+{
+ atom_t *aptr;
+
+ for (aptr = &rule->self; aptr != NULL; aptr = aptr->next)
+ if (strcmp(get_aname(rule, aptr), attrib) == 0) {
+ if (aptr->help == NULL)
+ goto nohelp;
+ print_helpstring(aptr->help);
+ return;
+ }
+ for (aptr = &globals->self; aptr != NULL; aptr = aptr->next)
+ if (strcmp(get_aname(globals, aptr), attrib) == 0) {
+ if (aptr->help == NULL)
+ goto nohelp;
+ print_helpstring(aptr->help);
+ return;
+ }
+nohelp:
+ error("no help available for variable \"%s\" of rule %s",
+ attrib, rule->self.name);
+}
+
+/* prints a named parameter from a rule, return -1 on failure */
+int
+print_attribute(rule_t *rule, char *attrib, int dohelp)
+{
+ atom_t *aptr;
+ int isattrib = is_attribute(attrib);
+
+ if (isattrib == ATTRIB_PREDICATE && rule != globals)
+ print_predicatestring(rule->predicate);
+ else if (isattrib == ATTRIB_HELP)
+ print_helpstring(rule->self.help);
+ else if (isattrib != -1 && rule != globals) {
+ if (dohelp)
+ pprintf(" var: %s\n help: No help available.\n", attrib);
+ else
+ pprintf("\t%s = %s\n", attrib, get_attribute(attrib, &rule->self));
+ }
+ else {
+ for (aptr = rule->self.next; aptr != NULL; aptr = aptr->next)
+ if (strcmp(get_aname(rule, aptr), attrib) == 0) {
+ if (dohelp) {
+ pprintf(" var: %s\n", attrib);
+ print_helpstring(aptr->help);
+ }
+ else
+ pprintf("\t%s = %s\n", attrib, value_string(aptr, 1));
+ return 0;
+ }
+ for (aptr = globals->self.next; aptr != NULL; aptr = aptr->next)
+ if (strcmp(get_aname(globals, aptr), attrib) == 0) {
+ if (dohelp) {
+ pprintf(" var: %s\n", attrib);
+ print_helpstring(aptr->help);
+ }
+ else
+ pprintf("\t%s = %s\n", attrib, value_string(aptr, 1));
+ return 0;
+ }
+ error("variable \"%s\" is undefined for rule %s",
+ attrib, rule->self.name);
+ return -1;
+ }
+ return 0;
+}
+
+
+/* one line summary (name and short help) for a given rule */
+void
+print_rule_summary(rule_t *rule, char *prefix)
+{
+ char *str;
+ char fmt[] = "%s%s [%s]\n";
+
+ if ((str = dollar_expand(rule, rule->self.data, 0)) == NULL)
+ return;
+ pprintf(fmt, prefix, rule->self.name, str);
+ free(str);
+}
+
+
+void
+print_rule(rule_t *rule)
+{
+ atom_t *a;
+ int needvars = 1;
+
+ print_rule_summary(rule, " rule: ");
+ if (rule->self.help != NULL)
+ print_helpstring(rule->self.help);
+ if (rule != globals) { /* non-global */
+ print_predicatestring(rule->predicate);
+ pprintf(" vars: enabled = %s\n", rule->self.enabled?"yes":"no");
+ }
+ for (a = rule->self.next; a != NULL; a = a->next) {
+ if (!a->display)
+ continue;
+ pprintf("%s%s = %s\n", (rule == globals && needvars)?
+ " vars: ":"\t", get_aname(rule, a), value_string(a, 1));
+ needvars = 0;
+ }
+ if (verbose && rule != globals) {
+ for (a = globals->self.next; a != NULL; a = a->next) {
+ if (is_overridden(rule, a))
+ continue; /* printed already as part of attribs */
+ pprintf("\t%s = %s (*)\n", get_aname(globals,a), value_string(a,1));
+ }
+ }
+}
+
+
+/* print out the list of unique group names (sort done previously) */
+static int
+print_grouplist(int argcount)
+{
+ int i;
+ char *j;
+ char lastgroup[MAXVARLEN];
+
+ if (argcount != 0) {
+ error("too many arguments for \"groups\" command");
+ return -1;
+ }
+ lastgroup[0] = '\0';
+ for (i = 1; i < rulecount; i++) {
+ if ((j = strchr(rulelist[i].self.name, '.')) != NULL) {
+ *j = '\0'; /* mark end of group name */
+ if (strcmp(rulelist[i].self.name, lastgroup) != 0) {
+ strcpy(lastgroup, rulelist[i].self.name);
+ pprintf(" %s\n", lastgroup);
+ }
+ *j = '.'; /* repair the rule name */
+ }
+ }
+ return 0;
+}
+
+/*
+ * print out the current verbosity setting, running pmies using this
+ * pmie file, total number of rules & number of rules switched on.
+ */
+static int
+print_status(int argcount)
+{
+ int i, count = 0, pcount = 0;
+ char **processes;
+
+ if (argcount != 0) {
+ error("too many arguments for \"status\" command");
+ return -1;
+ }
+
+ for (i = 1; i < rulecount; i++) /* find enabled rules */
+ if (rulelist[i].self.enabled)
+ count++;
+ lookup_processes(&pcount, &processes); /* find running pmies */
+ printf(" verbose: %s\n"
+ " enabled rules: %u of %u\n"
+ " pmie configuration file: %s\n"
+ " pmie %s using this file: ",
+ verbose? "on" : "off", count, rulecount-1, get_pmiefile(),
+ pcount == 1? "process (PID)" : "processes (PIDs)");
+ if (pcount == 0)
+ printf(" (none found)");
+ else {
+ for (i = 0; i < pcount; i++) {
+ printf(" %s", processes[i]);
+ free(processes[i]);
+ }
+ free(processes);
+ }
+ printf("\n");
+ return 0;
+}
+
+int
+write_pmie(void)
+{
+ int i, count;
+ char *msg;
+ dep_t *list;
+
+ if ((msg = write_pmiefile(pmProgname, autocreate)) != NULL) {
+ error(msg);
+ return 1;
+ }
+ if ((count = fetch_deprecated(&list)) > 0) {
+ if (interactive)
+ pprintf(" Warning - some rules have been deprecated:\n");
+ else
+ pprintf("%s: some rules have been deprecated:\n", pmProgname);
+ for (i = 0; i < count; i++) {
+ pprintf(" %s (deprecated, %s)\n", list[i].name, list[i].reason);
+ free(list[i].name);
+ }
+ free(list);
+ pprintf("\n See %s for details\n", get_pmiefile());
+ }
+ return 0;
+}
+
+/* display the list of available, enabled or disabled rules */
+static int
+command_rules(int argcount)
+{
+ int i;
+
+ if (argcount > 1) {
+ error("too many arguments for \"rules\" command");
+ return -1;
+ }
+ if (argcount == 0)
+ for (i = 1; i < rulecount; i++)
+ print_rule_summary(&rulelist[i], " ");
+ else {
+ if (strcmp(inbuf[1], "enabled") == 0) {
+ for (i = 1; i < rulecount; i++)
+ if (rulelist[i].self.enabled)
+ print_rule_summary(&rulelist[i], " ");
+ }
+ else if (strcmp(inbuf[1], "disabled") == 0) {
+ for (i = 1; i < rulecount; i++)
+ if (!rulelist[i].self.enabled)
+ print_rule_summary(&rulelist[i], " ");
+ }
+ else {
+ error("invalid argument for \"rules\" command");
+ return -1;
+ }
+ }
+ return 0;
+}
+
+/* display or set the verbosity level */
+static int
+command_verbose(int argcount)
+{
+ int sts;
+
+ if (argcount < 1) {
+ printf(" verbose: %s\n", verbose? "on" : "off");
+ return 0;
+ }
+ else if (argcount > 1) {
+ error("too many arguments for \"verbose\" command");
+ return -1;
+ }
+ if (strcmp(inbuf[1], "on") == 0)
+ sts = 1;
+ else if (strcmp(inbuf[1], "off") == 0)
+ sts = 0;
+ else {
+ error("invalid argument, expected \"on\" or \"off\"");
+ return -1;
+ }
+ verbose = sts;
+ return 0;
+}
+
+static int
+command_list(int argcount)
+{
+ unsigned int rcount;
+ rule_t **rptr;
+ char *msg;
+ int all = 0;
+ int sts = 0;
+ int i;
+
+ if (argcount < 1) {
+ error("too few arguments for \"list\" command");
+ return -1;
+ }
+ else if (argcount > 2) {
+ error("too many arguments for \"list\" command");
+ return -1;
+ }
+
+ if (strcmp(".", inbuf[1]) == 0)
+ strcpy(inbuf[1], previous);
+ if (strcmp("all", inbuf[1]) == 0)
+ all = 1;
+ if ((msg = lookup_rules(inbuf[1], &rptr, &rcount, all)) != NULL) {
+ error(msg);
+ return -1;
+ }
+ if (argcount == 1) { /* print out one rule or one group */
+ for (i = 0; i < rcount; i++) {
+ if (i > 0) pprintf("\n");
+ print_rule(rptr[i]);
+ }
+ }
+ else { /* print out one rule/group variable */
+ for (i = 0; i < rcount; i++) {
+ print_rule_summary(rptr[i], " rule: ");
+ if (print_attribute(rptr[i], inbuf[2], 0) == -1)
+ sts = -1; /* failure */
+ }
+ }
+ free(rptr);
+ strcpy(previous, inbuf[1]);
+ return sts;
+}
+
+static int
+command_undo(int argcount)
+{
+ unsigned int rcount;
+ rule_t **rptr;
+ char *msg = NULL;
+ char *var = NULL;
+ int all = 0;
+ int i;
+
+ if (argcount < 1) {
+ error("too few arguments for \"undo\" command");
+ return -1;
+ }
+ else if (argcount > 2) {
+ error("too many arguments for \"undo\" command");
+ return -1;
+ }
+
+ if (strcmp(".", inbuf[1]) == 0)
+ strcpy(inbuf[1], previous);
+ if (strcmp("all", inbuf[1]) == 0)
+ all = 1;
+ if ((msg = lookup_rules(inbuf[1], &rptr, &rcount, all)) != NULL) {
+ error(msg);
+ return -1;
+ }
+ if (argcount == 2)
+ var = inbuf[2];
+
+ for (i = 0; i < rcount; i++)
+ if ((msg = rule_defaults(rptr[i], var)) != NULL)
+ break;
+ free(rptr);
+ if (msg != NULL) {
+ error(msg);
+ return -1;
+ }
+ strcpy(previous, inbuf[1]);
+ pmiefile_modified = 1;
+ return 0;
+}
+
+static int
+command_modify(int command, int argcount)
+{
+ unsigned int rcount;
+ rule_t **rptr;
+ char *msg;
+ int all = 0;
+ int c;
+
+ if (command == COMMAND_MODIFY) {
+ if (argcount != 3) {
+ error("too %s arguments for \"modify\" command",
+ argcount < 3? "few":"many");
+ return -1;
+ }
+ }
+ else if (strcmp(inbuf[1], "global") == 0) {
+ error("invalid argument - \"global\"");
+ return -1;
+ }
+ else if (command == COMMAND_ENABLE) {
+ if (argcount != 1) {
+ error("too %s arguments for \"enable\" command",
+ argcount < 1? "few":"many");
+ return -1;
+ }
+ strcpy(inbuf[2], "enabled");
+ strcpy(inbuf[3], "yes");
+ }
+ else { /* (command == COMMAND_DISABLE) */
+ if (argcount != 1) {
+ error("too %s arguments for \"disable\" command",
+ argcount < 1? "few":"many");
+ return -1;
+ }
+ strcpy(inbuf[2], "enabled");
+ strcpy(inbuf[3], "no");
+ }
+
+ if (strcmp(".", inbuf[1]) == 0)
+ strcpy(inbuf[1], previous);
+ if (strcmp("all", inbuf[1]) == 0)
+ all = 1;
+ if ( ((c = is_attribute(inbuf[2])) != -1) && c != ATTRIB_ENABLED ) {
+ error("no change - variable \"%s\" is always readonly", inbuf[2]);
+ return -1;
+ }
+ if ((msg = lookup_rules(inbuf[1], &rptr, &rcount, all)) != NULL) {
+ error(msg);
+ return -1;
+ }
+
+ for (c = 0; c < rcount; c++) {
+ if ((msg = value_change(rptr[c], inbuf[2], inbuf[3])) != NULL) {
+ error("change aborted - %s", msg);
+ free(rptr);
+ return -1;
+ }
+ }
+ pmiefile_modified = 1;
+ free(rptr);
+ strcpy(previous, inbuf[1]);
+ return 0;
+}
+
+static int
+command_help(int argcount)
+{
+ unsigned int rcount;
+ rule_t **rptr;
+ char *msg;
+ int sts = 0;
+ int all = 0;
+ int i;
+
+ if (argcount < 1) {
+ puts(help);
+ return 0;
+ }
+ else if (argcount > 2) {
+ error("too many arguments for \"help\" command");
+ return -1;
+ }
+
+ if (strcmp(".", inbuf[1]) == 0)
+ strcpy(inbuf[1], previous);
+ if (strcmp("all", inbuf[1]) == 0)
+ all = 1;
+ if ((msg = lookup_rules(inbuf[1], &rptr, &rcount, all)) != NULL) {
+ error(msg);
+ return -1;
+ }
+
+ if (argcount == 1) {
+ for (i = 0; i < rcount; i++) {
+ print_rule_summary(rptr[i], " rule: ");
+ print_helpstring(rptr[i]->self.help);
+ }
+ }
+ else {
+ for (i = 0; i < rcount; i++) {
+ print_rule_summary(rptr[i], " rule: ");
+ if (print_attribute(rptr[i], inbuf[2], 1) == -1)
+ sts = -1; /* failure */
+ }
+ }
+ free(rptr);
+ strcpy(previous, inbuf[1]);
+ return sts;
+}
+
+static void
+command_quit(void)
+{
+ static int done = 0;
+ int i, pcount = 0;
+ char **processes;
+ char *msg;
+
+ /* must only come thru here once, but can be called multiple times */
+ if (done != 0)
+ return;
+ done = 1;
+
+ if (pmiefile_modified && write_pmie() != 0)
+ exit(1);
+
+ /* show any running pmie processes which use this pmie config */
+ if (interactive && pmiefile_modified) {
+ if ((msg = lookup_processes(&pcount, &processes)) != NULL)
+ error(msg);
+ else if (pcount > 0) {
+ pprintf(" %s is in use by %d running pmie process%s:\n\t",
+ get_pmiefile(), pcount, pcount == 1? "":"es");
+ for (i = 0; i < pcount; i++)
+ pprintf("%s ", processes[i]);
+ pprintf("\n Restart %s for the configuration change to take effect.",
+ pcount == 1 ? "this process" : "these processes");
+ pprintf("\n o Use kill(1) to stop; e.g.\tkill -INT ");
+ for (i = 0; i < pcount; i++) {
+ pprintf("%s ", processes[i]);
+ free(processes[i]);
+ }
+ free(processes);
+ pprintf("\n\
+ o Refer to pmie_check(1) for a convenient mechanism for restarting pmie\n\
+ daemons launched under the control of %s/pmie/control;\n\
+ e.g.\t%s/pmie_check -V\n", pmGetConfig("PCP_SYSCONF_DIR"), pmGetConfig("PCP_BINADM_DIR"));
+ }
+ }
+}
+
+/*
+ * workhorse routine - dishes out work depending on user command;
+ * returns 1 on user-quit, 0 on success, -1 on failure
+ */
+static int
+configure(int count)
+{
+ int command = atoi(inbuf[0]);
+
+ if (command <= 0 || command > LAST_COMMAND)
+ command = map_symbol(commands, ncommands, inbuf[0]);
+
+ switch(command) {
+ case COMMAND_HELP:
+ return command_help(count-1);
+ case COMMAND_RULES:
+ return command_rules(count-1);
+ case COMMAND_GROUPS:
+ return print_grouplist(count-1);
+ case COMMAND_STATUS:
+ return print_status(count-1);
+ case COMMAND_VERBOSE:
+ return command_verbose(count-1);
+ case COMMAND_ENABLE: /* shortcut for modify */
+ case COMMAND_DISABLE: /* shortcut for modify */
+ case COMMAND_MODIFY:
+ return command_modify(command, count-1);
+ case COMMAND_LIST:
+ return command_list(count-1);
+ case COMMAND_UNDO:
+ return command_undo(count-1);
+ case COMMAND_QUIT:
+ command_quit();
+ return 1;
+ case COMMAND_ABORT:
+ exit(0);
+ default:
+ error("unrecognised command \"%s\" - try \"help\"", inbuf[0]);
+ return -1;
+ }
+ /*NOTREACHED*/
+}
+
+
+static void
+interact(void)
+{
+ int done = 0;
+ int n, sts;
+
+ if (interactive)
+ printf("Updates will be made to %s\n", get_pmiefile());
+ do {
+ sts = 0;
+ for (n = 0; n < MAXARGS; n++)
+ inbuf[n][0] = '\0';
+ if (interactive) {
+ setio(0);
+ printf("\n%s> ", pmProgname);
+ fflush(stdout);
+ }
+
+ do {
+ if ((n = read_token(stdin, inbuf[sts], MAXBUFLEN, '\n')) == -1) {
+ error("failed to parse argument %d correctly", sts+1);
+ break;
+ }
+ else if (n > 0)
+ sts++;
+ else {
+ if (n == -2) {
+ command_quit();
+ done = 1;
+ }
+ break;
+ }
+ } while (sts <= MAXARGS);
+
+ if (n < 0) /* done (EOF) or error reported above */
+ continue;
+ else if (sts > MAXARGS) {
+ error("too many arguments - try \"help\"");
+ /* consume until '\n' reached... */
+ while ((n = read_token(stdin, inbuf[0], MAXBUFLEN, '\n')) > 0);
+ if (n == -2) {
+ command_quit();
+ done = 1; /* reached EOF, bail out! */
+ }
+ }
+ else if (sts > 0 && !done)
+ done = (configure(sts) == 1);
+ } while (!done);
+}
+
+static pmLongOptions longopts[] = {
+ PMAPI_OPTIONS_HEADER("Options"),
+ { "", 0, 'c', 0, "an automated pmie configuration by the system" },
+ { "force", 0, 'F', 0, "force creation/update of pmie file, then exit" },
+ { "config", 1, 'f', "FILE", "location of generated pmie configuration file" },
+ { "rules", 1, 'r', "PATH", "path specifying groups of rule files" },
+ { "verbose", 0, 'v', 0, "increase level of diagnostics" },
+ PMOPT_HELP,
+ PMAPI_OPTIONS_END
+};
+
+static pmOptions opts = {
+ .flags = PM_OPTFLAG_NOFLUSH,
+ .short_options = "cFf:r:v?",
+ .long_options = longopts,
+ .short_usage = "[options] [ command [args...] ]",
+};
+
+int
+main(int argc, char **argv)
+{
+ int c;
+ int force = 0;
+ char *p;
+ char *in_rules = NULL;
+ char *in_pmie = NULL;
+
+ while ((c = pmgetopt_r(argc, argv, &opts)) != EOF) {
+ switch (c) {
+ case 'c':
+ autocreate = 1;
+ interactive = 0;
+ break;
+
+ case 'F':
+ force = 1;
+ break;
+
+ case 'f':
+ in_pmie = opts.optarg;
+ break;
+
+ case 'r':
+ in_rules = opts.optarg;
+ break;
+
+ case 'v':
+ verbose = 1;
+ break;
+
+ case '?':
+ default:
+ opts.errors++;
+ }
+ }
+
+ if (force && opts.optind < argc) {
+ pmprintf("%s: cannot use -F option with a command\n", pmProgname);
+ opts.optind = argc;
+ opts.errors++;
+ }
+
+ for (c = 0; opts.optind < argc && c < MAXARGS; c++) {
+ strncpy(inbuf[c], argv[opts.optind++], MAXBUFLEN);
+ inbuf[c][MAXBUFLEN] = '\0';
+ interactive = 0;
+ }
+ if (opts.optind < argc) {
+ pmprintf("%s: too many arguments\n", pmProgname);
+ opts.errors++;
+ }
+
+ if (opts.errors) {
+ pmUsageMessage(&opts);
+ pmprintf("\nCommands:\n%s\n", help);
+ pmflush();
+ exit(1);
+ }
+
+ if ((p = initialise(in_rules, in_pmie, warn, sizeof(warn))) != NULL) {
+ error(p);
+ exit(1);
+ }
+ sort_rules();
+
+ if (rulecount <= 1) {
+ fprintf(stderr, "%s: no rules were found using rule path: %s\n",
+ pmProgname, get_rules());
+ exit(1);
+ }
+
+ if (force || (*warn && p == NULL)) { /* force/pmie doesn't exist */
+ if (write_pmie() != 0)
+ exit(1);
+ else if (force)
+ exit(0);
+ }
+ else if (*warn) /* some other warning */
+ error(warn);
+
+ if (interactive) {
+ if (!isatty(0)) /* reading commands from a file */
+ interactive = 0;
+ else if (isatty(1)) { /* be $PAGER, handle window-resize */
+ setscroll();
+ onwinch(0);
+ }
+ interact();
+ }
+ else if (configure(c) == -1)
+ exit(1);
+ command_quit();
+ exit(0);
+ /*NOTREACHED*/
+}
diff --git a/src/pmieconf/rate-syscalls.c b/src/pmieconf/rate-syscalls.c
new file mode 100644
index 0000000..eb7643e
--- /dev/null
+++ b/src/pmieconf/rate-syscalls.c
@@ -0,0 +1,140 @@
+/*
+ * Copyright (c) 2010 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 "pmapi.h"
+#include "impl.h"
+
+/*
+ * test program to calibrate system call rates ... just compile
+ * and run:
+ * $ make rate-syscalls
+ * $ ./rate-syscalls
+ *
+ * useful for *cpu/syscall rules
+ */
+
+int
+main()
+{
+ int fd;
+ int i;
+ int n;
+ int c;
+ struct timeval now;
+ struct timeval then;
+ struct timeval eek;
+ double delta;
+ struct hostent *servInfo;
+ int s;
+ struct sockaddr_in myAddr;
+ struct linger noLinger = {1, 0};
+ int scale = 2;
+
+ __pmtimevalNow(&then);
+ n = 600000 * scale;
+ for (i = 0; i < n; i++)
+ getpid();
+ __pmtimevalNow(&now);
+ delta = now.tv_sec - then.tv_sec +
+ (double)(now.tv_usec - then.tv_usec) / 1000000;
+ printf("getpid()\t\t\t- %9d syscalls/sec [%.2f sec]\n",
+ (int)(0.5 + n / delta), delta);
+
+ __pmtimevalNow(&then);
+ n = 300000 * scale;
+ for (i = 0; i < n; i++)
+ __pmtimevalNow(&eek);
+ __pmtimevalNow(&now);
+ delta = now.tv_sec - then.tv_sec +
+ (double)(now.tv_usec - then.tv_usec) / 1000000;
+ printf("gettimeofday()\t\t\t- %9d syscalls/sec [%.2f sec]\n",
+ (int)(0.5 + n / delta), delta);
+
+ fd = open("/dev/null", 0);
+ n = 150000 * scale;
+ __pmtimevalNow(&then);
+ for (i = 0; i < n; i++) {
+ /* expect EOF */
+ read(fd, &c, 1);
+ }
+ __pmtimevalNow(&now);
+ delta = now.tv_sec - then.tv_sec +
+ (double)(now.tv_usec - then.tv_usec) / 1000000;
+ printf("read() at end of file\t\t- %9d syscalls/sec [%.2f sec]\n",
+ (int)(0.5 + n / delta), delta);
+ close(fd);
+
+ fd = open("/dev/null", 0);
+ n = 400000 * scale;
+ __pmtimevalNow(&then);
+ for (i = 0; i < n; i++) {
+ lseek(fd, 0L, 0);
+ }
+ __pmtimevalNow(&now);
+ delta = now.tv_sec - then.tv_sec +
+ (double)(now.tv_usec - then.tv_usec) / 1000000;
+ printf("lseek() to start of file\t- %9d syscalls/sec [%.2f sec]\n",
+ (int)(0.5 + n / delta), delta);
+ close(fd);
+
+ unlink("/tmp/creat-clo");
+ n = 20000 * scale;
+ __pmtimevalNow(&then);
+ for (i = 0; i < n; i++) {
+ if ((fd = creat("/tmp/creat-clo", 0644)) < 0) {
+ fprintf(stderr, "creat: %s\n", osstrerror());
+ exit(1);
+ }
+ close(fd);
+ }
+ __pmtimevalNow(&now);
+ delta = now.tv_sec - then.tv_sec +
+ (double)(now.tv_usec - then.tv_usec) / 1000000;
+ printf("file creat() and close()\t- %9d syscalls/sec [%.2f sec]\n",
+ (int)(0.5 + 2*n / delta), delta);
+ unlink("/tmp/creat-clo");
+
+ servInfo = gethostbyname("localhost");
+ memset(&myAddr, 0, sizeof(myAddr));
+ myAddr.sin_family = AF_INET;
+ memcpy(&myAddr.sin_addr, servInfo->h_addr, servInfo->h_length);
+ myAddr.sin_port = htons(80);
+ n = 4000 * scale;
+ __pmtimevalNow(&then);
+ for (i = 0; i < n; i++) {
+
+ if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+ fprintf(stderr, "socket: %s\n", netstrerror());
+ exit(1);
+ }
+
+ if (setsockopt(fd, SOL_SOCKET, SO_LINGER, (char *) &noLinger, sizeof(noLinger)) < 0) {
+ fprintf(stderr, "setsockopt(SO_LINGER): %s\n", netstrerror());
+ exit(1);
+ }
+
+ if (connect(s, (struct sockaddr*) &myAddr, sizeof(myAddr)) < 0) {
+ fprintf(stderr, "connect: %s\n", netstrerror());
+ exit(1);
+ }
+ close(s);
+ }
+ __pmtimevalNow(&now);
+ delta = now.tv_sec - then.tv_sec +
+ (double)(now.tv_usec - then.tv_usec) / 1000000;
+ printf("socket(), connect() and close()\t- %9d syscalls/sec [%.2f sec]\n",
+ (int)(0.5 + 3*n / delta), delta);
+
+ return 0;
+}
diff --git a/src/pmieconf/rules.c b/src/pmieconf/rules.c
new file mode 100644
index 0000000..cb0c9a0
--- /dev/null
+++ b/src/pmieconf/rules.c
@@ -0,0 +1,2335 @@
+/*
+ * rules.c - rule description parsing routines (rules & pmie config)
+ *
+ * Copyright (c) 1998-2002 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 <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <time.h>
+#include <ctype.h>
+#include <string.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include "pmapi.h"
+#include "impl.h"
+#include "rules.h"
+#include "stats.h"
+
+#define SEP __pmPathSeparator()
+
+
+#define PMIE_FILE "pmieconf-pmie"
+#define PMIE_VERSION "1" /* local configurations file format version */
+#define RULES_FILE "pmieconf-rules"
+#define RULES_VERSION "1" /* rule description file format version */
+
+#define START_STRING \
+ "// --- START GENERATED SECTION (do not change this section) ---\n"
+#define END_STRING \
+ "// --- END GENERATED SECTION (changes below will be preserved) ---\n"
+#define TOKEN_LENGTH 2048 /* max length of input token, incl string */
+#define LINE_LENGTH 4096
+
+#if !defined(sgi)
+#define PROC_DIR "/proc"
+#else
+#define PROC_DIR "/proc/pinfo"
+#endif
+
+char errmsg[512]; /* error message buffer */
+char rulepath[MAXPATHLEN+1]; /* root of rules files */
+char pmiefile[MAXPATHLEN+1]; /* pmie configuration file */
+char token[TOKEN_LENGTH+1];
+
+rule_t *rulelist; /* global list of rules */
+unsigned int rulecount; /* # rule list elements */
+rule_t *globals; /* list of atoms with global scope */
+
+#define GLOBAL_LEN 7
+static char global_name[] = "global"; /* GLOBAL_LEN chars long */
+static char global_data[] = "generic variables applied to all rules";
+static char global_help[] = \
+ "The global variables are used by all rules, but their values can be\n"
+ "overridden at the level of an individual rule or group of rules.";
+static char yes[] = "yes";
+static char no[] = "no";
+
+static char *filename; /* file currently being parsed */
+static unsigned int linenum; /* input line number */
+
+symbol_t types[] = {
+ { TYPE_STRING, "string" }, /* predicate data types */
+ { TYPE_DOUBLE, "double" },
+ { TYPE_INTEGER, "integer" },
+ { TYPE_UNSIGNED, "unsigned" },
+ { TYPE_PERCENT, "percent" },
+ { TYPE_HOSTLIST, "hostlist" },
+ { TYPE_INSTLIST, "instlist" },
+ { TYPE_PRINT, "print" }, /* action types */
+ { TYPE_SHELL, "shell" },
+ { TYPE_ALARM, "alarm" },
+ { TYPE_SYSLOG, "syslog" },
+ { TYPE_RULE, "rule" }, /* fundamental type */
+};
+int numtypes = (sizeof(types)/sizeof(types[0]));
+
+symbol_t attribs[] = {
+ { ATTRIB_HELP, "help" },
+ { ATTRIB_MODIFY, "modify" },
+ { ATTRIB_ENABLED, "enabled" },
+ { ATTRIB_DISPLAY, "display" },
+ { ATTRIB_DEFAULT, "default" },
+ { ATTRIB_DEFAULT, "summary" }, /* alias for "default" */
+ { ATTRIB_VERSION, "version" }, /* applies to rules only */
+ { ATTRIB_PREDICATE, "predicate" }, /* applies to rules only */
+ { ATTRIB_ENUMERATE, "enumerate" }, /* applies to rules only */
+};
+int numattribs = (sizeof(attribs)/sizeof(attribs[0]));
+
+/* pmiefile variables */
+static int gotpath; /* state flag - has realpath been run */
+static char *save_area; /* holds text to restore on write */
+static int sa_size; /* current size of save area */
+static int sa_mark = 1; /* number used chars in save area, 1 for \0 */
+static dep_t *dlist; /* list of depreciated rules */
+static int dcount; /* number of entries in dlist */
+static char drulestring[] = "rule definition no longer exists";
+static char dverstring[] = "rule version no longer supported";
+
+/* io-related stuff */
+extern int resized(void);
+
+char *get_pmiefile(void) { return &pmiefile[0]; }
+char *get_rules(void) { return &rulepath[0]; }
+
+char *
+get_aname(rule_t *r, atom_t *a)
+{
+ if (r == globals)
+ return &a->name[GLOBAL_LEN]; /* lose "globals." at the start */
+ return a->name;
+}
+
+
+/*
+ * #### error reporting routines ###
+ */
+
+static void
+alloc_error(size_t request)
+{
+ if (linenum == 0) /* parsing user input, not a file */
+ snprintf(errmsg, sizeof(errmsg), "insufficient memory for requested operation.\n"
+ " requested: %u bytes", (unsigned int)request);
+ else
+ snprintf(errmsg, sizeof(errmsg), "insufficient memory for parsing file.\n"
+ " requested: %u bytes", (unsigned int)request);
+}
+
+static void
+parse_error(char *expected, char *found)
+{
+ if (linenum == 0) /* parsing user input, not a file */
+ snprintf(errmsg, sizeof(errmsg), "input is invalid - expected %.60s, got \"%.60s\"",
+ expected, found);
+ else
+ snprintf(errmsg, sizeof(errmsg), "file parsing error.\n"
+ " line number: %u (\"%s\")\n"
+ " expected: %.60s\n"
+ " found: %.60s", linenum, filename, expected, found);
+}
+
+/* report attribute format error */
+static void
+type_error(char *attrib, char *expected)
+{
+ snprintf(errmsg, sizeof(errmsg), "%s's value is invalid.\n"
+ " It should %s.", attrib, expected);
+}
+
+
+/*
+ * #### search routines ###
+ */
+
+char *
+find_rule(char *name, rule_t **rule)
+{
+ int i;
+
+ for (i = 0; i < rulecount; i++) {
+ if (strcmp(rulelist[i].self.name, name) == 0) {
+ *rule = &rulelist[i];
+ return NULL;
+ }
+ }
+ snprintf(errmsg, sizeof(errmsg), "rule named \"%s\" does not exist", name);
+ return errmsg;
+}
+
+/* is global attribute 'atom' overridden by a local in 'rule's atom list */
+int
+is_overridden(rule_t *rule, atom_t *atom)
+{
+ atom_t *aptr;
+
+ for (aptr = rule->self.next; aptr != NULL; aptr = aptr->next)
+ if (strcmp(get_aname(globals, atom), get_aname(rule, aptr)) == 0)
+ return 1;
+ return 0;
+}
+
+/* tests whether a rule is in the fullname group, if so returns 0 */
+int
+rule_match(char *fullname, char *rulename)
+{
+ char *s;
+
+ /* if fullname == rulename, then obvious match */
+ if (strcmp(fullname, rulename) == 0)
+ return 1;
+ /* fullname may be a group, so match against rulename's groups */
+ s = strcpy(token, rulename); /* reuse the token buffer */
+ while ((s = strrchr(s, '.')) != NULL) {
+ s[0] = '\0';
+ if (strcmp(token, fullname) == 0)
+ return 1;
+ }
+ return 0;
+}
+
+/* find rule or set of rules in given rule or group name */
+char *
+lookup_rules(char *name, rule_t ***rlist, unsigned int *count, int all)
+{
+ size_t size;
+ rule_t **rptr = NULL;
+ unsigned int i;
+ unsigned int matches = 0;
+
+ /* search through the rulelist and build up rlist & count */
+ for (i = 0; i < rulecount; i++) {
+ /* don't match globals if we've been asked for "all" */
+ if ((all && i > 0) || rule_match(name, rulelist[i].self.name)) {
+ size = (1 + matches) * sizeof(rule_t *);
+ if ((rptr = (rule_t **)realloc(rptr, size)) == NULL) {
+ snprintf(errmsg, sizeof(errmsg), "insufficient memory for rule search"
+ " (needed %u bytes)\n", (unsigned int)size);
+ return errmsg;
+ }
+ rptr[matches] = &rulelist[i];
+ matches++;
+ }
+ }
+ if (matches == 0) {
+ snprintf(errmsg, sizeof(errmsg), "no group or rule names match \"%s\"", name);
+ return errmsg;
+ }
+ *rlist = rptr; /* rlist must be freed by caller */
+ *count = matches;
+ return NULL;
+}
+
+
+/*
+ * #### memory management routines ###
+ */
+
+static char *
+alloc_string(size_t size)
+{
+ char *p;
+
+ if ((p = (char *)malloc(size)) == NULL)
+ alloc_error(size);
+ return p;
+}
+
+atom_t *
+alloc_atom(rule_t *r, atom_t atom, int global)
+{
+ atom_t *aptr;
+ atom_t *tmp;
+
+ /* create some space and copy in the atom data we have already */
+ if ((aptr = (atom_t *)malloc(sizeof(atom_t))) == NULL) {
+ alloc_error(sizeof(atom_t));
+ return NULL;
+ }
+ *aptr = atom;
+ aptr->next = NULL; /* want contents of this atom, but not rest of list */
+ if (global) { /* applies to all rules */
+ r = rulelist;
+ aptr->global = 1;
+ }
+
+ aptr->next = NULL; /* want contents of this atom, but not rest of list */
+
+ /* stick into the list of atoms associated with this rule */
+ if (r->self.next == NULL)
+ r->self.next = aptr; /* insert at head of list */
+ else {
+ for (tmp = r->self.next; tmp->next != NULL; tmp = tmp->next);
+ tmp->next = aptr; /* append at tail of list */
+ }
+
+ return aptr;
+}
+
+rule_t *
+alloc_rule(rule_t rule)
+{
+ size_t size;
+ rule_t *rptr;
+
+ /* first check that name is unique */
+ if (find_rule(rule.self.name, &rptr) == NULL) {
+ snprintf(errmsg, sizeof(errmsg), "rule name \"%s\" has already been used, duplicate name"
+ " found in:\n\t\"%.60s\", line %u.", rule.self.name, filename, linenum);
+ return NULL;
+ }
+ size = (rulecount+1) * sizeof(rule_t);
+ if ((rulelist = globals = (rule_t *)realloc(rulelist, size)) == NULL) {
+ alloc_error(size);
+ return NULL;
+ }
+ rptr = &rulelist[rulecount];
+ *rptr = rule;
+ rulecount++;
+ return rptr;
+}
+
+
+/*
+ * #### misc parsing routines ###
+ */
+
+/* given string contains no isgraph chars? */
+int
+empty_string(char *s)
+{
+ char *str = s;
+ while (*str != '\0') {
+ if (isgraph((int)*str))
+ return 0;
+ str++;
+ }
+ return 1;
+}
+
+
+/* lookup keyword, returns symbol identifier or -1 if not there */
+int
+map_symbol(symbol_t *table, int tsize, char *symbol)
+{
+ int i;
+
+ for (i = 0; i < tsize; i++) {
+ if (strcmp(symbol, table[i].symbol) == 0)
+ return table[i].symbol_id;
+ }
+ return -1;
+}
+
+/* lookup symbol identifier, returns keyword or NULL if not there */
+char *
+map_identifier(symbol_t *table, int tsize, int symbol_id)
+{
+ int i;
+
+ for (i = 0; i < tsize; i++) {
+ if (symbol_id == table[i].symbol_id)
+ return table[i].symbol;
+ }
+ return NULL;
+}
+
+
+/* parse yes/no attribute value; returns 0 no, 1 yes, -1 error */
+int
+map_boolean(char *token)
+{
+ if (token[0] == 'y')
+ return 1;
+ if (token[0] == 'n')
+ return 0;
+ parse_error("yes or no", token);
+ return -1;
+}
+
+
+/* scan token from string, return 1 ok, 0 no more, -1 error */
+int
+string_token(char **scan, char *token)
+{
+ char *s = *scan;
+ char *t = token;
+
+ while (! isgraph((int)*s) || *s == ',') {
+ if (*s == '\0')
+ return 0;
+ s++;
+ }
+
+ if (*s == '\'') { /* quoted token */
+ *t++ = *s++;
+ while (*s != '\'') {
+ if (*s == '\\')
+ s++;
+ if (*s == '\0')
+ return -1;
+ *t++ = *s++;
+ }
+ *t++ = *s++;
+ }
+ else { /* ordinary token */
+ while (isgraph((int)*s) && *s != ',')
+ *t++ = *s++;
+ }
+
+ *t = '\0';
+ *scan = s;
+ return 1;
+}
+
+
+/* check proposed value for type, returns NULL/failure message */
+char *
+validate(int type, char *name, char *value)
+{
+ int x;
+ char *s;
+ double d;
+ /*
+ * Below we don't care about the value from strtol() and strtoul()
+ * we're interested in updating the pointer "s". The messiness is
+ * thanks to gcc and glibc ... strtol() amd strtoul() are marked
+ * __attribute__((warn_unused_result)) ... to avoid warnings on all
+ * platforms, assign to dummy variables that are explicitly marked
+ * unused.
+ */
+ long l __attribute__((unused));
+ unsigned long ul __attribute__((unused));
+
+ switch (type) {
+ case TYPE_RULE:
+ case TYPE_STRING:
+ break;
+ case TYPE_SHELL:
+ case TYPE_PRINT:
+ case TYPE_ALARM:
+ case TYPE_SYSLOG:
+ if (map_boolean(value) < 0)
+ return errmsg;
+ break;
+ case TYPE_DOUBLE:
+ d = strtod(value, &s);
+ if (*s != '\0') {
+ type_error(name, "be a real number");
+ return errmsg;
+ }
+ break;
+ case TYPE_INTEGER:
+ l = strtol(value, &s, 10);
+ if (*s != '\0') {
+ type_error(name, "be an integer number");
+ return errmsg;
+ }
+ break;
+ case TYPE_UNSIGNED:
+ ul = strtoul(value, &s, 10);
+ if (*s != '\0') {
+ type_error(name, "be a positive integer number");
+ return errmsg;
+ }
+ break;
+ case TYPE_PERCENT:
+ if ((s = strrchr(value, '%')) != NULL) /* % as final char is OK */
+ *s = '\0';
+ d = strtod(value, &s);
+ if (*s != '\0' || d < 0.0 || d > 100.0) {
+ type_error(name, "be a percentage between 0.0 and 100.0");
+ return errmsg;
+ }
+ break;
+ case TYPE_HOSTLIST:
+ case TYPE_INSTLIST:
+ if ((s = alloc_string(strlen(value)+1)) == NULL)
+ return errmsg;
+ while ((x = string_token(&value, s)) > 0)
+ ;
+ if (x < 0) {
+ type_error(name, "include a closing single quote");
+ return errmsg;
+ }
+ free(s);
+ break;
+ }
+ return NULL;
+}
+
+
+/*
+ * printable string form of atoms value, returns NULL terminated string
+ * pp (pretty print) argument valued 1 means use format appropriate for
+ * a user interface
+ */
+
+char *
+value_string(atom_t *atom, int pp)
+{
+ int key;
+ int i = 0;
+ int start = 1;
+ int quoted = 0;
+ char *s;
+
+ switch (atom->type) {
+ case TYPE_RULE:
+ case TYPE_STRING:
+ if (pp) {
+ snprintf(token, sizeof(token), "\"%s\"", atom->data);
+ return token;
+ }
+ return atom->data;
+ case TYPE_PRINT:
+ case TYPE_SHELL:
+ case TYPE_ALARM:
+ case TYPE_SYSLOG:
+ return atom->enabled? yes : no;
+ case TYPE_HOSTLIST:
+ case TYPE_INSTLIST:
+ if (pp) token[i++] = '"';
+ if (atom->type == TYPE_HOSTLIST) key = ':';
+ else key = '#';
+ for (s = atom->data; *s != '\0'; s++) {
+ if (!isspace((int)*s)) {
+ if (start && !pp) {
+ token[i++] = key;
+ token[i++] = '\'';
+ if (*s != '\'')
+ quoted = 0;
+ else if (!quoted) {
+ quoted = 1;
+ start = 0;
+ continue;
+ }
+ }
+ else if (*s == '\'' && !start && !pp)
+ quoted = 0;
+ start = 0;
+ }
+ else if (!pp && !quoted) {
+ quoted = 0;
+ if (i > 0 && token[i-1] != '\'')
+ token[i++] = '\'';
+ start = 1;
+ }
+ token[i++] = *s;
+ }
+ if (!pp && i > 0 && token[i-1] != '\'')
+ token[i++] = '\'';
+ else if (pp) token[i++] = '"';
+ token[i++] = '\0';
+ return token;
+ case TYPE_DOUBLE:
+ snprintf(token, sizeof(token), "%g", strtod(atom->data, &s));
+ return token;
+ case TYPE_INTEGER:
+ snprintf(token, sizeof(token), "%ld", strtol(atom->data, &s, 10));
+ return token;
+ case TYPE_UNSIGNED:
+ snprintf(token, sizeof(token), "%lu", strtoul(atom->data, &s, 10));
+ return token;
+ case TYPE_PERCENT:
+ snprintf(token, sizeof(token), "%g%c", strtod(atom->data, &s), pp? '%':'\0');
+ return token;
+ }
+ return NULL;
+}
+
+
+/* #### rules file parsing routines #### */
+
+
+/* returns attrib number or -1 if not an attribute */
+int
+is_attribute(char *aname)
+{
+ return map_symbol(attribs, numattribs, aname);
+}
+
+/* returns attrib value as a string, or NULL on error */
+char *
+get_attribute(char *attrib, atom_t *atom)
+{
+ char *value = NULL;
+
+ switch (map_symbol(attribs, numattribs, attrib)) {
+ case ATTRIB_HELP:
+ value = atom->help;
+ break;
+ case ATTRIB_MODIFY:
+ if (atom->modify) value = yes;
+ else value = no;
+ break;
+ case ATTRIB_ENABLED:
+ if (atom->enabled) value = yes;
+ else value = no;
+ break;
+ case ATTRIB_DISPLAY:
+ if (atom->display) value = yes;
+ else value = no;
+ break;
+ case ATTRIB_DEFAULT:
+ if (IS_ACTION(atom->type)) {
+ if (atom->enabled) value = yes;
+ else value = no;
+ }
+ else
+ value = atom->data;
+ break;
+ }
+ return value;
+}
+
+
+/*
+ * #### sorting routines ###
+ */
+
+static int
+compare_rules(const void *a, const void *b)
+{
+ rule_t *ra = (rule_t *)a;
+ rule_t *rb = (rule_t *)b;
+ return strcmp(ra->self.name, rb->self.name);
+}
+
+void
+sort_rules(void)
+{
+ /* start at second array element so that 'globals' is skipped */
+ qsort(&rulelist[1], rulecount-1, sizeof(rule_t), compare_rules);
+}
+
+
+/* revert to default rules file values for a single atom (enabled/data/both) */
+static char *
+atom_defaults(atom_t *a, atom_t *p, char *param)
+{
+ int sts = map_symbol(attribs, numattribs, param);
+
+ if (sts != -1) { /* an attribute - is it valid? */
+ if (sts == ATTRIB_ENABLED) {
+ if (a->global) { /* this was a global atom promoted to local */
+ if (p) p->next = a->next;
+ free(a->name);
+ free(a);
+ a = NULL;
+ }
+ else {
+ a->enabled = a->denabled; /* reset enabled flag */
+ a->changed = 0;
+ }
+ return NULL;
+ }
+ snprintf(errmsg, sizeof(errmsg), "variable \"%s\" is inappropriate for this "
+ "operation", param);
+ return errmsg;
+ }
+ else {
+ if (a->global) { /* this was a global atom promoted to local */
+ if (p) p->next = a->next;
+ free(a->name);
+ free(a);
+ a = NULL;
+ }
+ else {
+ if (strcmp(a->data, a->ddata) != 0) { /* need to alloc mem? */
+ free(a->data);
+ if ((a->data = strdup(a->ddata)) == NULL) {
+ snprintf(errmsg, sizeof(errmsg), "insufficient memory to set defaults");
+ return errmsg;
+ }
+ }
+ a->enabled = a->denabled;
+ a->changed = 0;
+ }
+ return NULL;
+ }
+}
+
+/* revert to default rules values for a rule or attribute (enabled/data/both) */
+char *
+rule_defaults(rule_t *rule, char *param)
+{
+ atom_t *aptr;
+ atom_t *prev = NULL;
+
+ if (param == NULL) { /* for this rule, reset all attributes */
+ for (aptr = &rule->self; aptr != NULL; aptr = aptr->next) {
+ atom_defaults(aptr, prev, aptr->name);
+ prev = aptr;
+ }
+ }
+ else { /* find the associated atom, and just reset that */
+ if (map_symbol(attribs, numattribs, param) != -1) {
+ rule->self.enabled = rule->self.denabled; /* reset enabled flag */
+ rule->self.changed = 0;
+ return NULL;
+ }
+ for (aptr = &rule->self; aptr != NULL; aptr = aptr->next) {
+ if (strcmp(get_aname(rule, aptr), param) == 0)
+ return atom_defaults(aptr, prev, param);
+ prev = aptr;
+ }
+ }
+ return NULL;
+}
+
+/* set an attribute field in an atom; returns NULL/failure message */
+static char *
+set_attribute(rule_t *r, atom_t *atom, int attrib, char *value, int changed)
+{
+ char *s;
+ int sts;
+
+ switch(attrib) {
+ case ATTRIB_HELP:
+ if (empty_string(value)) {
+ parse_error("non-empty string for help", value);
+ return errmsg;
+ }
+ if ((s = alloc_string(strlen(value)+1)) == NULL)
+ return errmsg;
+ atom->help = strcpy(s, value);
+ break;
+ case ATTRIB_MODIFY:
+ if ((sts = map_boolean(value)) < 0)
+ return errmsg;
+ atom->modify = sts;
+ break;
+ case ATTRIB_ENABLED:
+ if ((sts = map_boolean(value)) < 0)
+ return errmsg;
+ if (!changed) /* initially, set enabled to default */
+ atom->denabled = sts;
+ atom->enabled = sts;
+ break;
+ case ATTRIB_DISPLAY:
+ if ((sts = map_boolean(value)) < 0)
+ return errmsg;
+ atom->display = sts;
+ break;
+ case ATTRIB_DEFAULT:
+ if (IS_ACTION(atom->type) && changed) {
+ if ((sts = map_boolean(value)) < 0)
+ return errmsg;
+ atom->enabled = sts;
+ }
+ else { /* actions from rules file (string) handled here too... */
+ if (!IS_ACTION(atom->type) &&
+ (validate(atom->type, get_aname(r, atom), value) != NULL))
+ return errmsg;
+ sts = strlen(value)+1;
+ if ((s = alloc_string(sts)) == NULL)
+ return errmsg;
+ atom->data = strcpy(s, value);
+ if (!changed) { /* initially, set the default as well */
+ if ((s = alloc_string(sts)) == NULL) {
+ free(atom->data);
+ atom->data = NULL;
+ return errmsg;
+ }
+ atom->ddata = strcpy(s, value);
+ }
+ }
+ break;
+ }
+ if (changed)
+ atom->changed = 1;
+ return NULL;
+}
+
+/* set a parameter field in a rule; returns NULL/failure message */
+char *
+value_change(rule_t *rule, char *param, char *value)
+{
+ int sts;
+ atom_t *aptr;
+
+ if ((sts = map_symbol(attribs, numattribs, param)) != -1)
+ return set_attribute(rule, &rule->self, sts, value, 1);
+ else {
+ for (aptr = rule->self.next; aptr != NULL; aptr = aptr->next) {
+ if (strcmp(get_aname(rule, aptr), param) == 0)
+ return set_attribute(rule, aptr, ATTRIB_DEFAULT, value, 1);
+ }
+ /* if found in globals, promote the global to customised local.. */
+ for (aptr = globals->self.next; aptr != NULL; aptr = aptr->next) {
+ if (strcmp(get_aname(globals, aptr), param) == 0) {
+ if ((aptr = alloc_atom(rule, *aptr, 0)) == NULL)
+ return errmsg;
+ if ((aptr->name = strdup(get_aname(globals, aptr))) == NULL) {
+ snprintf(errmsg, sizeof(errmsg), "insufficient memory to change value");
+ return errmsg;
+ }
+ return set_attribute(globals, aptr, ATTRIB_DEFAULT, value, 1);
+ }
+ }
+ }
+ snprintf(errmsg, sizeof(errmsg), "variable \"%s\" is undefined for rule %s",
+ param, rule->self.name);
+ return errmsg;
+}
+
+static char *
+append_string(char *s, char *append, int len)
+{
+ size_t size = (strlen(s) + len + 1);
+
+#ifdef PMIECONF_DEBUG
+ fprintf(stderr, "debug - \"%s\" + (%d)\"%s\" = (%d chars)\n",
+ s, len, append, size);
+#endif
+ if ((s = (char *)realloc(s, size)) == NULL)
+ return NULL;
+ strncat(s, append, len);
+ s[size-1] = '\0';
+ return s;
+}
+
+/* fix up value strings by doing variable expansion */
+char *
+dollar_expand(rule_t *rule, char *string, int pp)
+{
+ atom_t *aptr;
+ char *tmp, *r;
+ char *sptr;
+ char *s;
+ char *mark = NULL;
+ char localbuf[TOKEN_LENGTH];
+
+#ifdef PMIECONF_DEBUG
+ fprintf(stderr, "debug - dollar_expand %s in %s\n", string, rule->self.name);
+#endif
+ if ((s = (char *)malloc(sizeof(char))) == NULL)
+ return NULL;
+ *s = '\0';
+
+ for (sptr = string; *sptr != '\0'; sptr++) {
+ if (*sptr == '\\' && *(sptr+1) == '$') { /* skip escaped $ */
+ if ((s = append_string(s, sptr+1, 1)) == NULL)
+ return NULL;
+ sptr++; /* move passed the escaped char */
+ continue;
+ }
+ if (*sptr == '$') {
+ if (mark == NULL) /* start of an expansion section */
+ mark = sptr+1;
+ else { /* end of an expansion section */
+ /* look through atom list & if not there search globally */
+ strncpy(localbuf, mark, sptr - mark);
+ localbuf[sptr - mark] = '\0';
+ mark = NULL;
+#ifdef PMIECONF_DEBUG
+ fprintf(stderr, "debug - expand localbuf: %s\n", localbuf);
+#endif
+ if ((tmp = get_attribute(localbuf, &rule->self)) == NULL) {
+ for (aptr = &rule->self; tmp == NULL && aptr != NULL; aptr = aptr->next)
+ if (strcmp(get_aname(rule, aptr), localbuf) == 0)
+ tmp = value_string(aptr, pp);
+ for (aptr = globals->self.next; tmp == NULL && aptr != NULL; aptr = aptr->next)
+ if (strcmp(get_aname(globals, aptr), localbuf) == 0)
+ tmp = value_string(aptr, pp);
+#ifdef PMIECONF_DEBUG
+ fprintf(stderr, "debug - expanded localbuf? %s\n", tmp);
+#endif
+ if (tmp == NULL) {
+ snprintf(errmsg, sizeof(errmsg), "variable \"$%s$\" in %s is undefined",
+ localbuf, rule->self.name);
+ free(s);
+ return NULL;
+ }
+ }
+ if (tmp != NULL) {
+ if ((r = dollar_expand(rule, tmp, pp)) == NULL) {
+ free(s);
+ return NULL;
+ }
+ if ((s = append_string(s, r, strlen(r))) == NULL) {
+ free(r);
+ return NULL;
+ }
+ free(r);
+ }
+ }
+ }
+ else if (mark == NULL) { /* need memory to hold this character */
+ if ((s = append_string(s, sptr, 1)) == NULL)
+ return NULL;
+ }
+ }
+ if (mark != NULL) /* no terminating '$' */
+ s = append_string(s, mark, strlen(mark));
+#ifdef PMIECONF_DEBUG
+ fprintf(stderr, "debug - expanded '%s' to '%s'\n", string, s);
+#endif
+ return s;
+}
+
+
+/*
+ * #### main parsing routines ###
+ */
+
+/* need a SIGWINCH-aware read routine for interactive pmieconf */
+static int
+mygetc(FILE *f)
+{
+ int c;
+
+ for (;;) {
+ setoserror(0);
+ c = getc(f);
+ /* did we get told to resize the window during read? */
+ if (c == -1 && oserror() == EINTR && resized() == 1)
+ continue; /* signal handled, try reading again */
+ break;
+ }
+ return c;
+}
+
+/*
+ * skip leading white space and comments, return first character in next token
+ * or zero on end of file
+ */
+static int
+prime_next_pread(FILE *f, int end)
+{
+ int c;
+
+ do {
+ c = mygetc(f);
+ if (c == '#')
+ do {
+ c = mygetc(f);
+ } while (c != '\n' && c != end && c != EOF);
+ if (c == end)
+ return 0;
+ else if (c == EOF)
+ return -2;
+ if (c == '\n' && end != '\n')
+ linenum++;
+ } while (!isgraph(c));
+ return c;
+}
+
+/*
+ * read next input token; returns 1 ok, 0 end, -1 error, -2 EOF (if end!=EOF)
+ * nb: `end' can be either EOL or EOF, depending on use of this routine
+ */
+int
+read_token(FILE *f, char *token, int token_length, int end)
+{
+ int c;
+ int n = 0;
+
+ switch (c = prime_next_pread(f, end)) {
+ case 0: /* end */
+ case -2: /* EOF */
+ return c;
+ case '"': /* scan string */
+ c = mygetc(f);
+ while (c != '"') {
+ if (c == '\\')
+ c = mygetc(f);
+ if (c == end || c == EOF || n == token_length) {
+ token[n] = '\0';
+ parse_error("end-of-string", token);
+ return -1;
+ }
+ if (c == '\n' && end != '\n')
+ linenum++;
+ token[n++] = c;
+ c = mygetc(f);
+ }
+ break;
+ case ';':
+ case '=':
+ token[n++] = c; /* single char token */
+ break;
+ default: /* some other token */
+ while (isgraph(c)) {
+ if (c == '=' || c == ';') {
+ ungetc(c, f);
+ break;
+ }
+ if (n == token_length) {
+ token[n] = '\0';
+ parse_error("end-of-token", token);
+ return -1;
+ }
+ token[n++] = c;
+ c = mygetc(f);
+ if (c == end || c == EOF)
+ ungetc(c, f);
+ }
+ if (c == '\n' && end != '\n')
+ linenum++;
+ break;
+ }
+
+ token[n] = '\0';
+ return 1;
+}
+
+/*
+ * get attribute list part of an atom; returns -1 on error, 0 on reaching
+ * the end of the attribute list, and 1 at end of each attribute.
+ */
+static int
+read_next_attribute(FILE *f, char **attr, char **value)
+{
+ int sts;
+
+ if ((sts = read_token(f, token, TOKEN_LENGTH, EOF)) <= 0) {
+ if (sts == 0)
+ parse_error("attribute or ';'", "end-of-file");
+ return -1;
+ }
+ if (token[0] == ';')
+ return 0;
+ if (map_symbol(attribs, numattribs, token) < 0) {
+ parse_error("attribute keyword", token);
+ return -1;
+ }
+ if ((*attr = alloc_string(strlen(token)+1)) == NULL)
+ return -1;
+ strcpy(*attr, token);
+ if ((sts = read_token(f, token, TOKEN_LENGTH, EOF)) <= 0
+ || token[0] != '=') {
+ if (sts == 0)
+ parse_error("=", "end-of-file");
+ else
+ parse_error("=", token);
+ free(*attr);
+ return -1;
+ }
+ if ((sts = read_token(f, token, TOKEN_LENGTH, EOF)) <= 0) {
+ if (sts == 0)
+ parse_error("attribute value", "end-of-file");
+ else
+ parse_error("attribute value", token);
+ free(*attr);
+ return -1;
+ }
+ if ((*value = alloc_string(strlen(token)+1)) == NULL) {
+ free(*attr);
+ return -1;
+ }
+ strcpy(*value, token);
+ return 1;
+}
+
+
+/* parse an atom, return NULL/failure message */
+static char *
+read_atom(FILE *f, rule_t *r, char *name, int type, int global)
+{
+ int sts;
+ int attrib;
+ char *attr;
+ char *value;
+ atom_t atom;
+
+ memset(&atom, 0, sizeof(atom_t));
+ atom.name = name;
+ atom.type = type;
+ atom.enabled = atom.display = atom.modify = 1; /* defaults */
+ for (;;) {
+ if ((sts = read_next_attribute(f, &attr, &value)) < 0)
+ return errmsg;
+ else if (sts == 0) { /* end of parameter list */
+ if (alloc_atom(r, atom, global) == NULL)
+ return errmsg;
+ break;
+ }
+ else {
+ if ((attrib = map_symbol(attribs, numattribs, attr)) < 0) {
+ parse_error("attribute keyword", attr);
+ goto fail;
+ }
+ if (set_attribute(r, &atom, attrib, value, 0) != NULL)
+ goto fail;
+ free(attr);
+ free(value);
+ }
+ }
+ return NULL;
+
+fail:
+ free(attr);
+ free(value);
+ return errmsg;
+}
+
+
+/* parse type-identifier pair, return NULL/failure message */
+static char *
+read_type(FILE *f, rule_t *r, int *type, int *global, char **name)
+{
+ int sts;
+
+ /* read type - rule, percent, double, unsigned, string... */
+ if ((sts = read_token(f, token, TOKEN_LENGTH, EOF)) < 0)
+ return errmsg;
+ else if (sts == 0)
+ return NULL;
+ if ((*type = map_symbol(types, numtypes, token)) < 0) {
+ parse_error("type keyword", token);
+ return errmsg;
+ }
+
+ /* read name identifying this rule/atom of type '*type' */
+ if ((sts = read_token(f, token, TOKEN_LENGTH, EOF)) <= 0)
+ return errmsg;
+ if ((*name = alloc_string(strlen(token)+1)) == NULL)
+ return errmsg;
+ strcpy(*name, token);
+ *global = (strncmp(*name, "global.", GLOBAL_LEN) == 0)? 1 : 0;
+
+ /* do some simple validity checks */
+ if (IS_RULE(*type) && strncmp(*name, global_name, GLOBAL_LEN-1) == 0) {
+ snprintf(errmsg, sizeof(errmsg), "rule name may not be \"%s\" - this is reserved",
+ global_name);
+ free(*name);
+ return errmsg;
+ }
+ if (r == NULL) { /* any rule defined yet? - simple validity checks */
+ if (*global && IS_RULE(*type)) {
+ snprintf(errmsg, sizeof(errmsg), "rules not allowed in global group: \"%s\"", *name);
+ free(*name);
+ return errmsg;
+ }
+ else if (!*global && !IS_RULE(*type)) { /* not global, and no rule */
+ snprintf(errmsg, sizeof(errmsg), "no rule defined, cannot make sense of %s \"%s\""
+ " without one\n line number: %u (\"%s\")\n",
+ types[*type].symbol, *name, linenum, filename);
+ free(*name);
+ return errmsg;
+ }
+ }
+ return NULL;
+}
+
+/* set an attribute field in an atom; returns NULL/failure message */
+static char *
+set_rule_attribute(rule_t *rule, int attrib, char *value)
+{
+ char *s;
+
+ if (attrib == ATTRIB_PREDICATE) {
+ if ((s = alloc_string(strlen(value)+1)) == NULL)
+ return errmsg;
+ rule->predicate = strcpy(s, value);
+ return NULL;
+ }
+ if (attrib == ATTRIB_ENUMERATE) {
+ if ((s = alloc_string(strlen(value)+1)) == NULL)
+ return errmsg;
+ rule->enumerate = strcpy(s, value);
+ return NULL;
+ }
+ else if (attrib == ATTRIB_VERSION) {
+ rule->version = strtoul(value, &s, 10);
+ if (*s != '\0') {
+ parse_error("version number", "be a positive integer number");
+ return errmsg;
+ }
+ return NULL;
+ }
+ /* else */
+ return set_attribute(rule, &rule->self, attrib, value, 0);
+}
+
+
+/* parse a single "rule" expression, return NULL/failure message */
+static char *
+read_rule(FILE *f, rule_t **r, char *name)
+{
+ int sts;
+ int attrib;
+ char *attr;
+ char *value;
+ rule_t rule;
+
+ memset(&rule, 0, sizeof(rule_t));
+ rule.self.name = name;
+ rule.self.type = TYPE_RULE;
+ rule.version = rule.self.enabled = rule.self.display = 1; /* defaults */
+ for (;;) {
+ if ((sts = read_next_attribute(f, &attr, &value)) < 0)
+ return errmsg;
+ else if (sts == 0) { /* end of attribute list */
+ if ((*r = alloc_rule(rule)) == NULL)
+ return errmsg;
+ break;
+ }
+ else {
+ if ((attrib = map_symbol(attribs, numattribs, attr)) < 0) {
+ parse_error("rule attribute keyword", attr);
+ goto fail;
+ }
+ if (set_rule_attribute(&rule, attrib, value) != NULL)
+ goto fail;
+ free(attr);
+ free(value);
+ }
+ }
+ return NULL;
+
+fail:
+ free(attr);
+ free(value);
+ return errmsg;
+}
+
+
+/* parse rule description file; returns NULL/failure message */
+static char *
+read_all_rules(FILE *f)
+{
+ rule_t *rule = NULL; /* current rule */
+ char *name = NULL;
+ int type = 0;
+ int global = 0;
+
+ /* rule files have quite a simple grammar, along these lines:
+ TYPE identifier [ ATTRIB '=' value ]* ';'
+ */
+ for (;;) {
+ if (read_type(f, rule, &type, &global, &name) != NULL)
+ return errmsg;
+ if (feof(f)) /* end of file reached without error */
+ break;
+ if (type == TYPE_RULE) {
+ if (read_rule(f, &rule, name) != NULL)
+ return errmsg;
+ }
+ else {
+ if (read_atom(f, rule, name, type, global) != NULL)
+ return errmsg;
+ }
+ }
+
+ return NULL;
+}
+
+
+/*
+ * validate header of rule description file, return NULL/failure message
+ */
+static char *
+read_pheader(FILE *f)
+{
+ int c;
+
+ c = getc(f);
+ if (c != '#' || read_token(f, token, TOKEN_LENGTH, EOF) != 1 ||
+ strcmp(token, RULES_FILE) != 0 ||
+ read_token(f, token, TOKEN_LENGTH, EOF) != 1) {
+ snprintf(errmsg, sizeof(errmsg), "%s is not a rule description file (bad header)\n"
+ "found \"%s\", expected \"%s\"", filename,
+ token, RULES_FILE);
+ return errmsg;
+ }
+ else if (strcmp(token, RULES_VERSION) != 0) { /* one version only */
+ snprintf(errmsg, sizeof(errmsg), "unknown version number in %s: \"%s\" (expected %s)",
+ filename, token, RULES_VERSION);
+ return errmsg;
+ }
+ return NULL;
+}
+
+
+/*
+ * builds up rule data structures for all rule files in given directory
+ * and all its subdirectories, returns NULL/failure message
+ */
+char *
+read_rule_subdir(char *subdir)
+{
+ struct stat sbuf;
+ struct dirent *dp;
+ FILE *fp;
+ DIR *dirp;
+ char fullpath[MAXPATHLEN+1];
+
+ if (stat(subdir, &sbuf) < 0) {
+ snprintf(errmsg, sizeof(errmsg), "cannot stat %s: %s",
+ subdir, osstrerror());
+ return errmsg;
+ }
+ if (!S_ISDIR(sbuf.st_mode)) {
+ if ((fp = fopen(subdir, "r")) == NULL) {
+ snprintf(errmsg, sizeof(errmsg), "cannot open %s: %s",
+ subdir, osstrerror());
+ return errmsg;
+ }
+ linenum = 1;
+ filename = subdir;
+ if (read_pheader(fp) == NULL) {
+ if (read_all_rules(fp) != NULL) {
+ fclose(fp);
+ return errmsg;
+ }
+ }
+#ifdef PMIECONF_DEBUG
+ else {
+ fprintf(stderr, "debug - %s isn't a pmie rule file: %s\n",
+ filename, errmsg);
+ }
+#endif
+ fclose(fp);
+ }
+ else {
+ /* iterate through the rules directory and for each subdirectory */
+ /* fetch all the rules along with associated parameters & values */
+
+ if ((dirp = opendir(subdir)) == NULL) {
+ snprintf(errmsg, sizeof(errmsg), "cannot opendir %s: %s", subdir, osstrerror());
+ return errmsg;
+ }
+ while ((dp = readdir(dirp)) != NULL) { /* groups */
+ if (strcmp(dp->d_name, ".") == 0 || strcmp(dp->d_name, "..") == 0)
+ continue;
+ snprintf(fullpath, sizeof(fullpath), "%s%c%s", subdir, SEP, dp->d_name);
+ if (read_rule_subdir(fullpath) != NULL) { /* recurse */
+ closedir(dirp);
+ return errmsg;
+ }
+ }
+ closedir(dirp);
+ }
+ return NULL;
+}
+
+
+/* ##### pmiefile parsing routines #### */
+
+
+/* returns NULL on successfully adding rule to list, else failure message */
+char *
+deprecate_rule(char *name, unsigned int version, int type)
+{
+ int index;
+
+ /* first check to see if this rule is deprecated already */
+ for (index = 0; index < dcount; index++) {
+ if (strcmp(dlist[index].name, name) == 0
+ && dlist[index].version == version)
+ return NULL;
+ }
+
+ /* get the memory we need & then keep a copy of deprecated rule info */
+ if ((dlist = (dep_t *)realloc(dlist, (dcount+1)*sizeof(dep_t))) == NULL
+ || (dlist[dcount].name = strdup(name)) == NULL) {
+ snprintf(errmsg, sizeof(errmsg), "insufficient memory to deprecate rule %s", name);
+ return errmsg;
+ }
+ dlist[dcount].type = type;
+ dlist[dcount].version = version;
+ if (type == DEPRECATE_NORULE)
+ dlist[dcount].reason = drulestring;
+ else
+ dlist[dcount].reason = dverstring;
+ dcount++;
+ return NULL;
+}
+
+/*
+ * makes list of deprecated rules available to caller (for warning message)
+ * nb: called following a pmiefile write to see what was deprecated during
+ * that write - subsequent writing of this pmiefile should not deprecate
+ * anything (deprecations done once & not again). caller must free list.
+ */
+int
+fetch_deprecated(dep_t **list)
+{
+ int sts;
+
+ *list = dlist;
+ sts = dcount;
+ dcount = 0;
+ return sts;
+}
+
+/* merges local customisations back into the rules atom list */
+static char *
+merge_local(unsigned int version, char *name, char *attrib, char *value)
+{
+ atom_t *aptr;
+ rule_t *rule;
+ int a;
+
+ /*
+ first find the rule to which this local belongs, then figure
+ out what sort of attribute this really is, and finally merge
+ the customisation back into the values in the rules attribs.
+ */
+
+ if (find_rule(name, &rule) != NULL) /* in pmiefile but not rules */
+ return NULL; /* this will be deprecated later */
+ else if (rule->version != version) /* no rule for this version */
+ return NULL; /* this will be deprecated later */
+
+ if ((a = is_attribute(attrib)) != -1)
+ return set_attribute(rule, &rule->self, a, value, 1);
+ else { /* search through this rules list of atoms */
+ for (aptr = rule->self.next; aptr != NULL; aptr = aptr->next) {
+ if (strcmp(get_aname(rule, aptr), attrib) == 0)
+ return set_attribute(rule, aptr, ATTRIB_DEFAULT, value, 1);
+ }
+ for (aptr = globals->self.next; aptr != NULL; aptr = aptr->next) {
+ if (strcmp(get_aname(globals, aptr), attrib) == 0) {
+ /* promote global to become a local */
+ if ((aptr = alloc_atom(rule, *aptr, 0)) == NULL)
+ return errmsg;
+ if ((aptr->name = strdup(get_aname(globals, aptr))) == NULL) {
+ snprintf(errmsg, sizeof(errmsg), "insufficient memory to change value");
+ return errmsg;
+ }
+ return set_attribute(globals, aptr, ATTRIB_DEFAULT, value, 1);
+ }
+ }
+ }
+ snprintf(errmsg, sizeof(errmsg), "variable \"%s\" is undefined for rule %s",
+ attrib, name);
+ return errmsg;
+}
+
+
+/*
+ * #### produce pmie configuration file from rules ###
+ */
+
+char *
+action_string(int type)
+{
+ switch (type) {
+ case TYPE_PRINT: return "print";
+ case TYPE_SHELL: return "shell";
+ case TYPE_ALARM: return "alarm";
+ case TYPE_SYSLOG: return "syslog";
+ }
+ return NULL;
+}
+
+static int
+expand_action(FILE *f, int count, rule_t *rule, atom_t *atom)
+{
+ char *p;
+ char *str;
+
+ if (IS_ACTION(atom->type) && atom->enabled) {
+ if ((str = dollar_expand(rule, " $holdoff$ ", 0)) == NULL)
+ return count;
+ if (count == 0)
+ fprintf(f, " -> ");
+ else
+ fprintf(f, "\n & ");
+ fprintf(f, "%s", action_string(atom->type));
+ fprintf(f, "%s", str);
+ free(str);
+ if ((str = dollar_expand(rule, atom->data, 0)) == NULL) {
+ fprintf(stderr, "Warning - failed to expand action for rule %s\n"
+ " string: \"%s\"\n", rule->self.name, atom->data);
+ /* keep going - too late to bail out without file corruption */
+ }
+#ifdef PMIECONF_DEBUG
+ else {
+ fprintf(stderr, "expanded action= \"%s\"\n", str);
+ }
+#endif
+ fputc('"', f);
+ for (p = str; p != NULL && *p; p++) { /* expand the '^' character */
+ if (*p == '/' && *(p+1) == '^') {
+ fputc(*p, f); p++;
+ fputc(*p, f); p++;
+ }
+ else if (*p == '^')
+ fputs("\" \"", f);
+ else fputc(*p, f);
+ }
+ fputc('"', f);
+ if (str != NULL)
+ free(str);
+ return count + 1;
+ }
+ return count;
+}
+
+
+/*
+ * this struct and the enumerate function are used only in generate_rules()
+ * and the enumerate() routines, for enumeration of either a hostlist or an
+ * instlist
+ */
+
+typedef struct {
+ atom_t *atom;
+ int nvalues;
+ char **valuelist;
+ char *restore;
+} enumlist_t;
+
+static enumlist_t *list;
+static int nlistitems;
+static int writecount;
+
+/*
+ * expands and writes out a single rule, and optionally the delta
+ * note: in the single rule case (not enumerated), we absolutely
+ * must write out the delta every time
+ */
+static char *
+write_rule(FILE *f, rule_t *rule)
+{
+ atom_t *aptr;
+ char *dgen = NULL; /* holds generated "delta" */
+ char *pgen; /* holds generated "predicate" */
+ int actions = 0;
+
+#ifdef PMIECONF_DEBUG
+ fprintf(stderr, "debug - writing rule %s\n", rule->self.name);
+#endif
+
+ if (writecount == 0 && (dgen = dollar_expand(rule, "$delta$", 0)) == NULL) {
+ snprintf(errmsg, sizeof(errmsg), "\"$delta$\" variable expansion failed for rule %s",
+ rule->self.name);
+ return errmsg;
+ }
+ if ((pgen = dollar_expand(rule, rule->predicate, 0)) == NULL) {
+ snprintf(errmsg, sizeof(errmsg), "\"$predicate$\" variable expansion failed "
+ "for rule %s", rule->self.name);
+ return errmsg;
+ }
+ if (writecount == 0) {
+ fprintf(f, "// %u %s\ndelta = %s;\n%s = \n", rule->version,
+ rule->self.name, dgen, rule->self.name);
+ free(dgen);
+ }
+ else /* we're enumerating, need to differentiate rule names */
+ fprintf(f, "%s%u = \n", rule->self.name, writecount);
+ fputs(pgen, f);
+ free(pgen);
+ for (aptr = rule->self.next; aptr != NULL; aptr = aptr->next)
+ actions = expand_action(f, actions, rule, aptr);
+ for (aptr = globals->self.next; aptr != NULL; aptr = aptr->next)
+ actions = expand_action(f, actions, rule, aptr);
+ fprintf(f, ";\n\n");
+
+ writecount++;
+ return NULL;
+}
+
+/* parses the "enumerate" value string passed in thru the rules file */
+char *
+parse_enumerate(rule_t *rule)
+{
+ atom_t *ap;
+ char *p = rule->enumerate;
+ int needsave = 0; /* should we save this variable name yet? */
+ int i = 0;
+
+#ifdef PMIECONF_DEBUG
+ fprintf(stderr, "debug - parse_enumerate called for %s\n", rule->self.name);
+#endif
+
+ nlistitems = 0;
+ list = NULL;
+ while (*p != '\0') {
+ if (!isspace((int)*p)) {
+ needsave = 1;
+ token[i++] = *p;
+ }
+ p++;
+ if ((isspace((int)*p) && needsave) || *p == '\0') {
+ token[i] = '\0';
+ i = 0;
+ if (map_symbol(attribs, numattribs, token) != -1) {
+ snprintf(errmsg, sizeof(errmsg), "cannot enumerate rule %s using attribute"
+ " \"%s\"", rule->self.name, token);
+ return errmsg;
+ }
+ else {
+ for (ap = rule->self.next; ap != NULL; ap = ap->next)
+ if (strcmp(get_aname(rule, ap), token) == 0)
+ goto foundname;
+ for (ap = globals->self.next; ap != NULL; ap = ap->next)
+ if (strcmp(get_aname(globals, ap), token) == 0)
+ goto foundname;
+ snprintf(errmsg, sizeof(errmsg), "variable \"%s\" undefined for enumerated"
+ " rule %s", token, rule->self.name);
+ return errmsg;
+ }
+foundname:
+ if (ap->type != TYPE_HOSTLIST && ap->type != TYPE_INSTLIST) {
+ snprintf(errmsg, sizeof(errmsg), "rules file error - \"$%s$\" in \"enumerate\" "
+ "clause of rule %s is not of type hostlist or instlist",
+ token, rule->self.name);
+ return errmsg;
+ }
+ /* increase size of list & keep a copy of the variable name */
+ if ((list = realloc(list, (nlistitems+1)*sizeof(enumlist_t))) == NULL) {
+ snprintf(errmsg, sizeof(errmsg), "insufficient memory to write rules");
+ return errmsg;
+ }
+ list[nlistitems].atom = ap;
+ list[nlistitems].nvalues = 0;
+ list[nlistitems].valuelist = NULL;
+ list[nlistitems].restore = NULL;
+#ifdef PMIECONF_DEBUG
+ fprintf(stderr, "debug - variable %s added to enum list (#%d)\n",
+ list[nlistitems].atom->name, nlistitems);
+#endif
+ nlistitems++;
+ needsave = 0;
+ }
+ }
+ return NULL;
+}
+
+/*
+ * converts a host/inst list into individual elements, overwrites liststr
+ * (turns all spaces to NULLs to mark string ends - reduces mallocing)
+ */
+char **
+get_listitems(char *liststr, int *count)
+{
+ char **result = NULL;
+ char *p = liststr;
+ int keepwhite = 0;
+ int startagain = 0; /* set to signify new list item has started */
+ int ptrcount = 0;
+
+ if ((result = realloc(result, (ptrcount+1) * sizeof(char *))) == NULL) {
+ snprintf(errmsg, sizeof(errmsg), "insufficient memory to get list elements");
+ return NULL;
+ }
+ result[ptrcount++] = p;
+ while (*p != '\0') {
+ if (!isspace((int)*p) || keepwhite) {
+ if (startagain) {
+ result = realloc(result, (ptrcount+1) * sizeof(char *));
+ if (result == NULL) {
+ snprintf(errmsg, sizeof(errmsg), "insufficient memory to get list elements");
+ return NULL;
+ }
+ result[ptrcount++] = p;
+ startagain = 0;
+ }
+ if (*p == '\\')
+ p++;
+ else if (*p == '\'')
+ keepwhite = !keepwhite;
+ }
+ else {
+ *p = '\0';
+ startagain = 1;
+ }
+ p++;
+ }
+#ifdef PMIECONF_DEBUG
+ fputs("debug - instances are:", stderr);
+ for (keepwhite = 0; keepwhite < ptrcount; keepwhite++)
+ fprintf(stderr, " %s", result[keepwhite]);
+ fputs("\n", stderr);
+#endif
+ *count = ptrcount;
+ return result;
+}
+
+/* expands variables from the "enumerate" string in the rules file */
+char *
+expand_enumerate(rule_t *rule)
+{
+ int i, j;
+ char *p;
+
+#ifdef PMIECONF_DEBUG
+ fprintf(stderr, "debug - expanding enum variables for rule %s\n",
+ rule->self.name);
+#endif
+
+ for (i = 0; i < nlistitems; i++) {
+ if ((p = dollar_expand(rule, list[i].atom->data, 0)) == NULL)
+ return errmsg;
+ if ((list[i].valuelist = realloc(list[i].valuelist,
+ sizeof(char *) * (list[i].nvalues + 1))) == NULL) {
+ snprintf(errmsg, sizeof(errmsg), "insufficient memory for rule enumeration");
+ free(p);
+ return errmsg;
+ }
+ if ((list[i].valuelist = get_listitems(p, &j)) == NULL) {
+ free(p);
+ return errmsg;
+ }
+ list[i].nvalues = j;
+#ifdef PMIECONF_DEBUG
+ fprintf(stderr, "debug - %s value list:", list[i].atom->name);
+ for (j = 0; j < list[i].nvalues; j++)
+ fprintf(stderr, " %s", list[i].valuelist[j]);
+ fprintf(stderr, "\n");
+#endif
+ }
+ return NULL;
+}
+
+void
+enumerate(FILE *f, rule_t *r, enumlist_t *listoffset, int wssize, char **wset)
+{
+ int i;
+
+ if (wssize < nlistitems) {
+ for (i = 0; i < listoffset->nvalues; i++) {
+ /* add current word to word set, and move down a level */
+ wset[wssize] = listoffset->valuelist[i];
+ enumerate(f, r, &list[wssize+1], wssize+1, wset);
+ }
+ }
+ else { /* have a full set, generate rule */
+#ifdef PMIECONF_DEBUG
+ for (i = 0; i < wssize; i++)
+ printf("%s=%s ", list[i].atom->name, wset[i]);
+ printf("\n");
+#endif
+ for (i = 0; i < wssize; i++) {
+ list[i].restore = list[i].atom->data;
+ list[i].atom->data = wset[i];
+ }
+
+ write_rule(f, r);
+
+ for (i = 0; i < wssize; i++)
+ list[i].atom->data = list[i].restore;
+ }
+}
+
+/* generate pmie rules for rule, returns rule string/NULL */
+static char *
+generate_rules(FILE *f, rule_t *rule)
+{
+ int i;
+
+ if (rule->self.enabled == 0)
+ return NULL;
+ if (rule->enumerate == NULL) {
+ writecount = 0;
+ write_rule(f, rule);
+ }
+ else {
+ char **workingset; /* holds current variable values set */
+
+#ifdef PMIECONF_DEBUG
+ fprintf(stderr, "debug - generating enumerated rule %s\n",
+ rule->self.name);
+#endif
+
+ /* "enumerate" attrib is a space-separated list of variables */
+ /* 1.create a list of variable info structs (name->valuelist) */
+ /* 2.recurse thru lists, when each set built, write out rule */
+
+ if ((parse_enumerate(rule)) != NULL)
+ return errmsg;
+ if ((expand_enumerate(rule)) != NULL)
+ return errmsg;
+ if ((workingset = malloc(nlistitems * sizeof(char*))) == NULL) {
+ snprintf(errmsg, sizeof(errmsg), "insufficient memory to generate rules");
+ return errmsg;
+ }
+ writecount = 0;
+ enumerate(f, rule, list, 0, workingset);
+ free(workingset);
+ for (i = 0; i < nlistitems; i++)
+ free(list[i].valuelist); /* alloc'd by dollar_expand */
+ free(list);
+ }
+
+ return NULL;
+}
+
+/* generate local configuration changes, returns rule string/NULL */
+static char *
+generate_pmiefile(FILE *f, rule_t *rule)
+{
+ atom_t *aptr;
+
+ for (aptr = &rule->self; aptr != NULL; aptr = aptr->next) {
+ if (aptr->changed == 0)
+ continue;
+ if (IS_RULE(aptr->type)) {
+ fprintf(f, "// %u %s %s = %s\n", rule->version,
+ get_aname(rule, aptr), "enabled",
+ rule->self.enabled? "yes" : "no");
+ }
+ else {
+ fprintf(f, "// %u %s %s = %s\n", rule->version, rule->self.name,
+ get_aname(rule, aptr), value_string(aptr, 1));
+ }
+ }
+ return NULL;
+}
+
+/* generate pmie rules and write to file, returns NULL/failure message */
+char *
+write_pmiefile(char *program, int autocreate)
+{
+ time_t now = time(NULL);
+ char *p, *msg = NULL;
+ char buf[MAXPATHLEN+10];
+ char *fname = get_pmiefile();
+ FILE *fp;
+ int i;
+
+ /* create full path to file if it doesn't exist */
+ if ((p = strrchr(fname, '/')) != NULL) {
+ struct stat sbuf;
+
+ *p = '\0'; /* p is the dirname of fname */
+ if (stat(fname, &sbuf) < 0) {
+ snprintf(buf, sizeof(buf), "/bin/mkdir -p %s", fname);
+ if (system(buf) < 0) {
+ snprintf(errmsg, sizeof(errmsg), "failed to create directory \"%s\"", p);
+ return errmsg;
+ }
+ }
+ else if (!S_ISDIR(sbuf.st_mode)) {
+ snprintf(errmsg, sizeof(errmsg), "\"%s\" exists and is not a directory", p);
+ return errmsg;
+ }
+ fname[strlen(fname)] = '/'; /* stitch together */
+ }
+
+ if ((fp = fopen(fname, "w")) == NULL) {
+ snprintf(errmsg, sizeof(errmsg), "cannot write file %s: %s", fname, osstrerror());
+ return errmsg;
+ }
+ else if (!gotpath) {
+ strcpy(token, fname);
+ if (realpath(token, pmiefile) == NULL) {
+ fclose(fp);
+ snprintf(errmsg, sizeof(errmsg), "failed to resolve %s realpath: %s", token, osstrerror());
+ return errmsg;
+ }
+ gotpath = 1;
+ }
+
+ fprintf(fp, "// %s %s %s\n", PMIE_FILE, PMIE_VERSION, get_rules());
+ for (i = 0; i < rulecount; ++i)
+ if ((msg = generate_pmiefile(fp, &rulelist[i])) != NULL)
+ goto imouttahere;
+ fputs("// end\n//\n", fp);
+
+ fprintf(fp, "%s// %sgenerated by %s on: %s//\n\n",
+ START_STRING, autocreate ? "Auto-" : " ", program, ctime(&now));
+ for (i = 1; i < rulecount; ++i) /* 1: start _after_ globals */
+ if ((msg = generate_rules(fp, &rulelist[i])) != NULL)
+ goto imouttahere;
+
+ /* write user-modifications area */
+ fprintf(fp, END_STRING);
+ /* finally any other local changes */
+ if (save_area != NULL)
+ fputs(save_area, fp);
+
+imouttahere:
+ fclose(fp);
+ return msg;
+}
+
+
+/*
+ * #### pmiefile manipulation routines ###
+ */
+
+/*
+ * skip leading white space and comments, return first character in next token
+ * or zero on end of file
+ */
+static int
+prime_next_lread(FILE *f)
+{
+ int c;
+
+ do {
+ c = getc(f);
+ if (c == EOF)
+ return 0;
+ if (c == '\n') {
+ linenum++;
+ if (getc(f) != '/') return 0;
+ if (getc(f) != '/') return 0;
+ }
+ } while (! isgraph(c));
+ return c;
+}
+
+/* read next input token; returns 1 ok, 0 eof, -1 error */
+static int
+read_ltoken(FILE *f)
+{
+ int c;
+ int n = 0;
+
+ switch (c = prime_next_lread(f)) {
+ case 0: /* EOF */
+ return 0;
+ case '"': /* scan string */
+ c = getc(f);
+ while (c != '"') {
+ if (c == '\\')
+ c = getc(f);
+ if (c == EOF || n == TOKEN_LENGTH) {
+ token[n] = '\0';
+ parse_error("end-of-string", token);
+ return -1;
+ }
+ if (c == '\n') {
+ token[n] = '\0';
+ parse_error("end-of-string", "end-of-line");
+ return -1;
+ }
+ token[n++] = c;
+ c = getc(f);
+ }
+ break;
+ case '=':
+ token[n++] = c; /* single char token */
+ break;
+ default: /* some other token */
+ while (isgraph(c)) {
+ if (c == '=') {
+ ungetc(c, f);
+ break;
+ }
+ if (n == TOKEN_LENGTH) {
+ token[n] = '\0';
+ parse_error("end-of-token", token);
+ return -1;
+ }
+ token[n++] = c;
+ c = getc(f);
+ }
+ if (c == '\n') {
+ linenum++;
+ if (strncmp(token, "end", 3) == 0) break;
+ if (getc(f) != '/') return 0;
+ if (getc(f) != '/') return 0;
+ }
+ break;
+ }
+
+ token[n] = '\0';
+ return 1;
+}
+
+
+/* allocates memory & appends a string to the save area */
+char *
+save_area_append(char *str)
+{
+ int size = strlen(str);
+
+ while ( (size+1) >= (sa_size-sa_mark) ) {
+ sa_size += 256; /* increase area by 256 bytes at a time */
+ if ((save_area = (char *)realloc(save_area, sa_size)) == NULL)
+ return NULL;
+ }
+ if (sa_mark == 1)
+ save_area = strcpy(save_area, str);
+ else
+ save_area = strcat(save_area, str);
+ sa_mark += size;
+ return save_area;
+}
+
+/* read and save text which is to be restored on pmiefile write */
+static char *
+read_restore(FILE *f)
+{
+ unsigned int version;
+ rule_t *rule;
+ char buf[LINE_LENGTH];
+ int saverule = 0;
+ int saveall = 0;
+
+ do {
+ if (fgets(buf, LINE_LENGTH, f) == NULL)
+ break;
+ if (!saveall) { /* not yet at start of explicit "save" position */
+ if (strcmp(buf, END_STRING) == 0)
+ saveall = 1;
+ else if (sscanf(buf, "// %u %s\n", &version, token) == 2) {
+ /*
+ * where the rule has disappeared or its version does not match
+ * the one in the pmiefile, add the rule name & version to list
+ * of rules to be deprecated (i.e. moved to the "save area")
+ */
+ /* check that we still have this rule definition */
+ if (find_rule(token, &rule) != NULL) { /* not found! */
+ snprintf(buf, sizeof(buf), "// %u %s (deprecated, %s)\n",
+ version, token, drulestring);
+ deprecate_rule(token, version, DEPRECATE_NORULE);
+ saverule = 1;
+ }
+ else if (rule->version != version) { /* not supported! */
+ snprintf(buf, sizeof(buf), "// %u %s (deprecated, %s)\n",
+ version, token, dverstring);
+ deprecate_rule(token, version, DEPRECATE_VERSION);
+ saverule = 1;
+ }
+ else
+ saverule = 0;
+ }
+ if (!saveall && saverule) {
+ if (save_area_append("// ") == NULL ||
+ save_area_append(buf) == NULL) {
+ snprintf(errmsg, sizeof(errmsg), "insufficient memory to deprecate a rule");
+ return errmsg;
+ }
+ }
+ }
+ else if (save_area_append(buf) == NULL) {
+ snprintf(errmsg, sizeof(errmsg), "insufficient memory to preserve save area");
+ return errmsg;
+ }
+ } while (!feof(f));
+ return NULL;
+}
+
+
+/* read custom values, return NULL/failure message */
+static char *
+read_locals(FILE *f)
+{
+ int sts;
+ char *rule;
+ char *attrib;
+ char *value;
+ unsigned int version;
+
+ /* general pmiefile format: version rulename attribute = value */
+
+ for (;;) {
+ if ((sts = read_ltoken(f)) < 0)
+ return errmsg;
+ else if (sts == 0) {
+ parse_error("rule identifier or \"end\" symbol", "end-of-file");
+ return errmsg;
+ }
+ if (strcmp("end", token) == 0)
+ break;
+
+ /* read the version number for this rule */
+ version = strtoul(token, &value, 10);
+ if (*value != '\0') {
+ parse_error("version number", token);
+ return errmsg;
+ }
+
+ /* read the name of the rule */
+ if ((sts = read_ltoken(f)) != 1 ||
+ (rule = alloc_string(strlen(token)+1)) == NULL) {
+ if (sts == 0)
+ parse_error("rule name", token);
+ return errmsg;
+ }
+ strcpy(rule, token);
+
+ /* read the rule attribute component */
+ if ((sts = read_ltoken(f)) != 1 ||
+ (attrib = alloc_string(strlen(token)+1)) == NULL) {
+ free(rule);
+ if (sts == 0)
+ parse_error("rule attribute", token);
+ return errmsg;
+ }
+ strcpy(attrib, token);
+
+ if ((sts = read_ltoken(f)) != 1 || strcmp("=", token) != 0) {
+ free(rule); free(attrib);
+ if (sts == 0)
+ parse_error("'=' symbol", "end-of-file");
+ return errmsg;
+ }
+
+ /* read the modified value of this attribute */
+ if ((sts = read_ltoken(f)) != 1 ||
+ (value = alloc_string(strlen(token)+1)) == NULL) {
+ free(rule); free(attrib);
+ if (sts == 0)
+ parse_error("rule attribute value", "end-of-file");
+ return errmsg;
+ }
+ strcpy(value, token);
+
+ if (merge_local(version, rule, attrib, value) != NULL) {
+ free(rule); free(attrib); free(value);
+ return errmsg;
+ }
+ free(rule); free(attrib); free(value); /* no longer need these */
+ }
+ return NULL;
+}
+
+/* validate header of rule customizations file, return NULL/failure message */
+static char *
+read_lheader(FILE *f, char **proot)
+{
+ if (read_ltoken(f) != 1 || strcmp(token, "//") || read_ltoken(f) != 1
+ || strcmp(token, PMIE_FILE) || read_ltoken(f) != 1) {
+ snprintf(errmsg, sizeof(errmsg), "%s is not a rule customization file (bad header)",
+ filename);
+ return errmsg;
+ }
+ else if (strcmp(token, PMIE_VERSION) != 0) { /* one version only */
+ snprintf(errmsg, sizeof(errmsg), "unknown version number in %s: \"%s\" (expected %s)",
+ filename, token, PMIE_VERSION);
+ return errmsg;
+ }
+ else if (read_ltoken(f) != 1) {
+ snprintf(errmsg, sizeof(errmsg), "no rules path specified in %s after version number",
+ filename);
+ return errmsg;
+ }
+ *proot = token;
+ return NULL;
+}
+
+
+/*
+ * read the pmiefile format into global data structures
+ */
+char *
+read_pmiefile(char *warning, size_t warnlen)
+{
+ char *tmp = NULL;
+ char *p, *home;
+ FILE *f;
+ char *rule_path_sep;
+
+ if ((f = fopen(get_pmiefile(), "r")) == NULL) {
+ if (oserror() == ENOENT)
+ return NULL;
+ snprintf(errmsg, sizeof(errmsg), "cannot open %s: %s",
+ get_pmiefile(), osstrerror());
+ return errmsg;
+ }
+
+ linenum = 1;
+ filename = get_pmiefile();
+ if (read_lheader(f, &tmp) != NULL) {
+ fclose(f);
+ return errmsg;
+ }
+
+ /* check that we have access to all components of the path */
+ if ((home = strdup(tmp)) == NULL) {
+ snprintf(errmsg, sizeof(errmsg), "insufficient memory for pmie file parsing");
+ return errmsg;
+ }
+#ifdef IS_MINGW
+ rule_path_sep = ";";
+#else
+ rule_path_sep = ":";
+#endif
+ p = strtok(home, rule_path_sep);
+ while (p != NULL) {
+ if (access(p, F_OK) < 0) {
+ free(home);
+ snprintf(errmsg, sizeof(errmsg), "cannot access rules path component: \"%s\"", p);
+ return errmsg;
+ }
+ p = strtok(NULL, rule_path_sep);
+ }
+ free(home);
+
+ if (strcmp(get_rules(), tmp) != 0)
+ snprintf(warning, warnlen, "warning - pmie configuration file \"%s\"\n"
+ " may not have been built using rules path:\n\t\"%s\"\n"
+ " (originally built using \"%s\")", filename, get_rules(), tmp);
+
+ tmp = NULL;
+ if (read_locals(f) != NULL || read_restore(f) != NULL)
+ tmp = errmsg;
+ fclose(f);
+ return tmp;
+}
+
+
+/* #### setup global data structures; return NULL/failure message #### */
+char *
+initialise(char *in_rules, char *in_pmie, char *warning, size_t warnlen)
+{
+ char *p;
+ char *home;
+ rule_t global;
+ char *rule_path_sep;
+
+ /* setup pointers to the configuration files */
+#ifdef IS_MINGW
+ if ((home = getenv("USERPROFILE")) == NULL) {
+ snprintf(errmsg, sizeof(errmsg), "USERPROFILE undefined in environment");
+ return errmsg;
+ }
+ if (in_pmie == NULL)
+ snprintf(pmiefile, sizeof(pmiefile), "%s\\%s", home, DEFAULT_USER_PMIE);
+ else
+ strcpy(pmiefile, in_pmie);
+ rule_path_sep = ";";
+#else
+ if (getuid() == 0) {
+ if (in_pmie == NULL)
+ snprintf(pmiefile, sizeof(pmiefile), "%s%c%s", pmGetConfig("PCP_SYSCONF_DIR"), SEP, DEFAULT_ROOT_PMIE);
+ else if (realpath(in_pmie, pmiefile) == NULL && oserror() != ENOENT) {
+ snprintf(errmsg, sizeof(errmsg), "failed to resolve realpath for %s: %s",
+ in_pmie, osstrerror());
+ return errmsg;
+ }
+ else if (oserror() != ENOENT)
+ gotpath = 1;
+ }
+ else {
+ if ((home = getenv("HOME")) == NULL) {
+ snprintf(errmsg, sizeof(errmsg), "$HOME undefined in environment");
+ return errmsg;
+ }
+ if (in_pmie == NULL)
+ snprintf(pmiefile, sizeof(pmiefile), "%s%c%s", home, SEP, DEFAULT_USER_PMIE);
+ else
+ strcpy(pmiefile, in_pmie);
+ }
+ rule_path_sep = ":";
+#endif
+
+ if (in_rules == NULL) {
+ if ((p = getenv("PMIECONF_PATH")) == NULL)
+ snprintf(rulepath, sizeof(rulepath), "%s%c%s", pmGetConfig("PCP_VAR_DIR"), SEP, DEFAULT_RULES);
+ else
+ strcpy(rulepath, p);
+ }
+ else
+ snprintf(rulepath, sizeof(rulepath), "%s", in_rules);
+
+ memset(&global, 0, sizeof(rule_t));
+ global.self.name = global_name;
+ global.self.data = global_data;
+ global.self.help = global_help;
+ global.self.global = 1;
+ if (alloc_rule(global) == NULL) { /* 1st rule holds global (fake rule) */
+ snprintf(errmsg, sizeof(errmsg), "insufficient memory for global parameters");
+ return errmsg;
+ }
+
+ if ((home = strdup(rulepath)) == NULL) {
+ snprintf(errmsg, sizeof(errmsg), "insufficient memory for rules path parsing");
+ return errmsg;
+ }
+ p = strtok(home, rule_path_sep);
+ while (p != NULL) {
+ if (read_rule_subdir(p) != NULL) {
+ free(home);
+ return errmsg;
+ }
+ p = strtok(NULL, rule_path_sep);
+ }
+ free(home);
+
+ if (read_pmiefile(warning, warnlen) != NULL)
+ return errmsg;
+ linenum = 0; /* finished all parsing */
+ return NULL;
+}
+
+
+/* iterate through the pmie status directory and find running pmies */
+char *
+lookup_processes(int *count, char ***processes)
+{
+ int fd;
+ int running = 0;
+ DIR *dirp;
+ void *ptr;
+ char proc[MAXPATHLEN+1];
+ char **proc_list = NULL;
+ size_t size;
+ pmiestats_t *stats;
+ struct dirent *dp;
+ struct stat statbuf;
+ int sep = __pmPathSeparator();
+
+ snprintf(proc, sizeof(proc), "%s%c%s",
+ pmGetConfig("PCP_TMP_DIR"), sep, PMIE_SUBDIR);
+ if ((dirp = opendir(proc)) == NULL) {
+ snprintf(errmsg, sizeof(errmsg), "cannot opendir %s: %s",
+ proc, osstrerror());
+ return NULL;
+ }
+ while ((dp = readdir(dirp)) != NULL) {
+ /* bunch of checks to find valid pmie data files... */
+ if (strcmp(dp->d_name, ".") == 0 || strcmp(dp->d_name, "..") == 0)
+ continue;
+ snprintf(proc, sizeof(proc), "%s%c%s",
+ PROC_DIR, sep, dp->d_name); /* check /proc */
+ if (access(proc, F_OK) < 0)
+ continue; /* process has exited */
+ snprintf(proc, sizeof(proc), "%s%c%s%c%s",
+ pmGetConfig("PCP_TMP_DIR"), sep, PMIE_SUBDIR, sep, dp->d_name);
+ if (stat(proc, &statbuf) < 0)
+ continue;
+ if (statbuf.st_size != sizeof(pmiestats_t))
+ continue;
+ if ((fd = open(proc, O_RDONLY)) < 0)
+ continue;
+ ptr = __pmMemoryMap(fd, statbuf.st_size, 0);
+ close(fd);
+ if (ptr == NULL)
+ continue;
+ stats = (pmiestats_t *)ptr;
+ if (strcmp(stats->config, get_pmiefile()) != 0)
+ continue;
+
+ size = (1 + running) * sizeof(char *);
+ if ((proc_list = (char **)realloc(proc_list, size)) == NULL
+ || (proc_list[running] = strdup(dp->d_name)) == NULL) {
+ snprintf(errmsg, sizeof(errmsg), "insufficient memory for process search");
+ if (proc_list) free(proc_list);
+ closedir(dirp);
+ close(fd);
+ return errmsg;
+ }
+ running++;
+ }
+ closedir(dirp);
+ *count = running;
+ *processes = proc_list;
+ return NULL;
+}
diff --git a/src/pmieconf/rules.h b/src/pmieconf/rules.h
new file mode 100644
index 0000000..2ce3378
--- /dev/null
+++ b/src/pmieconf/rules.h
@@ -0,0 +1,154 @@
+/*
+ * rules.h - rule description data structures and parsing
+ *
+ * Copyright 1998, 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.
+ */
+
+#ifndef RULES_H
+#define RULES_H
+
+/* defaults relative to $PCP_VAR_DIR */
+#ifdef IS_MINGW
+#define DEFAULT_RULES "config\\pmieconf"
+#define DEFAULT_ROOT_PMIE "pmie\\config.default"
+#else
+#define DEFAULT_RULES "config/pmieconf"
+#define DEFAULT_ROOT_PMIE "pmie/config.default"
+#endif
+
+/* default relative to $HOME */
+#ifdef IS_MINGW
+#define DEFAULT_USER_PMIE ".pcp\\pmie\\config.pmie"
+#else
+#define DEFAULT_USER_PMIE ".pcp/pmie/config.pmie"
+#endif
+
+#define TYPE_STRING 0 /* arbitrary string (default) */
+#define TYPE_DOUBLE 1 /* real number */
+#define TYPE_INTEGER 2 /* integer number */
+#define TYPE_UNSIGNED 3 /* cardinal number */
+#define TYPE_PERCENT 4 /* percentage 0..100 */
+#define TYPE_HOSTLIST 5 /* list of host names */
+#define TYPE_INSTLIST 6 /* list of metric instances */
+#define TYPE_PRINT 7 /* print action */
+#define TYPE_SHELL 8 /* shell action */
+#define TYPE_ALARM 9 /* alarm window action */
+#define TYPE_SYSLOG 10 /* syslog action */
+#define TYPE_RULE 11 /* rule definition */
+
+#define ATTRIB_HELP 0 /* help= text */
+#define ATTRIB_MODIFY 1 /* modify= text */
+#define ATTRIB_ENABLED 2 /* enabled= y/n */
+#define ATTRIB_DISPLAY 3 /* display= y/n */
+#define ATTRIB_DEFAULT 4 /* default= value */
+#define ATTRIB_VERSION 5 /* version= int */
+#define ATTRIB_PREDICATE 6 /* predicate= text */
+#define ATTRIB_ENUMERATE 7 /* enumerate= text */
+
+#define IS_ACTION(t) \
+ (t==TYPE_PRINT || t==TYPE_SHELL || t==TYPE_ALARM || t==TYPE_SYSLOG)
+#define IS_RULE(t) (t == TYPE_RULE)
+
+/* generic data block definition */
+struct atom_type {
+ char *name; /* atom identifier */
+ char *data; /* value string */
+ char *ddata; /* default value */
+ char *help; /* help text */
+ unsigned int type : 16; /* data type */
+ unsigned int modify : 1; /* advice for editor */
+ unsigned int display : 1; /* advice for editor */
+ unsigned int enabled : 1; /* switched on/off */
+ unsigned int denabled: 1; /* default on/off */
+ unsigned int changed : 1; /* has value been changed? */
+ unsigned int global : 1; /* global scope */
+ unsigned int padding : 10; /* unused */
+ struct atom_type *next;
+};
+typedef struct atom_type atom_t;
+
+typedef struct {
+ unsigned int version;
+ atom_t self;
+ char *predicate;
+ char *enumerate; /* list of hostlist/instlist params */
+} rule_t;
+
+
+extern rule_t *rulelist;
+extern unsigned int rulecount;
+extern rule_t *globals;
+
+extern char errmsg[]; /* error message buffer */
+
+
+/*
+ * routines below returning char*, on success return NULL else failure message
+ */
+
+char *initialise(char *, char *, char *, size_t); /* setup global data */
+
+char *get_pmiefile(void);
+char *get_rules(void);
+char *get_aname(rule_t *, atom_t *);
+
+void sort_rules(void);
+char *find_rule(char *, rule_t **);
+char *lookup_rules(char *, rule_t ***, unsigned int *, int);
+
+char *value_string(atom_t *, int); /* printable string form of atoms value */
+char *value_change(rule_t *, char *, char *); /* change rule parameter value */
+char *validate(int, char *, char *); /* check proposed value for named type */
+
+char *write_pmiefile(char *, int);
+char *lookup_processes(int *, char ***);
+
+int is_attribute(char *);
+char *get_attribute(char *, atom_t *);
+char *rule_defaults(rule_t *, char *);
+
+int is_overridden(rule_t *, atom_t *);
+int read_token(FILE *, char *, int, int);
+char *dollar_expand(rule_t *, char *, int);
+
+
+/* deprecated rules stuff */
+#define DEPRECATE_NORULE 0
+#define DEPRECATE_VERSION 1
+
+typedef struct {
+ unsigned int version; /* version not matching/in rules */
+ char *name; /* full name of the offending rule */
+ char *reason; /* ptr to deprecation description */
+ int type; /* reason for deprecating this rule */
+} dep_t;
+int fetch_deprecated(dep_t **list);
+
+
+/* generic symbol table definition */
+typedef struct {
+ int symbol_id;
+ char *symbol;
+} symbol_t;
+
+/* lookup keyword, returns symbol identifier or -1 if not there */
+int map_symbol(symbol_t *, int, char *);
+
+/* lookup symbol identifier, returns keyword or NULL if not there */
+char *map_identifier(symbol_t *, int, int);
+
+/* parse yes/no attribute value; returns 0 no, 1 yes, -1 error */
+int map_boolean(char *);
+
+#endif
diff --git a/src/pmieconf/web/errors b/src/pmieconf/web/errors
new file mode 100644
index 0000000..5af87ff
--- /dev/null
+++ b/src/pmieconf/web/errors
@@ -0,0 +1,60 @@
+#pmieconf-rules 1
+# --- DO NOT MODIFY THIS FILE --- see pmieconf(4)
+#
+
+rule web.errors
+ default = "$rule$"
+ predicate =
+"some_host (
+ 100 * web.allservers.errors $hosts$
+ / web.allservers.requests.total $hosts$ > $threshold$
+ &&
+ web.allservers.requests.total > $min_requests$ count/minute
+)"
+ enabled = yes
+ version = 1
+ help =
+"Across all web servers on the target host, there is some web serving
+activity (at least min_requests per minute), but more than threshold
+percent of the requests are resulting in errors being reported in the
+activity logs.";
+
+string rule
+ default = "Excessive aggregate web server errors"
+ modify = no
+ display = no;
+
+percent threshold
+ default = 20
+ help =
+"Maximum acceptable percentage of web requests that may result in
+errors.";
+
+double min_requests
+ default = 2
+ help =
+"Minimum number of requests per minute before considering the error
+rate as significant.";
+
+string action_expand
+ default = "%v%errs@%h"
+ display = no
+ modify = no;
+
+string email_expand
+ default = "host: %h web server errors: %v% of all requests"
+ display = no
+ modify = no;
+
+
+# Configuration info specific to non-PCP tools follows...
+#
+
+# for SGI Embedded Support Partner integration:
+string esp_type
+ default = "0x2000A3"
+ display = no
+ modify = no;
+
+#
+# --- DO NOT MODIFY THIS FILE --- see pmieconf(4)
diff --git a/src/pmieconf/web/high_requests b/src/pmieconf/web/high_requests
new file mode 100644
index 0000000..8e6e579
--- /dev/null
+++ b/src/pmieconf/web/high_requests
@@ -0,0 +1,50 @@
+#pmieconf-rules 1
+# --- DO NOT MODIFY THIS FILE --- see pmieconf(4)
+#
+
+rule web.high_requests
+ default = "$rule$"
+ predicate =
+"some_host (
+ web.allservers.requests.total $hosts$
+ > $threshold$ count/second
+)"
+ enabled = no
+ version = 1
+ help =
+"The aggregate request rate for all web servers on the target host is
+above threshold requests per second over the last sample interval.";
+
+string rule
+ default = "High aggregate rate of web requests"
+ display = no
+ modify = no;
+
+double threshold
+ default = 10
+ help =
+"Threshold web requests per second for all web servers on the target
+hosts.";
+
+string action_expand
+ default = "%vreqs@%h"
+ display = no
+ modify = no;
+
+string email_expand
+ default = "host: %h high web server request rate: %v/sec"
+ display = no
+ modify = no;
+
+
+# Configuration info specific to non-PCP tools follows...
+#
+
+# for SGI Embedded Support Partner integration:
+string esp_type
+ default = "0x2000A4"
+ display = no
+ modify = no;
+
+#
+# --- DO NOT MODIFY THIS FILE --- see pmieconf(4)
diff --git a/src/pmieconf/web/low_requests b/src/pmieconf/web/low_requests
new file mode 100644
index 0000000..9850e5e
--- /dev/null
+++ b/src/pmieconf/web/low_requests
@@ -0,0 +1,50 @@
+#pmieconf-rules 1
+# --- DO NOT MODIFY THIS FILE --- see pmieconf(4)
+#
+
+rule web.low_requests
+ default = "$rule$"
+ predicate =
+"some_host (
+ 60 * web.allservers.requests.total $hosts$
+ < 60 * $threshold$ count/minute
+)"
+ enabled = no
+ version = 1
+ help =
+"The aggregate request rate for all web servers on the target host is
+below threshold requests per minute over the last sample interval.";
+
+string rule
+ default = "Low aggregate rate of web requests"
+ display = no
+ modify = no;
+
+double threshold
+ default = 1
+ help =
+"Threshold minimum web requests per minute for all web servers on the
+target hosts.";
+
+string action_expand
+ default = "%vreqs@%h"
+ display = no
+ modify = no;
+
+string email_expand
+ default = "host: %h low web server request rate: %v/min"
+ display = no
+ modify = no;
+
+
+# Configuration info specific to non-PCP tools follows...
+#
+
+# for SGI Embedded Support Partner integration:
+string esp_type
+ default = "0x2000A5"
+ display = no
+ modify = no;
+
+#
+# --- DO NOT MODIFY THIS FILE --- see pmieconf(4)
diff --git a/src/pmieconf/webping/connect_errors b/src/pmieconf/webping/connect_errors
new file mode 100644
index 0000000..ff523af
--- /dev/null
+++ b/src/pmieconf/webping/connect_errors
@@ -0,0 +1,50 @@
+#pmieconf-rules 1
+# --- DO NOT MODIFY THIS FILE --- see pmieconf(4)
+#
+
+rule webping.connect_errors
+ default = "$rule$"
+ enumerate = hosts
+ predicate =
+"some_inst (
+ 60 * webping.errors.sockerr $hosts$
+ > 60 * $threshold$ count/minute
+)"
+ enabled = yes
+ version = 1
+ help =
+"The webping agent encountered more than threshold web server connection
+errors per minute in the last sample interval.";
+
+string rule
+ default = "High webping connection error rate"
+ display = no
+ modify = no;
+
+double threshold
+ default = 3
+ help =
+"Threshold webping web server connection errors per minute.";
+
+string action_expand
+ default = "%verrs@%h"
+ display = no
+ modify = no;
+
+string email_expand
+ default = "host: %h high connection errors: %verrs"
+ display = no
+ modify = no;
+
+
+# Configuration info specific to non-PCP tools follows...
+#
+
+# for SGI Embedded Support Partner integration:
+string esp_type
+ default = "0x2000A6"
+ display = no
+ modify = no;
+
+#
+# --- DO NOT MODIFY THIS FILE --- see pmieconf(4)
diff --git a/src/pmieconf/webping/html_errors b/src/pmieconf/webping/html_errors
new file mode 100644
index 0000000..58eecbb
--- /dev/null
+++ b/src/pmieconf/webping/html_errors
@@ -0,0 +1,50 @@
+#pmieconf-rules 1
+# --- DO NOT MODIFY THIS FILE --- see pmieconf(4)
+#
+
+rule webping.html_errors
+ default = "$rule$"
+ enumerate = hosts
+ predicate =
+"some_inst (
+ 60 * webping.errors.htmlerr $hosts$ $urls$
+ > 60 * $threshold$ count/minute
+)"
+ enabled = yes
+ version = 1
+ help =
+"The webping agent encountered more than threshold HTML errors per
+minute communicating with web servers in the last sample interval.";
+
+string rule
+ default = "High webping HTML error rate"
+ display = no
+ modify = no;
+
+double threshold
+ default = 3
+ help =
+"Threshold webping HTML errors per minute.";
+
+string action_expand
+ default = "%verrs[%i]@%h"
+ display = no
+ modify = no;
+
+string email_expand
+ default = "host: %h high HTML errors for %i: %verrs"
+ display = no
+ modify = no;
+
+
+# Configuration info specific to non-PCP tools follows...
+#
+
+# for SGI Embedded Support Partner integration:
+string esp_type
+ default = "0x2000A7"
+ display = no
+ modify = no;
+
+#
+# --- DO NOT MODIFY THIS FILE --- see pmieconf(4)
diff --git a/src/pmieconf/webping/http_errors b/src/pmieconf/webping/http_errors
new file mode 100644
index 0000000..6ac0aca
--- /dev/null
+++ b/src/pmieconf/webping/http_errors
@@ -0,0 +1,50 @@
+#pmieconf-rules 1
+# --- DO NOT MODIFY THIS FILE --- see pmieconf(4)
+#
+
+rule webping.http_errors
+ default = "$rule$"
+ enumerate = hosts
+ predicate =
+"some_inst (
+ 60 * webping.errors.httperr $hosts$
+ > 60 * $threshold$ count/minute
+)"
+ enabled = yes
+ version = 1
+ help =
+"The webping agent encountered more than threshold HTTP errors per
+minute communicating with web servers in the last sample interval.";
+
+string rule
+ default = "High webping HTTP error rate"
+ display = no
+ modify = no;
+
+double threshold
+ default = 3
+ help =
+"Threshold webping HTTP errors per minute.";
+
+string action_expand
+ default = "%verrs@%h"
+ display = no
+ modify = no;
+
+string email_expand
+ default = "host: %h high HTTP errors: %verrs"
+ display = no
+ modify = no;
+
+
+# Configuration info specific to non-PCP tools follows...
+#
+
+# for SGI Embedded Support Partner integration:
+string esp_type
+ default = "0x2000A8"
+ display = no
+ modify = no;
+
+#
+# --- DO NOT MODIFY THIS FILE --- see pmieconf(4)
diff --git a/src/pmieconf/webping/no_response b/src/pmieconf/webping/no_response
new file mode 100644
index 0000000..e5624dd
--- /dev/null
+++ b/src/pmieconf/webping/no_response
@@ -0,0 +1,56 @@
+#pmieconf-rules 1
+# --- DO NOT MODIFY THIS FILE --- see pmieconf(4)
+#
+
+rule webping.no_response
+ default = "$rule$"
+ enumerate = hosts
+ predicate =
+"some_inst (
+ webping.perurl.kbytes $hosts$ $urls$ @1 -
+ webping.perurl.kbytes $hosts$ $urls$ @0 > 0
+ && webping.perurl.kbytes $hosts$ $urls$ @0 == 0
+)"
+ enabled = yes
+ version = 1
+ help =
+"The retrieval of at least one URL by the webping agent failed on the
+most recent attempt, but succeeded on the previous attempt.";
+
+string rule
+ default = "No response to webping request"
+ display = no
+ modify = no;
+
+string action_expand
+ default = "%i@%h"
+ display = no
+ modify = no;
+
+string email_expand
+ default = "host: %h no web server response for %i"
+ display = no
+ modify = no;
+
+
+# Configuration info specific to non-PCP tools follows...
+#
+
+# for SGI Embedded Support Partner integration:
+string esp_type
+ default = "0x2000AB"
+ display = no
+ modify = no;
+
+# for HP OpenView integration:
+string ov_severity
+ display = no
+ default = "Critical";
+
+# for CA/Unicenter TNG integration:
+string tngfw_color
+ display = no
+ default = "Red";
+
+#
+# --- DO NOT MODIFY THIS FILE --- see pmieconf(4)
diff --git a/src/pmieconf/webping/other_errors b/src/pmieconf/webping/other_errors
new file mode 100644
index 0000000..29d56b2
--- /dev/null
+++ b/src/pmieconf/webping/other_errors
@@ -0,0 +1,51 @@
+#pmieconf-rules 1
+# --- DO NOT MODIFY THIS FILE --- see pmieconf(4)
+#
+
+rule webping.other_errors
+ default = "$rule$"
+ enumerate = hosts
+ predicate =
+"some_inst (
+ 60 * webping.errors.othererr $hosts$
+ > 60 * $threshold$ count/minute
+)"
+ enabled = yes
+ version = 1
+ help =
+"The webping agent encountered more than threshold errors (cause
+unknown) per minute communicating with web servers in the last sample
+interval.";
+
+string rule
+ default = "High webping error rate, cause unknown"
+ display = no
+ modify = no;
+
+double threshold
+ default = 3
+ help =
+"Threshold webping errors of unknown cause, per minute.";
+
+string action_expand
+ default = "%verrs@%h"
+ display = no
+ modify = no;
+
+string email_expand
+ default = "host: %h high errors of unknown cause: %verrs"
+ display = no
+ modify = no;
+
+
+# Configuration info specific to non-PCP tools follows...
+#
+
+# for SGI Embedded Support Partner integration:
+string esp_type
+ default = "0x2000A9"
+ display = no
+ modify = no;
+
+#
+# --- DO NOT MODIFY THIS FILE --- see pmieconf(4)
diff --git a/src/pmieconf/webping/slow_response b/src/pmieconf/webping/slow_response
new file mode 100644
index 0000000..40766ee
--- /dev/null
+++ b/src/pmieconf/webping/slow_response
@@ -0,0 +1,56 @@
+#pmieconf-rules 1
+# --- DO NOT MODIFY THIS FILE --- see pmieconf(4)
+#
+
+rule webping.slow_response
+ default = "$rule$"
+ enumerate = hosts
+ predicate =
+"some_inst (
+ webping.perurl.time.total $hosts$ $urls$
+ > $threshold$ seconds
+)"
+ enabled = yes
+ version = 1
+ help =
+"The retrieval of at least one URL by the webping agent took longer than
+threshold seconds at the most recent attempt.";
+
+string rule
+ default = "Slow response to webping request"
+ display = no
+ modify = no;
+
+double threshold
+ default = 8
+ help=
+"Threshold on the maximum acceptable web server response time, in
+seconds.
+The possible range of values for the threshold may be very large,
+e.g. 0.5 to 20 seconds or more, and the most appropriate value depends
+on the response time to fetch the URLs (the transmission time is
+included), the network latency between the webping agent and the web
+servers, and the loading of the web servers.";
+
+string action_expand
+ default = "%vsecs[%i]@%h"
+ display = no
+ modify = no;
+
+string email_expand
+ default = "host: %h web server response for %i: %vsecs"
+ display = no
+ modify = no;
+
+
+# Configuration info specific to non-PCP tools follows...
+#
+
+# for SGI Embedded Support Partner integration:
+string esp_type
+ default = "0x2000AA"
+ display = no
+ modify = no;
+
+#
+# --- DO NOT MODIFY THIS FILE --- see pmieconf(4)
diff --git a/src/pmieconf/xtractnames b/src/pmieconf/xtractnames
new file mode 100755
index 0000000..33aab7b
--- /dev/null
+++ b/src/pmieconf/xtractnames
@@ -0,0 +1,70 @@
+#!/bin/sh
+#
+# 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
+
+
+# Extract metric names (or likely looking ones) from pmieconf files
+# In comparison to the tools/xtractnames version, this one is a little
+# more context sensitive, and not PMNS-driven
+#
+
+for d in ../include ../../include /etc
+do
+ if [ -f $d/pcp.conf ]
+ then
+ . $d/pcp.conf
+ break
+ fi
+done
+
+tmp=`mktemp -d /tmp/pcp.XXXXXXXXX` || exit 1
+trap "rm -rf $tmp; exit" 0 1 2 3 15
+
+_usage()
+{
+ echo "Usage: xtractnames [file ...]"
+}
+
+while getopts "?" c
+do
+ case $c
+ in
+ ?)
+ _usage
+ exit 1
+ ;;
+ esac
+done
+shift `expr $OPTIND - 1`
+
+if [ $# -eq 0 ]
+then
+ # no args, from stdin
+ cat >$tmp/in
+ set -- $tmp/in
+fi
+
+for file
+do
+ awk <$file '
+/^[ ]*predicate[ ]*=/ { want = 1 }
+/^[ ]*enabled[ ]*=/ { exit }
+want == 1 { print }' \
+ | tr -cs 'a-zA-Z0-9_.' '[\012*]' \
+ | grep '[a-zA-Z].*\.[a-zA-Z0-9_]'
+done \
+| $PCP_SORT_PROG -u
+
+exit