diff options
author | Igor Pashev <pashev.igor@gmail.com> | 2014-10-26 12:33:50 +0400 |
---|---|---|
committer | Igor Pashev <pashev.igor@gmail.com> | 2014-10-26 12:33:50 +0400 |
commit | 47e6e7c84f008a53061e661f31ae96629bc694ef (patch) | |
tree | 648a07f3b5b9d67ce19b0fd72e8caa1175c98f1a /src/pmieconf | |
download | pcp-debian.tar.gz |
Debian 3.9.10debian/3.9.10debian
Diffstat (limited to 'src/pmieconf')
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 |