summaryrefslogtreecommitdiff
path: root/src/pmie
diff options
context:
space:
mode:
Diffstat (limited to 'src/pmie')
-rw-r--r--src/pmie/GNUmakefile68
-rw-r--r--src/pmie/config.default20
-rw-r--r--src/pmie/control46
-rw-r--r--src/pmie/crontab.in8
-rw-r--r--src/pmie/examples/GNUmakefile91
-rw-r--r--src/pmie/examples/README27
-rw-r--r--src/pmie/examples/cpu.009
-rw-r--r--src/pmie/examples/cpu.0115
-rw-r--r--src/pmie/examples/cpu.0210
-rw-r--r--src/pmie/examples/cpu.head12
-rw-r--r--src/pmie/examples/disk.0028
-rw-r--r--src/pmie/examples/disk.1023
-rw-r--r--src/pmie/examples/disk.2013
-rw-r--r--src/pmie/examples/disk.head11
-rw-r--r--src/pmie/examples/environ.0018
-rw-r--r--src/pmie/examples/environ.head17
-rw-r--r--src/pmie/examples/filesys.0014
-rw-r--r--src/pmie/examples/filesys.1014
-rw-r--r--src/pmie/examples/filesys.2014
-rw-r--r--src/pmie/examples/filesys.head10
-rw-r--r--src/pmie/examples/network.0010
-rw-r--r--src/pmie/examples/network.head15
-rw-r--r--src/pmie/examples/ras.0011
-rw-r--r--src/pmie/examples/ras.head5
-rw-r--r--src/pmie/examples/swap.0023
-rw-r--r--src/pmie/examples/swap.head13
-rw-r--r--src/pmie/examples/uag.005
-rw-r--r--src/pmie/examples/uag.014
-rw-r--r--src/pmie/examples/uag.028
-rw-r--r--src/pmie/examples/uag.037
-rw-r--r--src/pmie/examples/uag.0452
-rw-r--r--src/pmie/examples/uag.1032
-rw-r--r--src/pmie/examples/uag.117
-rw-r--r--src/pmie/examples/uag.1212
-rw-r--r--src/pmie/examples/uag.1333
-rw-r--r--src/pmie/examples/uag.207
-rw-r--r--src/pmie/examples/uag.219
-rw-r--r--src/pmie/examples/uag.3017
-rw-r--r--src/pmie/examples/uag.head3
-rw-r--r--src/pmie/examples/upm.006
-rw-r--r--src/pmie/examples/upm.014
-rw-r--r--src/pmie/examples/upm.0219
-rw-r--r--src/pmie/examples/upm.038
-rw-r--r--src/pmie/examples/upm.0410
-rw-r--r--src/pmie/examples/upm.059
-rw-r--r--src/pmie/examples/upm.0610
-rw-r--r--src/pmie/examples/upm.078
-rw-r--r--src/pmie/examples/upm.0813
-rw-r--r--src/pmie/examples/upm.0911
-rw-r--r--src/pmie/examples/upm.1011
-rw-r--r--src/pmie/examples/upm.head5
-rw-r--r--src/pmie/examples/webreport.008
-rw-r--r--src/pmie/examples/webreport.018
-rw-r--r--src/pmie/examples/webreport.head11
-rw-r--r--src/pmie/pmie.service.in13
-rwxr-xr-xsrc/pmie/pmie2col.sh133
-rw-r--r--src/pmie/pmie_check.sh691
-rw-r--r--src/pmie/pmie_daily.sh540
-rw-r--r--src/pmie/rc_pmie273
-rw-r--r--src/pmie/src/GNUmakefile79
-rw-r--r--src/pmie/src/README.DEBUG19
-rw-r--r--src/pmie/src/act.sk376
-rw-r--r--src/pmie/src/aggregate.sk176
-rw-r--r--src/pmie/src/andor.c457
-rw-r--r--src/pmie/src/andor.h34
-rw-r--r--src/pmie/src/binary.sk179
-rw-r--r--src/pmie/src/dstruct.c1307
-rw-r--r--src/pmie/src/dstruct.h461
-rw-r--r--src/pmie/src/eval.c808
-rw-r--r--src/pmie/src/eval.h46
-rw-r--r--src/pmie/src/fetch.sk460
-rw-r--r--src/pmie/src/fun.h124
-rw-r--r--src/pmie/src/grammar.y666
-rw-r--r--src/pmie/src/hdr.sk37
-rw-r--r--src/pmie/src/lexicon.c925
-rw-r--r--src/pmie/src/lexicon.h71
-rw-r--r--src/pmie/src/logger.h83
-rw-r--r--src/pmie/src/match_inst.c135
-rw-r--r--src/pmie/src/merge.sk103
-rwxr-xr-xsrc/pmie/src/meta299
-rw-r--r--src/pmie/src/misc.sk176
-rw-r--r--src/pmie/src/pmie.c942
-rw-r--r--src/pmie/src/pragmatics.c1226
-rw-r--r--src/pmie/src/pragmatics.h103
-rw-r--r--src/pmie/src/show.c1104
-rw-r--r--src/pmie/src/show.h32
-rw-r--r--src/pmie/src/stats.h38
-rw-r--r--src/pmie/src/stomp.c395
-rw-r--r--src/pmie/src/stomp.h26
-rw-r--r--src/pmie/src/symbol.c264
-rw-r--r--src/pmie/src/symbol.h93
-rw-r--r--src/pmie/src/syntax.c781
-rw-r--r--src/pmie/src/syntax.h97
-rw-r--r--src/pmie/src/systemlog.c198
-rw-r--r--src/pmie/src/systemlog.h1
-rw-r--r--src/pmie/src/unary.sk91
-rw-r--r--src/pmie/stomp11
97 files changed, 14955 insertions, 0 deletions
diff --git a/src/pmie/GNUmakefile b/src/pmie/GNUmakefile
new file mode 100644
index 0000000..48c46ec
--- /dev/null
+++ b/src/pmie/GNUmakefile
@@ -0,0 +1,68 @@
+#
+# Copyright (c) 2013-2014 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
+
+SUBDIRS = src examples
+OTHERS = control stomp rc_pmie pmie2col.sh pmie_check.sh pmie_daily.sh
+
+LSRCFILES = $(OTHERS) crontab.in
+LDIRT = crontab pmie.service
+
+ifeq ($(TARGET_OS),linux)
+CRONTAB_USER = $(PCP_USER)
+CRONTAB_PATH = $(PCP_ETC_DIR)/cron.d/pcp-pmie
+else
+CRONTAB_USER =
+CRONTAB_PATH = $(PCP_SYSCONF_DIR)/pmie/crontab
+endif
+
+default:: crontab pmie.service
+
+default:: $(SUBDIRS)
+ $(SUBDIRS_MAKERULE)
+
+install:: $(SUBDIRS)
+ $(SUBDIRS_MAKERULE)
+
+install:: default
+ $(INSTALL) -m 775 -o $(PCP_USER) -g $(PCP_GROUP) -d $(PCP_SYSCONF_DIR)/pmie
+ $(INSTALL) -m 664 -o $(PCP_USER) -g $(PCP_GROUP) control $(PCP_PMIECONTROL_PATH)
+ $(INSTALL) -m 755 pmie_check.sh $(PCP_BINADM_DIR)/pmie_check$(SHELLSUFFIX)
+ $(INSTALL) -m 755 pmie_daily.sh $(PCP_BINADM_DIR)/pmie_daily$(SHELLSUFFIX)
+ $(INSTALL) -m 755 pmie2col.sh $(PCP_BIN_DIR)/pmie2col$(SHELLSUFFIX)
+ $(INSTALL) -m 755 rc_pmie $(PCP_RC_DIR)/pmie
+ifeq ($(ENABLE_SYSTEMD),true)
+ $(INSTALL) -m 644 pmie.service $(PCP_SYSTEMDUNIT_DIR)/pmie.service
+endif
+ $(INSTALL) -m 775 -o $(PCP_USER) -g $(PCP_GROUP) -d $(PCP_LOG_DIR)/pmie
+ $(INSTALL) -m 775 -o $(PCP_USER) -g $(PCP_GROUP) -d $(PCP_TMP_DIR)/pmie
+ifeq ($(TARGET_OS),linux)
+ $(INSTALL) -m 755 -d `dirname $(CRONTAB_PATH)`
+endif
+ $(INSTALL) -m 644 crontab $(CRONTAB_PATH)
+
+include $(BUILDRULES)
+
+default_pcp : default
+
+install_pcp : install
+
+pmie.service : pmie.service.in
+ $(SED) -e 's;@path@;'$(PCP_RC_DIR)';' $< > $@
+
+crontab: crontab.in
+ $(SED) -e 's;@user@;'$(CRONTAB_USER)';' -e 's;@path@;'$(PCP_BINADM_DIR)';g' $< > $@
diff --git a/src/pmie/config.default b/src/pmie/config.default
new file mode 100644
index 0000000..2d92240
--- /dev/null
+++ b/src/pmie/config.default
@@ -0,0 +1,20 @@
+// pmieconf-pmie 1 /var/pcp/config/pmieconf
+// end
+//
+// Note: only make changes to this file after END GENERATED SECTION
+//
+// --- START GENERATED SECTION (do not change this section) ---
+// generated by pmieconf on: Wed Jul 12 10:10:20 2000
+//
+
+// 1 cpu.load_average
+delta = 2 min;
+cpu.load_average =
+some_host (
+ ( kernel.all.load #'1 minute' )
+ > hinv.ncpu * 3 &&
+ ( kernel.all.load #'1 minute' )
+ > 4
+) -> syslog 10 min "High 1-minute load average" " %vload@%h";
+
+// --- END GENERATED SECTION (changes below will be preserved) ---
diff --git a/src/pmie/control b/src/pmie/control
new file mode 100644
index 0000000..2dcfd1f
--- /dev/null
+++ b/src/pmie/control
@@ -0,0 +1,46 @@
+#
+# PCP inference engine configuration/control
+#
+# This file is used by a number of the PCP inference engine administrative
+# tools to perform maintenance on the pmie instances running on the local host.
+#
+# This file contains one line per host to be monitored, fields are
+# Host name of host to be monitored
+# S(ocks)? should this pmie be launched with pmsocks? y or n
+# Log File full pathname to file where pmie activity log is to be
+# maintained ... note all scripts "cd" the directory housing
+# this file as a first step
+# Arguments optional additional arguments to pmie
+#
+
+
+# === VARIABLE ASSIGNMENTS ===
+#
+$version=1.0
+
+# if pmsocks is being used, edit the IP address for $SOCKS_SERVER and
+# uncomment the next line
+#$SOCKS_SERVER=123.456.789.123; export SOCKS_SERVER
+
+# if remote pmie instances are run over a WAN with potentially long delays,
+# adjust the following and uncomment
+#$PMCD_CONNECT_TIMEOUT=20; export PMCD_CONNECT_TIMEOUT
+#$PMCD_REQUEST_TIMEOUT=15; export PMCD_REQUEST_TIMEOUT
+
+
+# === PMIE CONTROL SPECIFICATIONS ===
+#
+# Note: - if multiple pmie instances for the same host, then they MUST use
+# different log files;
+# - any occurence of LOCALHOSTNAME will be replaced by local hostname;
+# - pmie's configuration file search path is "./:$PCP_SYSCONF_DIR/pmie/",
+# and the working directory ('.') is the dirname of the Log File.
+#
+#Host S? Log File Arguments
+LOCALHOSTNAME n PCP_LOG_DIR/pmie/LOCALHOSTNAME/pmie.log -c config.default
+
+# remote host
+#remote n PCP_LOG_DIR/pmie/remote/pmie.log -c config.remote
+
+# thru the firewall via socks
+#distant y PCP_LOG_DIR/pmie/distant/pmie.log -c config.distant
diff --git a/src/pmie/crontab.in b/src/pmie/crontab.in
new file mode 100644
index 0000000..90d96af
--- /dev/null
+++ b/src/pmie/crontab.in
@@ -0,0 +1,8 @@
+#
+# Performance Co-Pilot crontab entries for a monitored site
+# with one or more pmie instances running
+#
+# daily processing of pmie logs (with compression enabled)
+08 0 * * * @user@ @path@/pmie_daily -X xz -x 3
+# every 30 minutes, check pmie instances are running
+28,58 * * * * @user@ @path@/pmie_check -C
diff --git a/src/pmie/examples/GNUmakefile b/src/pmie/examples/GNUmakefile
new file mode 100644
index 0000000..f4db24f
--- /dev/null
+++ b/src/pmie/examples/GNUmakefile
@@ -0,0 +1,91 @@
+#
+# Copyright (c) 2000-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.
+#
+
+TOPDIR = ../../..
+include $(TOPDIR)/src/include/builddefs
+
+UAG_SOURCE = uag.head $(sort $(wildcard uag.[0-9][0-9]))
+UPM_SOURCE = upm.head $(sort $(wildcard upm.[0-9][0-9]))
+CPU_SOURCE = cpu.head $(sort $(wildcard cpu.[0-9][0-9]))
+DISK_SOURCE = disk.head $(sort $(wildcard disk.[0-9][0-9]))
+FILESYS_SOURCE = filesys.head $(sort $(wildcard filesys.[0-9][0-9]))
+RAS_SOURCE = ras.head $(sort $(wildcard ras.[0-9][0-9]))
+SWAP_SOURCE = swap.head $(sort $(wildcard swap.[0-9][0-9]))
+NETWORK_SOURCE = network.head $(sort $(wildcard network.[0-9][0-9]))
+ENVIRON_SOURCE = environ.head $(sort $(wildcard environ.[0-9][0-9]))
+WEBREPORT_SOURCE= webreport.head $(sort $(wildcard webreport.[0-9][0-9]))
+
+TARGETS = UAG UPM CPU DISK FILESYS RAS SWAP NETWORK ENVIRON \
+ WEBREPORT
+
+LDIRT = $(TARGETS)
+
+LSRCFILES = README $(UAG_SOURCE) $(UPM_SOURCE) $(CPU_SOURCE) \
+ $(DISK_SOURCE) $(FILESYS_SOURCE) $(RAS_SOURCE) $(SWAP_SOURCE) \
+ $(NETWORK_SOURCE) $(ENVIRON_SOURCE) $(WEBREPORT_SOURCE)
+EX_DIR = $(PCP_SHARE_DIR)/examples/pmie
+
+default: $(TARGETS) README
+
+install: default
+ $(INSTALL) -m 755 -d $(EX_DIR)
+ $(INSTALL) -m 644 $(TARGETS) README $(EX_DIR)
+
+UAG: $(UAG_SOURCE)
+ rm -f UAG
+ for file in $(UAG_SOURCE); do cat $$file >>UAG; echo >>UAG; done
+
+UPM: $(UPM_SOURCE)
+ rm -f UPM
+ for file in $(UPM_SOURCE); do cat $$file >>UPM; echo >>UPM; done
+
+CPU: $(CPU_SOURCE)
+ rm -f CPU
+ for file in $(CPU_SOURCE); do cat $$file >>CPU; echo >>CPU; done
+
+DISK: $(DISK_SOURCE)
+ rm -f DISK DISK.in
+ for file in $(DISK_SOURCE); do cat $$file >>DISK.in; echo >>DISK.in; done
+ sed -e "s@/usr/pcp/bin/pmpost@$(PCP_BINADM_DIR)/pmpost@" <DISK.in >DISK
+ rm -f DISK.in
+
+FILESYS: $(FILESYS_SOURCE)
+ rm -f FILESYS
+ for file in $(FILESYS_SOURCE); do cat $$file >>FILESYS; echo >>FILESYS; done
+
+RAS: $(RAS_SOURCE)
+ rm -f RAS
+ for file in $(RAS_SOURCE); do cat $$file >>RAS; echo >>RAS; done
+
+SWAP: $(SWAP_SOURCE)
+ rm -f SWAP
+ for file in $(SWAP_SOURCE); do cat $$file >>SWAP; echo >>SWAP; done
+
+NETWORK: $(NETWORK_SOURCE)
+ rm -f NETWORK
+ for file in $(NETWORK_SOURCE); do cat $$file >>NETWORK; echo >>NETWORK; done
+
+ENVIRON: $(ENVIRON_SOURCE)
+ rm -f ENVIRON
+ for file in $(ENVIRON_SOURCE); do cat $$file >>ENVIRON; echo >>ENVIRON; done
+
+WEBREPORT: $(WEBREPORT_SOURCE)
+ rm -f WEBREPORT
+ for file in $(WEBREPORT_SOURCE); do cat $$file >>WEBREPORT; echo >>WEBREPORT; done
+
+include $(BUILDRULES)
+
+default_pcp : default
+
+install_pcp : install
diff --git a/src/pmie/examples/README b/src/pmie/examples/README
new file mode 100644
index 0000000..1bdf4a6
--- /dev/null
+++ b/src/pmie/examples/README
@@ -0,0 +1,27 @@
+Example Expressions and Rules for pmie(1)
+
+The files in this directory contain a number of sample rules
+for the Performance Co-Pilot inference engine pmie(1).
+
+In some cases the rules could be used directly. In others the
+rules would require customization (host names, thresholds, rule
+evaluation frequency, choose a suitable alarm/action, etc) before
+they could be used at a particular site.
+
+Each file contains a set of related rules as follows:
+
+CPU general CPU utilization and saturation monitoring
+
+DISK general disk utilization and saturation monitoring
+
+FILESYS general file system monitoring
+
+RAS simple reliability and availability monitoring
+ (see also the shping PMDA, pmdashping(1))
+
+UAG examples from the Performance Co-Pilot User's and
+ Administrator's Guide
+
+UPM examples from the Performance Co-Pilot Programmer's
+ Guide
+
diff --git a/src/pmie/examples/cpu.00 b/src/pmie/examples/cpu.00
new file mode 100644
index 0000000..403dfa4
--- /dev/null
+++ b/src/pmie/examples/cpu.00
@@ -0,0 +1,9 @@
+//
+// Unusual usr-sys split when some CPU is more than 20% in usr mode
+// and sys mode is at least 1.5 times usr mode
+//
+cpu_usr_sys =
+ some_inst (
+ $percpu.cpu.sys > $percpu.cpu.user * 1.5 && $percpu.cpu.user > 0.2
+ )
+ -> alarm "Unusual sys time: " "%i ";
diff --git a/src/pmie/examples/cpu.01 b/src/pmie/examples/cpu.01
new file mode 100644
index 0000000..799aa99
--- /dev/null
+++ b/src/pmie/examples/cpu.01
@@ -0,0 +1,15 @@
+//
+// Over all CPUs, syscall_rate > 1000 * no_of_cpus
+//
+cpu_syscall =
+ $all.syscall > 1000 count/sec * hinv.ncpu
+ -> print "high aggregate syscalls: %v";
+
+// Sustained high syscall rate on a single CPU
+//
+delta = 30 sec;
+percpu_syscall =
+ some_inst (
+ $percpu.syscall > 2000 count/sec
+ )
+ -> syslog "Sustained syscalls per second? " "[%i] %v ";
diff --git a/src/pmie/examples/cpu.02 b/src/pmie/examples/cpu.02
new file mode 100644
index 0000000..d5b1fbb
--- /dev/null
+++ b/src/pmie/examples/cpu.02
@@ -0,0 +1,10 @@
+//
+// the 1 minute load average exceeds 5 * number of CPUs on any host
+//
+
+hosts = ":gonzo :moomba"; // change as required
+delta = 1 minute; // no need to evaluate more often than this
+high_load =
+ some_host (
+ $all.load $hosts #'1 minute' > 5 * hinv.ncpu $hosts
+ ) -> alarm "High Load Average? " "%h: %v ";
diff --git a/src/pmie/examples/cpu.head b/src/pmie/examples/cpu.head
new file mode 100644
index 0000000..52a493f
--- /dev/null
+++ b/src/pmie/examples/cpu.head
@@ -0,0 +1,12 @@
+//
+// Some Common Performance Monitoring Scenarios
+//
+// The CPU Group
+//
+
+delta = 2 sec; // more often for demonstration purposes
+
+// common prefixes
+//
+percpu = "kernel.percpu";
+all = "kernel.all";
diff --git a/src/pmie/examples/disk.00 b/src/pmie/examples/disk.00
new file mode 100644
index 0000000..a69a8d7
--- /dev/null
+++ b/src/pmie/examples/disk.00
@@ -0,0 +1,28 @@
+//
+// Any disk performing more than 40 I/Os per second, sustained over
+// at least 30 seconds is probably busy
+//
+delta = 30 seconds;
+disk_busy =
+ some_inst (
+ $disk.dev.total > 40 count/sec
+ )
+ -> shell 15 mins "Mail -s 'Heavy sustained disk traffic' sysadm </dev/null";
+
+// Try and catch bursts of activity ... more than 60 I/Os per second
+// for at least 25% of 8 consecutive 3 second samples
+//
+delta = 3 sec;
+disk_burst =
+ some_inst (
+ 25%_sample (
+ $disk.dev.total @0..7 > 60 count/sec
+ )
+ )
+ -> alarm 5 mins "Disk Burst? " "%i ";
+
+// any SCSI disk controller performing more than 3 Mbytes per sec is busy
+//
+some_inst $disk.ctl.blktotal * 0.5 > 3 Mbyte/sec
+ -> alarm "Busy Disk Controller: " "%i ";
+
diff --git a/src/pmie/examples/disk.10 b/src/pmie/examples/disk.10
new file mode 100644
index 0000000..9cbf50d
--- /dev/null
+++ b/src/pmie/examples/disk.10
@@ -0,0 +1,23 @@
+//
+// A subset of the disks on a particular host are either busy
+// (more than 30 I/Os per second averaged over these disks) or one
+// disk is busy (more than 50 I/Os per second) with write-dominated
+// (more than 75%) activity
+
+delta = 10 sec;
+
+myhost = "moomba"; // the host of interest
+mydisks = "#dks1d1 #dks1d2 #dks3d2"; // the disks of interest on this host
+
+metric = "disk.dev";
+
+disk_group_busy =
+ (
+ avg_inst ( $metric.total :$myhost $mydisks ) > 10 count/sec ||
+ some_inst (
+ $metric.total :$myhost $mydisks > 50 count/sec &&
+ $metric.write :$myhost $mydisks >
+ 3 * $metric.write :$myhost $mydisks
+ )
+ )
+ -> alarm "Busy disks: $mydisks on host: $myhost)";
diff --git a/src/pmie/examples/disk.20 b/src/pmie/examples/disk.20
new file mode 100644
index 0000000..ec086e5
--- /dev/null
+++ b/src/pmie/examples/disk.20
@@ -0,0 +1,13 @@
+//
+// Assume the / and /usr file systems are on different partitions
+// of the same disk (/dev/dsk0d1 in the example below).
+// Add an entry to the file $PCP_LOG_DIR/NOTICES when this disk is
+// busy and either of the file systems is more than 90% full.
+//
+// Suggestion from: Steve Daniels (steve@houdini.denver.sgi.com)
+
+delta = 60;
+
+( filesys.full #'/dev/root' > 90 || filesys.full #'/dev/usr' > 90 )
+&& disk.dev.total #'dks0d1' > 40 count/sec
+ -> shell 15min "/usr/pcp/bin/pmpost 'dks0d1 busy when / or /usr nearly full'";
diff --git a/src/pmie/examples/disk.head b/src/pmie/examples/disk.head
new file mode 100644
index 0000000..20f133e
--- /dev/null
+++ b/src/pmie/examples/disk.head
@@ -0,0 +1,11 @@
+//
+// Some Common Performance Monitoring Scenarios
+//
+// The Disk Group
+//
+
+delta = 15 sec; // often enough for disks?
+
+// common prefixes
+//
+disk = "disk";
diff --git a/src/pmie/examples/environ.00 b/src/pmie/examples/environ.00
new file mode 100644
index 0000000..85e5597
--- /dev/null
+++ b/src/pmie/examples/environ.00
@@ -0,0 +1,18 @@
+//
+// Absolute temperature ceiling.
+//
+// Rules donated by Kevin Wang at Silicon Graphics
+//
+
+some_host ( environ.temp $HOSTS > 33 )
+-> print 10 min "absolute temperature alarm! " "%h: %v degrees ";
+
+//
+// Watch the machine room temperature. If it rises more than 2 degrees
+// every $delta, danger!
+// This is different from the absolute rule above ... this one
+// gives early warning of sustained temperature increases.
+//
+some_host (
+ environ.temp $HOSTS @0 - environ.temp $HOSTS @1 > 2
+) -> print "temperature rise alarm: " "%h: %v degree rise in $DELTA_STR ";
diff --git a/src/pmie/examples/environ.head b/src/pmie/examples/environ.head
new file mode 100644
index 0000000..78936b9
--- /dev/null
+++ b/src/pmie/examples/environ.head
@@ -0,0 +1,17 @@
+//
+// Some Common Performance Monitoring Scenarios
+//
+// The Environ Group
+//
+// Note: need environ PMDA installed on a Challenge L or XL for
+// required metrics to be available
+
+// replace with your hosts
+HOSTS = ":localhost :foo";
+
+// replace this with your e-mail address
+MINDER = "root@localhost";
+
+// 1 minute rulesets in this group
+delta = 1 min; // numbers are diff than strings
+DELTA_STR = "1 minute"; // strings are diff than numbers
diff --git a/src/pmie/examples/filesys.00 b/src/pmie/examples/filesys.00
new file mode 100644
index 0000000..937e123
--- /dev/null
+++ b/src/pmie/examples/filesys.00
@@ -0,0 +1,14 @@
+//
+// Either the /tmp or the /usr filesystem being
+// more than 95% full
+//
+
+delta = 5 mins; // often enough for file system fullness?
+
+tmp_full =
+ $fsys.free #'/dev/root' / $fsys.capacity #'/dev/root' < 0.05
+ -> syslog "/dev/root filesystem (almost) full";
+
+usr_full =
+ $fsys.free #'/dev/usr' / $fsys.capacity #'/dev/usr' < 0.05
+ -> syslog "/dev/usr filesystem (almost) full";
diff --git a/src/pmie/examples/filesys.10 b/src/pmie/examples/filesys.10
new file mode 100644
index 0000000..2cf9488
--- /dev/null
+++ b/src/pmie/examples/filesys.10
@@ -0,0 +1,14 @@
+//
+// Some read activity through the buffer cache and the cache read
+// hit ratio is less than 80%
+// (lots of file system reads causing physical I/O)
+//
+
+delta = 1 min; // check every minute
+
+blkio = "kernel.all.io";
+poor_read_hits =
+ (($blkio.lread - $blkio.bread) / $blkio.lread) < 0.8 && $blkio.lread > 100
+ -> alarm 20 min "poor buffer cache read hit ratio (%v)";
+ // Note: %v in alarm string is bound to the left most
+ // expression in the predicate
diff --git a/src/pmie/examples/filesys.20 b/src/pmie/examples/filesys.20
new file mode 100644
index 0000000..856d8ed
--- /dev/null
+++ b/src/pmie/examples/filesys.20
@@ -0,0 +1,14 @@
+//
+// at least $threshold full and at the current rate of growth will fill
+// the file system in less than $lead_time
+// ie. used + $lead_time * growth-rate > capacity
+
+delta = 1 min; // check every minute
+threshold = 40; // must be at least this full now (percentage)
+lead_time = "15min"; // lead time before the filesystem will be full
+
+some_inst (
+ 100 * filesys.used / filesys.capacity > $threshold &&
+ filesys.used + $lead_time * ( rate filesys.used ) >
+ filesys.capacity
+) -> print "filesystem will be full within $lead_time:" " %i";
diff --git a/src/pmie/examples/filesys.head b/src/pmie/examples/filesys.head
new file mode 100644
index 0000000..de4c703
--- /dev/null
+++ b/src/pmie/examples/filesys.head
@@ -0,0 +1,10 @@
+//
+// Some Common Performance Monitoring Scenarios
+//
+// The File System Group
+//
+
+// common prefixes
+//
+fsys = "filesys";
+
diff --git a/src/pmie/examples/network.00 b/src/pmie/examples/network.00
new file mode 100644
index 0000000..44c2183
--- /dev/null
+++ b/src/pmie/examples/network.00
@@ -0,0 +1,10 @@
+//
+// Report when some interface has seen more than 15 errors per second
+// on at least 3 of the last 4 observations
+//
+// Rule donated by Kevin Wang at Silicon Graphics
+//
+
+some_host some_inst 75%_sample (
+ network.interface.total.errors $HOSTS @0..3 > 15
+) -> print 5 min "high network interface errors" "%h[%i] %v errors/sec ";
diff --git a/src/pmie/examples/network.head b/src/pmie/examples/network.head
new file mode 100644
index 0000000..909e773
--- /dev/null
+++ b/src/pmie/examples/network.head
@@ -0,0 +1,15 @@
+//
+// Some Common Performance Monitoring Scenarios
+//
+// The Network Group
+//
+
+// replace with your hosts
+HOSTS = ":localhost :foo";
+
+// replace this with your e-mail address
+MINDER = "root@localhost";
+
+// 10 second rulesets in this group
+delta = 10 sec; // numbers are diff than strings
+DELTA_STR = "10 seconds"; // strings are diff than numbers
diff --git a/src/pmie/examples/ras.00 b/src/pmie/examples/ras.00
new file mode 100644
index 0000000..930b964
--- /dev/null
+++ b/src/pmie/examples/ras.00
@@ -0,0 +1,11 @@
+//
+// For Origin systems, sequence number errors are not indicative of
+// a problem, but persistent checkbit and/or retry errors may indicate
+// a CrayLink interconnect problem.
+//
+some_inst ( all_sample (
+ hw.router.perport.cb_errors @0..2 > 0 ||
+ hw.router.perport.retry_errors @0..2 > 0
+) )
+ -> alarm 30mins "CrayLink SN and/or Retry errors: " "%i ";
+
diff --git a/src/pmie/examples/ras.head b/src/pmie/examples/ras.head
new file mode 100644
index 0000000..d571913
--- /dev/null
+++ b/src/pmie/examples/ras.head
@@ -0,0 +1,5 @@
+//
+// Some System Reliability, Availability and Serviceability (RAS) Checks
+//
+
+delta = 20 sec;
diff --git a/src/pmie/examples/swap.00 b/src/pmie/examples/swap.00
new file mode 100644
index 0000000..2e3f309
--- /dev/null
+++ b/src/pmie/examples/swap.00
@@ -0,0 +1,23 @@
+//
+// report when swap > 50-75% full and when swap > 75% full
+//
+// Rules donated by Kevin Wang at Silicon Graphics
+//
+// note: the sort hack '9999999' to keep the header first; later
+// removed by sed
+// note: -o option to ps(1) requires IRIX 6.2 or later ... for IRIX 5.3
+// this would have to be re-written using ps -el
+
+SWAP="swap";
+some_host (
+ ($SWAP.free $HOSTS / $SWAP.length $HOSTS) * 100 < 50 &&
+ ($SWAP.free $HOSTS / $SWAP.length $HOSTS) * 100 >= 25
+) -> print 10 min "swap more than half-full: " "%h: %v% free " &
+ shell 10 min "rsh -n guest@%h /sbin/ps -eo 'ruser=UID,pid=PID,ppid=PPID,pcpu=%CPU,sz=9999999SZ,rss=RSS,stime=STIME,time=TIME,args=CMD' | sort +4 -nr | sed -e 's/9999999SZ / SZ:/' | /usr/sbin/Mail -s '%h swap more than half-full (%v% free)' $MINDER &";
+
+some_host (
+ ($SWAP.free $HOSTS / $SWAP.length $HOSTS) * 100 < 25
+) -> print 10 min "swap almost full: " "%h: %v% free " &
+ shell 10 min "rsh -n guest@%h /sbin/ps -eo 'ruser=UID,pid=PID,ppid=PPID,pcpu=%CPU,sz=9999999SZ,rss=RSS,stime=STIME,time=TIME,args=CMD' | sort +4 -nr | sed -e 's/9999999SZ / SZ:/' | /usr/sbin/Mail -s '%h swap almost full (%v% free)' $MINDER &";
+
+
diff --git a/src/pmie/examples/swap.head b/src/pmie/examples/swap.head
new file mode 100644
index 0000000..254cf4a
--- /dev/null
+++ b/src/pmie/examples/swap.head
@@ -0,0 +1,13 @@
+//
+// The Swap Group
+//
+
+// replace with your hosts
+HOSTS = ":localhost :foo";
+
+// replace this with your e-mail address
+MINDER = "root@localhost";
+
+// 20 second rulesets in this group
+delta = 20 sec; // numbers are diff than strings
+DELTA_STR = "20 seconds"; // strings are diff than numbers
diff --git a/src/pmie/examples/uag.00 b/src/pmie/examples/uag.00
new file mode 100644
index 0000000..6cb9638
--- /dev/null
+++ b/src/pmie/examples/uag.00
@@ -0,0 +1,5 @@
+//
+// a simple expression, with multiple values
+//
+iops = disk.dev.total;
+
diff --git a/src/pmie/examples/uag.01 b/src/pmie/examples/uag.01
new file mode 100644
index 0000000..7e6d691
--- /dev/null
+++ b/src/pmie/examples/uag.01
@@ -0,0 +1,4 @@
+//
+// total disk write percentage
+//
+wrt_pct = (disk.all.write / disk.all.total) * 100;
diff --git a/src/pmie/examples/uag.02 b/src/pmie/examples/uag.02
new file mode 100644
index 0000000..6127209
--- /dev/null
+++ b/src/pmie/examples/uag.02
@@ -0,0 +1,8 @@
+//
+// some varied expressions
+//
+pct_wrt = (disk.all.write / disk.all.total) * 100;
+busy_wrt = disk.dev.total > 10 &&
+ disk.dev.write > disk.dev.read;
+busy = some_inst disk.dev.total > 60 -> print "[%i] high disk i/o ";
+
diff --git a/src/pmie/examples/uag.03 b/src/pmie/examples/uag.03
new file mode 100644
index 0000000..01e4d7c
--- /dev/null
+++ b/src/pmie/examples/uag.03
@@ -0,0 +1,7 @@
+//
+// simple use of a macro
+//
+disk = "disk.all";
+pct_wrt = ($disk.write / $disk.total) * 100;
+
+
diff --git a/src/pmie/examples/uag.04 b/src/pmie/examples/uag.04
new file mode 100644
index 0000000..e13a5ef
--- /dev/null
+++ b/src/pmie/examples/uag.04
@@ -0,0 +1,52 @@
+//
+// perverse example to show all possible choices of units for numeric
+// constants
+//
+mem.freemem > 1 byte;
+mem.freemem > 1 Kbyte;
+mem.freemem > 1 Mbyte;
+mem.freemem > 1 Gbyte;
+mem.freemem > 1 Tbyte;
+
+disk.dev.blktotal > 1 Mbyte / nsec;
+disk.dev.blktotal > 1 Mbyte / nanosecond;
+disk.dev.blktotal > 1 Mbyte / usec;
+disk.dev.blktotal > 1 Mbyte / microsecond;
+disk.dev.blktotal > 1 Mbyte / msec;
+disk.dev.blktotal > 1 Mbyte / millisecond;
+disk.dev.blktotal > 1 Mbyte / sec;
+disk.dev.blktotal > 1 Mbyte / second;
+disk.dev.blktotal > 1 Mbyte / min;
+disk.dev.blktotal > 1 Mbyte / minute;
+disk.dev.blktotal > 1 Mbyte / hour;
+
+hinv.ncpu > 1 count;
+hinv.ncpu > 1 Kcount;
+hinv.ncpu > 1 count;
+hinv.ncpu > 1 Gcount;
+hinv.ncpu > 1 Tcount;
+
+mem.freemem > 1 bytes;
+mem.freemem > 1 Kbytes;
+mem.freemem > 1 Mbytes;
+mem.freemem > 1 Gbytes;
+mem.freemem > 1 Tbytes;
+
+disk.dev.blktotal > 1 Mbyte / nsecs;
+disk.dev.blktotal > 1 Mbyte / nanoseconds;
+disk.dev.blktotal > 1 Mbyte / usecs;
+disk.dev.blktotal > 1 Mbyte / microseconds;
+disk.dev.blktotal > 1 Mbyte / msecs;
+disk.dev.blktotal > 1 Mbyte / milliseconds;
+disk.dev.blktotal > 1 Mbyte / secs;
+disk.dev.blktotal > 1 Mbyte / seconds;
+disk.dev.blktotal > 1 Mbyte / mins;
+disk.dev.blktotal > 1 Mbyte / minutes;
+disk.dev.blktotal > 1 Mbyte / hours;
+
+hinv.ncpu > 1 counts;
+hinv.ncpu > 1 Kcounts;
+hinv.ncpu > 1 counts;
+hinv.ncpu > 1 Gcounts;
+hinv.ncpu > 1 Tcounts;
+
diff --git a/src/pmie/examples/uag.10 b/src/pmie/examples/uag.10
new file mode 100644
index 0000000..7135f6b
--- /dev/null
+++ b/src/pmie/examples/uag.10
@@ -0,0 +1,32 @@
+//
+// metric expressions
+
+// all instances
+//
+enet = network.interface.total.packets;
+
+// restricted instance (loopback interface only)
+//
+enet_r = network.interface.total.packets #lo0;
+
+// restricted instances with weird instance names ...
+// note instance names are "identifiers" in the grammar, so single
+// quotes required for tricky characters, like /, spaces, etc, _not_
+// double quotes
+//
+root_n_usr = filesys.free #'/dev/root' #'/dev/usr';
+
+// multiple hosts
+//
+num_cpu = hinv.ncpu :babylon.engr.sgi.com :gonzo :sandpit;
+
+// multiple sample times
+//
+mem_trend = mem.freemem @0..3;
+
+// multi-dimension variations
+//
+
+// missing instance for non-singular instance domain, plus multiple hosts
+//
+net_view = network.interface.total.packets :gonzo :moomba;
diff --git a/src/pmie/examples/uag.11 b/src/pmie/examples/uag.11
new file mode 100644
index 0000000..7d4b86b
--- /dev/null
+++ b/src/pmie/examples/uag.11
@@ -0,0 +1,7 @@
+//
+// relational (logical) expressions
+//
+hosts = ":gonzo";
+intfs = "#ec0 #ec2";
+all_intf = network.interface.in.packets
+ $hosts $intfs @0..2 > 300 count/sec;
diff --git a/src/pmie/examples/uag.12 b/src/pmie/examples/uag.12
new file mode 100644
index 0000000..3850168
--- /dev/null
+++ b/src/pmie/examples/uag.12
@@ -0,0 +1,12 @@
+//
+// quantification examples
+//
+
+// some_instance
+all_intf = network.interface.in.packets
+ #ec0 #ec2 @0..2 > 300 count/sec;
+any_sample = some_sample
+ network.interface.in.packets
+ #ec0 #ec2 @0..2 > 300 count/sec;
+
+
diff --git a/src/pmie/examples/uag.13 b/src/pmie/examples/uag.13
new file mode 100644
index 0000000..9990407
--- /dev/null
+++ b/src/pmie/examples/uag.13
@@ -0,0 +1,33 @@
+//
+// nested quantification
+//
+
+Servers = ":moomba :gonzo"; // change as desired
+
+// read and write rate per disk per host
+//
+rd = disk.dev.read $Servers;
+wr = disk.dev.write $Servers;
+
+// one value per host, true if 20% or more of the disks are doing
+// significant reading or writing
+//
+rd_20 = 20%_inst disk.dev.read $Servers > 40;
+wr_20 = 20%_inst disk.dev.write $Servers > 40;
+
+// single truth value: more than 20% of the disks busy reading or writing
+// on all hosts?
+//
+summary = all_host (
+ 20%_inst disk.dev.read $Servers > 40 ||
+ 20%_inst disk.dev.write $Servers > 40
+ );
+
+// alternate form
+//
+summary2 = all_host (
+ 20%_inst (
+ disk.dev.read $Servers > 40 ||
+ disk.dev.write $Servers > 40
+ )
+ );
diff --git a/src/pmie/examples/uag.20 b/src/pmie/examples/uag.20
new file mode 100644
index 0000000..475017c
--- /dev/null
+++ b/src/pmie/examples/uag.20
@@ -0,0 +1,7 @@
+//
+// a rule expression with multiple actions and %-binding in the
+// arguments for the action methods
+//
+some_inst ( disk.dev.total > 60 )
+ -> syslog 10 mins "[%i] busy, %v IOPS " &
+ shell 1 hour "echo 'Disk %i is REALLY busy. Running at %v I/Os per second' | Mail -s 'pmie alarm' sysadm";
diff --git a/src/pmie/examples/uag.21 b/src/pmie/examples/uag.21
new file mode 100644
index 0000000..c841ff2
--- /dev/null
+++ b/src/pmie/examples/uag.21
@@ -0,0 +1,9 @@
+//
+// a rule expression with multiple actions and %-binding in the
+// arguments for the action methods ... use some creative string
+// composition for the final message
+//
+some_inst ( disk.dev.total > 50 )
+ -> syslog 10 mins "Busy disks: " "%i @ %v IOPS " &
+ shell 1 hour "echo 'REALLY busy disks: " "%i @ %v I/Os per second " "' | Mail -s 'pmie alarm' sysadm";
+
diff --git a/src/pmie/examples/uag.30 b/src/pmie/examples/uag.30
new file mode 100644
index 0000000..b15a781
--- /dev/null
+++ b/src/pmie/examples/uag.30
@@ -0,0 +1,17 @@
+//
+// intrinsic operators
+//
+
+m = mem.freemem;
+rate_m = rate mem.freemem;
+
+// At least 2 CPUs doing some reasonable amount of work
+//
+poke = ":moomba :'mac-larry' :bitbucket"; // note '' to escape - in host name
+u = kernel.percpu.cpu.user $poke;
+s = kernel.percpu.cpu.sys $poke;
+some_host (
+ count_inst ( kernel.percpu.cpu.user $poke +
+ kernel.percpu.cpu.sys $poke > 0.7 ) >= 2
+ )
+ -> alarm "2 or more busy CPUs";
diff --git a/src/pmie/examples/uag.head b/src/pmie/examples/uag.head
new file mode 100644
index 0000000..9ba4f40
--- /dev/null
+++ b/src/pmie/examples/uag.head
@@ -0,0 +1,3 @@
+//
+// Examples from the Performance Co-Pilot User's and Administrator's Guide
+//
diff --git a/src/pmie/examples/upm.00 b/src/pmie/examples/upm.00
new file mode 100644
index 0000000..1b9e04c
--- /dev/null
+++ b/src/pmie/examples/upm.00
@@ -0,0 +1,6 @@
+//
+// If the total context switch rate exceeds 10000 per second per CPU
+// then display an alarm notifier
+//
+kernel.all.pswitch / hinv.ncpu > 10000 count/sec
+ -> alarm "high context switch rate %v";
diff --git a/src/pmie/examples/upm.01 b/src/pmie/examples/upm.01
new file mode 100644
index 0000000..d83bd79
--- /dev/null
+++ b/src/pmie/examples/upm.01
@@ -0,0 +1,4 @@
+all_sample (
+ kernel.all.pswitch @0..9 > 10 Kcount/sec * hinv.ncpu
+) -> shell 5 min "xwsh -e 'top'";
+
diff --git a/src/pmie/examples/upm.02 b/src/pmie/examples/upm.02
new file mode 100644
index 0000000..eaa0c42
--- /dev/null
+++ b/src/pmie/examples/upm.02
@@ -0,0 +1,19 @@
+delta = 5 sec; // force evaluation once every 5 seconds from here on
+
+// If for any disk, for all 4 samples (20 seconds), the disk is performing
+// more than 40 I/Os per second, then print a message to standard output and
+// then launch dkvis(1)
+//
+some_inst all_sample
+ disk.dev.total @0..3 > 40 count/sec
+ -> print "disks busy for 20 sec:" " %i" &
+ shell 5 min "dkvis";
+
+// If any disk is performing more than 60 I/Os per second, then
+// print a message identifying the busy disk to standard output and
+// launch dkvis(1)
+some_inst (
+ disk.dev.total > 60 count/sec
+) -> print "busy disks:" " %i" &
+ shell 5 min "dkvis";
+
diff --git a/src/pmie/examples/upm.03 b/src/pmie/examples/upm.03
new file mode 100644
index 0000000..ff7501e
--- /dev/null
+++ b/src/pmie/examples/upm.03
@@ -0,0 +1,8 @@
+//
+// Refine the preceding rule to apply only between the hours of 9am and 5pm,
+// and to require that just 3 of the four samples exceed the threshold
+//
+$hour >= 9 && $hour <= 17 && some_inst 75 %_sample
+ disk.dev.total @0..3 > 40 count/sec
+ -> print "disk busy for 20 sec" &
+ shell 5 min "dkvis";
diff --git a/src/pmie/examples/upm.04 b/src/pmie/examples/upm.04
new file mode 100644
index 0000000..36c9699
--- /dev/null
+++ b/src/pmie/examples/upm.04
@@ -0,0 +1,10 @@
+//
+// Refine the preceding rule further to print the host name and disk name
+// for which the threshold is exceeded
+//
+$hour >= 9 && $hour <= 17 &&
+some_inst (
+ 75 %_sample (
+ disk.dev.total @0..3 > 40 count/sec
+ )
+) -> print "disks busy for 20 sec:" " [%h]%i";
diff --git a/src/pmie/examples/upm.05 b/src/pmie/examples/upm.05
new file mode 100644
index 0000000..702747e
--- /dev/null
+++ b/src/pmie/examples/upm.05
@@ -0,0 +1,9 @@
+//
+// Macro for use ...
+//
+bc = "buffer_cache";
+// Using the above macro; If the buffer cache is in use (more than 50 read
+// requests) with hit ratio less than 90%, then popup an alarm
+//
+$bc.getblks > 50 && $bc.getfound / $bc.getblks < 0.9
+ -> alarm "poor buffer cache hit rate";
diff --git a/src/pmie/examples/upm.06 b/src/pmie/examples/upm.06
new file mode 100644
index 0000000..dbf9da9
--- /dev/null
+++ b/src/pmie/examples/upm.06
@@ -0,0 +1,10 @@
+delta = 10 mins; // force evaluation once every 10 minutes from here on
+
+// If either the / or the /usr filesystem is more than 95% full, display
+// an alarm popup, but not if it has already been displayed during the last
+// 24 hours
+//
+filesys.free #'/dev/root' / filesys.capacity #'/dev/root' < 0.05
+ -> alarm 24 hour "root filesystem (almost) full";
+filesys.free #'/dev/usr' / filesys.capacity #'/dev/usr' < 0.05
+ -> alarm 24 hour "/usr filesystem (almost) full";
diff --git a/src/pmie/examples/upm.07 b/src/pmie/examples/upm.07
new file mode 100644
index 0000000..f325ee6
--- /dev/null
+++ b/src/pmie/examples/upm.07
@@ -0,0 +1,8 @@
+//
+// The following rule requires a machine that supports the PCP environment
+// metrics. If the machine environment temperature rises more than 2
+// degrees over a 10 minute interval, write an entry in the system log
+//
+environ.temp @1 - environ.temp @0 > 2
+ -> alarm "temperature rising fast" &
+ syslog "machine room temperature rise alarm";
diff --git a/src/pmie/examples/upm.08 b/src/pmie/examples/upm.08
new file mode 100644
index 0000000..8ab0e49
--- /dev/null
+++ b/src/pmie/examples/upm.08
@@ -0,0 +1,13 @@
+//
+// Something interesting if you have performance problems with
+// your Oracle data base ...
+//
+db = "oracle.ptg1";
+host = ":moomba.melbourne.sgi.com";
+lru = "#'cache buffers lru chain'";
+gets = "$db.latch.gets $host $lru";
+total = "$db.latch.gets $host $lru + $db.latch.misses $host $lru +
+ $db.latch.immisses $host $lru";
+
+$total > 100 && $gets / $total < 0.2
+ -> alarm "high LRU latch contention";
diff --git a/src/pmie/examples/upm.09 b/src/pmie/examples/upm.09
new file mode 100644
index 0000000..e2f12cf
--- /dev/null
+++ b/src/pmie/examples/upm.09
@@ -0,0 +1,11 @@
+// Busy disk?
+
+delta = 20 sec; // force evaluation once every 20 seconds from here on
+
+// If any disk is performing more than 60 I/Os per second, then
+// print a message to standard output and launch dkvis(1)
+//
+some_inst
+ disk.dev.total > 60 count/sec
+ -> print "disk busy for 20 sec" "%v IOPS %i@%h" &
+ shell 5 min "dkvis";
diff --git a/src/pmie/examples/upm.10 b/src/pmie/examples/upm.10
new file mode 100644
index 0000000..ce40c1a
--- /dev/null
+++ b/src/pmie/examples/upm.10
@@ -0,0 +1,11 @@
+delta = 1 minute;
+ruleset
+ kernel.all.load #'1 minute' > 10 * hinv.ncpu ->
+ print "extreme load average %v"
+else kernel.all.load #'1 minute' > 2 * hinv.ncpu ->
+ print "moderate load average %v"
+unknown ->
+ print "load average unavailable"
+otherwise ->
+ print "load average OK"
+;
diff --git a/src/pmie/examples/upm.head b/src/pmie/examples/upm.head
new file mode 100644
index 0000000..3a286e9
--- /dev/null
+++ b/src/pmie/examples/upm.head
@@ -0,0 +1,5 @@
+//
+// Examples from the pmie(1) man page
+//
+
+delta = 1 sec; // force evaluation once per second
diff --git a/src/pmie/examples/webreport.00 b/src/pmie/examples/webreport.00
new file mode 100644
index 0000000..70e04d8
--- /dev/null
+++ b/src/pmie/examples/webreport.00
@@ -0,0 +1,8 @@
+//
+// Request rate throughput (requests per second) summaries
+//
+
+// you may replace the metric below with any of the other
+// web.allservers.requests metrics
+
+request_rate = web.allservers.requests.total;
diff --git a/src/pmie/examples/webreport.01 b/src/pmie/examples/webreport.01
new file mode 100644
index 0000000..cab9272
--- /dev/null
+++ b/src/pmie/examples/webreport.01
@@ -0,0 +1,8 @@
+//
+// Data throughput (Kbytes per minute) summaries
+//
+
+// you may replace the metric below with any of the other
+// web.allservers.bytes metrics
+
+data_rate = web.allservers.bytes.total * 60 / 1024;
diff --git a/src/pmie/examples/webreport.head b/src/pmie/examples/webreport.head
new file mode 100644
index 0000000..91deb82
--- /dev/null
+++ b/src/pmie/examples/webreport.head
@@ -0,0 +1,11 @@
+//
+// Some Common Performance Monitoring Scenarios
+//
+// The WEBREPORT Group
+//
+// Intended to be used with archive logs to produce summaries, e.g.
+// pmie -v -A 1hour -S @10:00am -T @2:00pm -a somearchive WEBREPORT
+// produces hourly summaries for the hours 10am to 2pm
+//
+
+delta = 1 hour; // change to suit, else use -t from the command line
diff --git a/src/pmie/pmie.service.in b/src/pmie/pmie.service.in
new file mode 100644
index 0000000..28d228b
--- /dev/null
+++ b/src/pmie/pmie.service.in
@@ -0,0 +1,13 @@
+[Unit]
+Description=Performance Metrics Inference Engine
+Documentation=man:pmie(1)
+After=local-fs.target network.target
+
+[Service]
+Type=oneshot
+ExecStart=@path@/pmie start
+ExecStop=@path@/pmie stop
+RemainAfterExit=yes
+
+[Install]
+WantedBy=multi-user.target
diff --git a/src/pmie/pmie2col.sh b/src/pmie/pmie2col.sh
new file mode 100755
index 0000000..fafe542
--- /dev/null
+++ b/src/pmie/pmie2col.sh
@@ -0,0 +1,133 @@
+#!/bin/sh
+#
+# A crude ascii reporting tool, convert pmie to column output
+#
+# The pmie rules need to be of the form:
+# load_1 = kernel.all.load #'1 minute';
+# idle = kernel.all.cpu.idle;
+# column_name=some other expression;
+# ...
+#
+# Each pmie expression has to produce a singular value.
+#
+# With timestamps (pmie -e or pmie output from a PCP archive), lines look like
+# metric (Tue Feb 13 05:01:19 2001): value
+# load_1 (Tue Dec 23 12:20:45 2003): 0.24
+# the first sed step in the filter sorts this out.
+#
+# First e-mailed to Patrick Aland <paland@stetson.edu> and pcp@oss.sgi.com
+# on Wed, 24 Jan 2001.
+#
+
+# Get standard environment
+. $PCP_DIR/etc/pcp.env
+
+tmp=`mktemp -d /tmp/pcp.XXXXXXXXX` || exit 1
+status=1
+trap "rm -rf $tmp; exit \$status" 0 1 2 3 15
+prog=`basename $0`
+
+# For interactive use, works better with line buffered output from sed(1)
+# and awk(1).
+#
+case "$PCP_PLATFORM"
+in
+ linux|mingw|kfreebsd|gnu)
+ SED="sed -u"
+ ;;
+ freebsd|darwin)
+ SED="sed -l"
+ ;;
+ *)
+ SED=sed
+ ;;
+esac
+
+echo > $tmp/usage
+cat >> $tmp/usage <<EOF
+Options:
+ -d=CHAR,--delimiter=CHAR set the output delimiter character
+ -p=N,--precision=N set the output floating point precision
+ -w=N,--width=N set the width of each column of output
+ --help
+EOF
+
+_usage()
+{
+ pmgetopt --progname=$prog --config=$tmp/usage --usage
+ exit 1
+}
+
+pre=2 # floating point precision
+wid=7 # reporting column width
+delim=' '
+
+ARGS=`pmgetopt --progname=$prog --config=$tmp/usage -- "$@"`
+[ $? != 0 ] && exit 1
+
+eval set -- "$ARGS"
+while [ $# -gt 0 ]
+do
+ case "$1"
+ in
+ -w) wid="$2"
+ shift
+ ;;
+ -p) pre="$2"
+ shift
+ ;;
+ -d) delim="$2"
+ shift
+ ;;
+ --) shift
+ break
+ ;;
+ -\?) _usage
+ ;;
+ esac
+ shift
+done
+
+[ $# -eq 0 ] || _usage
+
+# culled output at the start is produced by pmie and/or pmafm
+$SED \
+ -e '/^pmie: timezone set to/d' \
+ -e '/^Note: running pmie serially, once per archive$/d' \
+ -e '/^Host: [A-Za-z0-9]* Archive: /d' \
+ -e '/^[^ ][^ ]* ([A-Z][a-z][a-z] [A-Z][a-z][a-z] *[0-9][0-9]* [0-2][0-9]:[0-5][0-9]:[0-5][0-9] [0-9][0-9][0-9][0-9]): /{
+s/ (/|/
+s/): /|/
+}' \
+ -e '/^\([^ ][^ ]*\):/s//\1||/' \
+| awk -F\| -v delim="$delim" '
+NF == 0 { if (state == 0) {
+ ncol = i
+ print ""
+ }
+ if (stamp != "") printf "%24s ",stamp
+ for (i = 0; i < ncol; i++) {
+ if (v[i] == "?")
+ # no value
+ printf "%s%'$wid'.'$wid's",delim,"?"
+ else if (v[i]+0 == v[i])
+ # number
+ printf "%s%'$wid'.'$pre'f",delim,v[i]
+ else
+ # string
+ printf "%s%'$wid'.'$wid's",delim,v[i]
+ v[i] = "?"
+ }
+ print ""
+ i = 0
+ stamp = ""
+ state = 1
+ next
+ }
+NF == 3 && stamp == "" { stamp = $2 }
+state == 0 { if (i == 0 && stamp != "") printf "%24s ",""
+ printf "%s%'$wid'.'$wid's",delim,$1 }
+ { v[i++] = $NF }'
+
+status=0
+exit
diff --git a/src/pmie/pmie_check.sh b/src/pmie/pmie_check.sh
new file mode 100644
index 0000000..273903b
--- /dev/null
+++ b/src/pmie/pmie_check.sh
@@ -0,0 +1,691 @@
+#! /bin/sh
+#
+# Copyright (c) 2013-2014 Red Hat.
+# Copyright (c) 1998-2000,2003 Silicon Graphics, Inc. All Rights Reserved.
+#
+# This program is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by the
+# Free Software Foundation; either version 2 of the License, or (at your
+# option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+# for more details.
+#
+# Administrative script to check pmie processes are alive, and restart
+# them as required.
+#
+
+# Get standard environment
+. $PCP_DIR/etc/pcp.env
+. $PCP_SHARE_DIR/lib/rc-proc.sh
+
+PMIE=pmie
+PMIECONF="$PCP_BIN_DIR/pmieconf"
+
+# error messages should go to stderr, not the GUI notifiers
+unset PCP_STDERR
+
+# added to handle problem when /var/log/pcp is a symlink, as first
+# reported by Micah_Altman@harvard.edu in Nov 2001
+#
+_unsymlink_path()
+{
+ [ -z "$1" ] && return
+ __d=`dirname $1`
+ __real_d=`cd $__d 2>/dev/null && $PWDCMND`
+ if [ -z "$__real_d" ]
+ then
+ echo $1
+ else
+ echo $__real_d/`basename $1`
+ fi
+}
+
+# constant setup
+#
+tmp=`mktemp -d /tmp/pcp.XXXXXXXXX` || exit 1
+status=0
+echo >$tmp/lock
+trap "rm -rf \`[ -f $tmp/lock ] && cat $tmp/lock\` $tmp; exit \$status" 0 1 2 3 15
+prog=`basename $0`
+
+# control file for pmie administration ... edit the entries in this
+# file to reflect your local configuration
+#
+CONTROL=$PCP_PMIECONTROL_PATH
+
+# NB: FQDN cleanup; don't guess a 'real name for localhost', and
+# definitely don't truncate it a la `hostname -s`. Instead now
+# we use such a string only for the default log subdirectory, ie.
+# for substituting LOCALHOSTNAME in the third column of $CONTROL.
+
+# determine path for pwd command to override shell built-in
+PWDCMND=`which pwd 2>/dev/null | $PCP_AWK_PROG '
+BEGIN { i = 0 }
+/ not in / { i = 1 }
+/ aliased to / { i = 1 }
+ { if ( i == 0 ) print }
+'`
+[ -z "$PWDCMND" ] && PWDCMND=/bin/pwd
+eval $PWDCMND -P >/dev/null 2>&1
+[ $? -eq 0 ] && PWDCMND="$PWDCMND -P"
+here=`$PWDCMND`
+
+# determine whether we can automatically enable any events sinks
+CONFARGS="-cF"
+if which esplogger >/dev/null 2>&1
+then
+ CONFARGS='m global syslog_prefix $esp_prefix$'
+fi
+
+# option parsing
+#
+SHOWME=false
+MV=mv
+RM=rm
+CP=cp
+KILL=pmsignal
+TERSE=false
+VERBOSE=false
+VERY_VERBOSE=false
+CHECK_RUNLEVEL=false
+START_PMIE=true
+
+echo > $tmp/usage
+cat >> $tmp/usage << EOF
+Options:
+ -c=FILE,--control=FILE configuration of pmie instances to manage
+ -C query system service runlevel information
+ -N,--showme perform a dry run, showing what would be done
+ -s,--stop stop pmie processes instead of starting them
+ -T,--terse produce a terser form of output
+ -V,--verbose increase diagnostic verbosity
+ --help
+EOF
+
+ARGS=`pmgetopt --progname=$prog --config=$tmp/usage -- "$@"`
+[ $? != 0 ] && exit 1
+
+eval set -- "$ARGS"
+while [ $# -gt 0 ]
+do
+ case "$1"
+ in
+ -c) CONTROL="$2"
+ shift
+ ;;
+ -C) CHECK_RUNLEVEL=true
+ ;;
+ -N) SHOWME=true
+ MV="echo + mv"
+ RM="echo + rm"
+ CP="echo + cp"
+ KILL="echo + kill"
+ ;;
+ -s) START_PMIE=false
+ ;;
+ -T) TERSE=true
+ ;;
+ -V) if $VERBOSE
+ then
+ VERY_VERBOSE=true
+ else
+ VERBOSE=true
+ fi
+ ;;
+ --) shift
+ break
+ ;;
+ -\?) pmgetopt --usage --progname=$prog --config=$tmp/usage
+ status=1
+ exit
+ ;;
+ esac
+ shift
+done
+
+if [ $# -ne 0 ]
+then
+ pmgetopt --usage --progname=$prog --config=$tmp/usage
+ status=1
+ exit
+fi
+
+_error()
+{
+ echo "$prog: [$CONTROL:$line]"
+ echo "Error: $1"
+ echo "... automated performance reasoning for host \"$host\" unchanged"
+ touch $tmp/err
+}
+
+_warning()
+{
+ echo "$prog [$CONTROL:$line]"
+ echo "Warning: $1"
+}
+
+_message()
+{
+ case $1
+ in
+ 'restart')
+ $PCP_ECHO_PROG $PCP_ECHO_N "Restarting pmie for host \"$host\" ...""$PCP_ECHO_C"
+ ;;
+ esac
+}
+
+_lock()
+{
+ # demand mutual exclusion
+ #
+ rm -f $tmp/stamp
+ delay=200 # tenths of a second
+ while [ $delay -ne 0 ]
+ do
+ if pmlock -v $logfile.lock >$tmp/out
+ then
+ echo $logfile.lock >$tmp/lock
+ break
+ else
+ if [ ! -f $tmp/stamp ]
+ then
+ touch -t `pmdate -30M %Y%m%d%H%M` $tmp/stamp
+ fi
+ if [ -n "`find $logfile.lock ! -newer $tmp/stamp -print 2>/dev/null`" ]
+ then
+ _warning "removing lock file older than 30 minutes"
+ ls -l $logfile.lock
+ rm -f $logfile.lock
+ fi
+ fi
+ pmsleep 0.1
+ delay=`expr $delay - 1`
+ done
+
+ if [ $delay -eq 0 ]
+ then
+ # failed to gain mutex lock
+ #
+ if [ -f $logfile.lock ]
+ then
+ _warning "is another PCP cron job running concurrently?"
+ ls -l $logfile.lock
+ else
+ echo "$prog: `cat $tmp/out`"
+ fi
+ _warning "failed to acquire exclusive lock ($logfile.lock) ..."
+ continue
+ fi
+}
+
+_unlock()
+{
+ rm -f $logfile.lock
+ echo >$tmp/lock
+}
+
+_check_logfile()
+{
+ if [ ! -f $logfile ]
+ then
+ echo "$prog: Error: cannot find pmie output file at \"$logfile\""
+ if $TERSE
+ then
+ :
+ else
+ logdir=`dirname $logfile`
+ echo "Directory (`cd $logdir; $PWDCMND`) contents:"
+ LC_TIME=POSIX ls -la $logdir
+ fi
+ else
+ echo "Contents of pmie output file \"$logfile\" ..."
+ cat $logfile
+ fi
+}
+
+_check_pmie()
+{
+ $VERBOSE && $PCP_ECHO_PROG $PCP_ECHO_N " [process $1] ""$PCP_ECHO_C"
+
+ # wait until pmie process starts, or exits
+ #
+ delay=5
+ [ ! -z "$PMCD_CONNECT_TIMEOUT" ] && delay=$PMCD_CONNECT_TIMEOUT
+ x=5
+ [ ! -z "$PMCD_REQUEST_TIMEOUT" ] && x=$PMCD_REQUEST_TIMEOUT
+
+ # wait for maximum time of a connection and 20 requests
+ #
+ delay=`expr \( $delay + 20 \* $x \) \* 10` # tenths of a second
+ while [ $delay -ne 0 ]
+ do
+ if [ -f $logfile ]
+ then
+ # $logfile was previously removed, if it has appeared again then
+ # we know pmie has started ... if not just sleep and try again
+ #
+ if ls "$PCP_TMP_DIR/pmie/$1" >$tmp/out 2>&1
+ then
+ if grep "No such file or directory" $tmp/out >/dev/null
+ then
+ :
+ else
+ $VERBOSE && echo " done"
+ return 0
+ fi
+ fi
+
+ _plist=`_get_pids_by_name pmie`
+ _found=false
+ for _p in `echo $_plist`
+ do
+ [ $_p -eq $1 ] && _found=true
+ done
+
+ if $_found
+ then
+ # process still here, just hasn't created its status file
+ # yet, try again
+ :
+ else
+ $VERBOSE || _message restart
+ echo " process exited!"
+ if $TERSE
+ then
+ :
+ else
+ echo "$prog: Error: failed to restart pmie"
+ echo "Current pmie processes:"
+ $PCP_PS_PROG $PCP_PS_ALL_FLAGS | tee $tmp/tmp | sed -n -e 1p
+ for _p in `echo $_plist`
+ do
+ sed -n -e "/^[ ]*[^ ]* [ ]*$_p /p" < $tmp/tmp
+ done
+ echo
+ fi
+ _check_logfile
+ return 1
+ fi
+ fi
+ pmsleep 0.1
+ delay=`expr $delay - 1`
+ $VERBOSE && [ `expr $delay % 10` -eq 0 ] && \
+ $PCP_ECHO_PROG $PCP_ECHO_N ".""$PCP_ECHO_C"
+ done
+ $VERBOSE || _message restart
+ echo " timed out waiting!"
+ if $TERSE
+ then
+ :
+ else
+ sed -e 's/^/ /' $tmp/out
+ fi
+ _check_logfile
+ return 1
+}
+
+_get_configfile()
+{
+ # extract the pmie configuration file (-c) from a list of arguments
+ #
+ echo $@ | sed -n \
+ -e 's/^/ /' \
+ -e 's/[ ][ ]*/ /g' \
+ -e 's/-c /-c/' \
+ -e 's/.* -c\([^ ]*\).*/\1/p'
+}
+
+_configure_pmie()
+{
+ # update a pmie configuration file if it should be created/modified
+ #
+ configfile="$1"
+
+ if [ -f "$configfile" ]
+ then
+ # look for "magic" string at start of file, and ensure we created it
+ sed 1q "$configfile" | grep '^// pmieconf-pmie [0-9]' >/dev/null
+ magic=$?
+ grep '^// Auto-generated by pmieconf' "$configfile" >/dev/null
+ owned=$?
+ if [ $magic -eq 0 -a $owned -eq 0 ]
+ then
+ # pmieconf file, see if re-generation is needed
+ cp "$configfile" $tmp/pmie
+ if $PMIECONF -f $tmp/pmie $CONFARGS >$tmp/diag 2>&1
+ then
+ grep -v "generated by pmieconf" "$configfile" >$tmp/old
+ grep -v "generated by pmieconf" $tmp/pmie >$tmp/new
+ if ! diff $tmp/old $tmp/new >/dev/null
+ then
+ if [ -w $configfile ]
+ then
+ $VERBOSE && echo "Reconfigured: \"$configfile\" (pmieconf)"
+ eval $CP $tmp/pmie "$configfile"
+ else
+ _warning "no write access to pmieconf file \"$configfile\", skip reconfiguration"
+ ls -l "$configfile"
+ fi
+ fi
+ else
+ _warning "pmieconf failed to reconfigure \"$configfile\""
+ cat "s;$tmp/pmie;$configfile;g" $tmp/diag
+ echo "=== start pmieconf file ==="
+ cat $tmp/pmie
+ echo "=== end pmieconf file ==="
+ fi
+ fi
+ elif [ ! -e "$configfile" ]
+ then
+ # file does not exist, generate it, if possible
+ if $SHOWME
+ then
+ echo "+ $PMIECONF -f $configfile $CONFARGS"
+ elif ! $PMIECONF -f "$configfile" $CONFARGS >$tmp/diag 2>&1
+ then
+ _warning "pmieconf failed to generate \"$configfile\""
+ cat $tmp/diag
+ echo "=== start pmieconf file ==="
+ cat "$configfile"
+ echo "=== end pmieconf file ==="
+ else
+ chown $PCP_USER:$PCP_GROUP "$configfile" >/dev/null 2>&1
+ fi
+ fi
+}
+
+QUIETLY=false
+if [ $CHECK_RUNLEVEL = true ]
+then
+ # determine whether to start/stop based on runlevel settings - we
+ # need to do this when running unilaterally from cron, else we'll
+ # always start pmie up (even when we shouldn't).
+ #
+ QUIETLY=true
+ if is_chkconfig_on pmie
+ then
+ START_PMIE=true
+ else
+ START_PMIE=false
+ fi
+fi
+
+if [ $START_PMIE = false ]
+then
+ # if pmie has never been started, there's no work to do to stop it
+ [ ! -d "$PCP_TMP_DIR/pmie" ] && exit
+ $QUIETLY || $PCP_BINADM_DIR/pmpost "stop pmie from $prog"
+fi
+
+if [ ! -f "$CONTROL" ]
+then
+ echo "$prog: Error: cannot find control file ($CONTROL)"
+ status=1
+ exit
+fi
+
+# 1.0 is the first release, and the version is set in the control file
+# with a $version=x.y line
+#
+version=1.0
+eval `grep '^version=' "$CONTROL" | sort -rn`
+if [ $version != "1.0" ]
+then
+ _error "unsupported version (got $version, expected 1.0)"
+ status=1
+ exit
+fi
+
+echo >$tmp/dir
+rm -f $tmp/err $tmp/pmies
+
+line=0
+cat "$CONTROL" \
+ | sed -e "s;PCP_LOG_DIR;$PCP_LOG_DIR;g" \
+ | while read host socks logfile args
+do
+ # start in one place for each iteration (beware relative paths)
+ cd "$here"
+ line=`expr $line + 1`
+
+ # NB: FQDN cleanup: substitute the LOCALHOSTNAME marker in the config line
+ # differently for the directory and the pcp -h HOST arguments.
+ logfile_hostname=`hostname || echo localhost`
+ logfile=`echo $logfile | sed -e "s;LOCALHOSTNAME;$logfile_hostname;"`
+ logfile=`_unsymlink_path $logfile`
+ [ "x$host" = "xLOCALHOSTNAME" ] && host=local:
+
+ case "$host"
+ in
+ \#*|'') # comment or empty
+ continue
+ ;;
+ \$*) # in-line variable assignment
+ $SHOWME && echo "# $host $socks $logfile $args"
+ cmd=`echo "$host $socks $logfile $args" \
+ | sed -n \
+ -e "/='/s/\(='[^']*'\).*/\1/" \
+ -e '/="/s/\(="[^"]*"\).*/\1/' \
+ -e '/=[^"'"'"']/s/[;&<>|].*$//' \
+ -e '/^\\$[A-Za-z][A-Za-z0-9_]*=/{
+s/^\\$//
+s/^\([A-Za-z][A-Za-z0-9_]*\)=/export \1; \1=/p
+}'`
+ if [ -z "$cmd" ]
+ then
+ # in-line command, not a variable assignment
+ _warning "in-line command is not a variable assignment, line ignored"
+ else
+ case "$cmd"
+ in
+ 'export PATH;'*)
+ _warning "cannot change \$PATH, line ignored"
+ ;;
+ 'export IFS;'*)
+ _warning "cannot change \$IFS, line ignored"
+ ;;
+ *)
+ $SHOWME && echo "+ $cmd"
+ eval $cmd
+ ;;
+ esac
+ fi
+ continue
+ ;;
+ esac
+
+ if [ -z "$socks" -o -z "$logfile" -o -z "$args" ]
+ then
+ _error "insufficient fields in control file record"
+ continue
+ fi
+
+ $VERY_VERBOSE && echo "Check pmie -h $host -l $logfile ..."
+
+ # make sure output directory exists
+ #
+ dir=`dirname $logfile`
+ if [ ! -d "$dir" ]
+ then
+ mkdir -p -m 755 "$dir" >$tmp/err 2>&1
+ if [ ! -d "$dir" ]
+ then
+ cat $tmp/err
+ _error "cannot create directory ($dir) for pmie log file"
+ continue
+ fi
+ chown $PCP_USER:$PCP_GROUP "$dir" >/dev/null 2>&1
+ fi
+
+ cd "$dir"
+ dir=`$PWDCMND`
+ $SHOWME && echo "+ cd $dir"
+
+ # ensure pcp user will be able to write there
+ #
+ chown -R $PCP_USER:$PCP_GROUP "$dir" >/dev/null 2>&1
+ if [ ! -w "$dir" ]
+ then
+ _warning "no write access in $dir, skip lock file processing"
+ ls -ld "$dir"
+ else
+ _lock
+ fi
+
+ # match $logfile from control file to running pmies
+ pid=""
+ for file in $PCP_TMP_DIR/pmie/[0-9]*
+ do
+ [ "$file" = "$PCP_TMP_DIR/pmie/[0-9]*" ] && continue
+ $VERY_VERBOSE && $PCP_ECHO_PROG $PCP_ECHO_N "... try $file: ""$PCP_ECHO_C"
+
+ p_id=`echo $file | sed -e 's,.*/,,'`
+ p_logfile=""
+ p_pmcd_host=""
+
+ # throw away stderr in case $file has been removed by now
+ eval `$PCP_BINADM_DIR/pmiestatus $file 2>/dev/null | $PCP_AWK_PROG '
+NR == 2 { printf "p_logfile=\"%s\"\n", $0; next }
+NR == 3 { printf "p_pmcd_host=\"%s\"\n", $0; next }
+ { next }'`
+
+ p_logfile=`_unsymlink_path $p_logfile`
+ if [ "$p_logfile" != $logfile ]
+ then
+ $VERY_VERBOSE && echo "different logfile, skip"
+ $VERY_VERBOSE && echo " $p_logfile differs to $logfile"
+ elif _get_pids_by_name pmie | grep "^$p_id\$" >/dev/null
+ then
+ $VERY_VERBOSE && echo "pmie process $p_id identified, OK"
+ pid=$p_id
+ break
+ else
+ $VERY_VERBOSE && echo "pmie process $p_id not running, skip"
+ $VERY_VERBOSE && _get_pids_by_name pmie
+ fi
+ done
+
+ if $VERY_VERBOSE
+ then
+ if [ -z "$pid" ]
+ then
+ echo "No current pmie process exists for:"
+ else
+ echo "Found pmie process $pid monitoring:"
+ fi
+ echo " host = $host"
+ echo " log file = $logfile"
+ fi
+
+ if [ -z "$pid" -a $START_PMIE = true ]
+ then
+ configfile=`_get_configfile $args`
+ if [ ! -z "$configfile" ]
+ then
+ # if this is a relative path and not relative to cwd,
+ # substitute in the default pmie search location.
+ #
+ if [ ! -f "$configfile" -a "`basename $configfile`" = "$configfile" ]
+ then
+ configfile="$PCP_SYSCONF_DIR/pmie/$configfile"
+ fi
+
+ # check configuration file exists and is up to date
+ _configure_pmie "$configfile" "$host"
+ fi
+
+ args="-h $host -l $logfile $args"
+
+ $VERBOSE && _message restart
+
+ sock_me=''
+ if [ "$socks" = y ]
+ then
+ # only check for pmsocks if it's specified in the control file
+ have_pmsocks=false
+ if which pmsocks >/dev/null 2>&1
+ then
+ # check if pmsocks has been set up correctly
+ if pmsocks ls >/dev/null 2>&1
+ then
+ have_pmsocks=true
+ fi
+ fi
+
+ if $have_pmsocks
+ then
+ sock_me="pmsocks "
+ else
+ echo "$prog: Warning: no pmsocks available, would run without"
+ sock_me=""
+ fi
+ fi
+
+ [ -f "$logfile" ] && eval $MV -f "$logfile" "$logfile.prior"
+
+ if $SHOWME
+ then
+ $VERBOSE && echo
+ echo "+ ${sock_me}$PMIE -b $args"
+ _unlock
+ continue
+ else
+ # since this is launched as a sort of daemon, any output should
+ # go on pmie's stderr, i.e. $logfile ... use -b for this
+ #
+ $VERY_VERBOSE && ( echo; $PCP_ECHO_PROG $PCP_ECHO_N "+ ${sock_me}$PMIE -b $args""$PCP_ECHO_C"; echo "..." )
+ $PCP_BINADM_DIR/pmpost "start pmie from $prog for host $host"
+ ${sock_me}$PMIE -b $args &
+ pid=$!
+ fi
+
+ # wait for pmie to get started, and check on its health
+ _check_pmie $pid
+
+ elif [ ! -z "$pid" -a $START_PMIE = false ]
+ then
+ # Send pmie a SIGTERM, which is noted as a pending shutdown.
+ # Add pid to list of pmies sent SIGTERM - may need SIGKILL later.
+ #
+ $VERY_VERBOSE && echo "+ $KILL -s TERM $pid"
+ eval $KILL -s TERM $pid
+ $PCP_ECHO_PROG $PCP_ECHO_N "$pid ""$PCP_ECHO_C" >> $tmp/pmies
+ fi
+
+ _unlock
+done
+
+# check all the SIGTERM'd pmies really died - if not, use a bigger hammer.
+#
+if $SHOWME
+then
+ :
+elif [ $START_PMIE = false -a -s $tmp/pmies ]
+then
+ pmielist=`cat $tmp/pmies`
+ if ps -p "$pmielist" >/dev/null 2>&1
+ then
+ $VERY_VERBOSE && ( echo; $PCP_ECHO_PROG $PCP_ECHO_N "+ $KILL -KILL `cat $tmp/pmies` ...""$PCP_ECHO_C" )
+ eval $KILL -s KILL $pmielist >/dev/null 2>&1
+ delay=30 # tenths of a second
+ while ps -f -p "$pmielist" >$tmp/alive 2>&1
+ do
+ if [ $delay -gt 0 ]
+ then
+ pmsleep 0.1
+ delay=`expr $delay - 1`
+ continue
+ fi
+ echo "$prog: Error: pmie process(es) will not die"
+ cat $tmp/alive
+ status=1
+ break
+ done
+ fi
+fi
+
+[ -f $tmp/err ] && status=1
+exit
diff --git a/src/pmie/pmie_daily.sh b/src/pmie/pmie_daily.sh
new file mode 100644
index 0000000..3e386c0
--- /dev/null
+++ b/src/pmie/pmie_daily.sh
@@ -0,0 +1,540 @@
+#! /bin/sh
+#
+# Copyright (c) 2013-2014 Red Hat.
+# Copyright (c) 2007 Aconex. All Rights Reserved.
+# Copyright (c) 1995-2000,2003 Silicon Graphics, Inc. All Rights Reserved.
+#
+# This program is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by the
+# Free Software Foundation; either version 2 of the License, or (at your
+# option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+# for more details.
+#
+# Daily administrative script for pmie logfiles
+#
+
+. $PCP_DIR/etc/pcp.env
+. $PCP_SHARE_DIR/lib/rc-proc.sh
+
+# added to handle problem when /var/log/pcp is a symlink, as first
+# reported by Micah_Altman@harvard.edu in Nov 2001
+#
+_unsymlink_path()
+{
+ [ -z "$1" ] && return
+ __d=`dirname $1`
+ __real_d=`cd $__d 2>/dev/null && $PWDCMND`
+ if [ -z "$__real_d" ]
+ then
+ echo $1
+ else
+ echo $__real_d/`basename $1`
+ fi
+}
+
+# error messages should go to stderr, not the GUI notifiers
+#
+unset PCP_STDERR
+
+# constant setup
+#
+tmp=`mktemp -d /tmp/pcp.XXXXXXXXX` || exit 1
+status=0
+echo >$tmp/lock
+trap "rm -rf \`[ -f $tmp/lock ] && cat $tmp/lock\` $tmp; exit \$status" 0 1 2 3 15
+prog=`basename $0`
+
+if is_chkconfig_on pmie
+then
+ PMIE_CTL=on
+else
+ PMIE_CTL=off
+fi
+
+# control file for pmie administration ... edit the entries in this
+# file to reflect your local configuration (see also -c option below)
+#
+CONTROL=$PCP_PMIECONTROL_PATH
+
+# default number of days to keep pmie logfiles
+#
+CULLAFTER=14
+
+# default compression program and days until starting compression
+#
+COMPRESS=xz
+COMPRESSAFTER=""
+COMPRESSREGEX="\.(meta|index|Z|gz|bz2|zip|xz|lzma|lzo|lz4)$"
+
+# mail addresses to send daily logfile summary to
+#
+MAILME=""
+
+# search for your mail agent of choice ...
+#
+MAIL=''
+for try in Mail mail email
+do
+ if which $try >/dev/null 2>&1
+ then
+ MAIL=$try
+ break
+ fi
+done
+
+# NB: FQDN cleanup; don't guess a 'real name for localhost', and
+# definitely don't truncate it a la `hostname -s`. Instead now
+# we use such a string only for the default log subdirectory, ie.
+# for substituting LOCALHOSTNAME in the third column of $CONTROL.
+
+# determine path for pwd command to override shell built-in
+# (see BugWorks ID #595416).
+PWDCMND=`which pwd 2>/dev/null | $PCP_AWK_PROG '
+BEGIN { i = 0 }
+/ not in / { i = 1 }
+/ aliased to / { i = 1 }
+ { if ( i == 0 ) print }
+'`
+if [ -z "$PWDCMND" ]
+then
+ # Looks like we have no choice here...
+ # force it to a known IRIX location
+ PWDCMND=/bin/pwd
+fi
+eval $PWDCMND -P >/dev/null 2>&1
+[ $? -eq 0 ] && PWDCMND="$PWDCMND -P"
+here=`$PWDCMND`
+
+echo > $tmp/usage
+cat >> $tmp/usage <<EOF
+Options:
+ -c=FILE,--control=FILE pmie control file
+ -k=N,--discard=N remove pmie log files after N days
+ -m=ADDRs,--mail=ADDRs send daily log files to email addresses
+ -N,--showme perform a dry run, showing what would be done
+ -V,--verbose verbose output (multiple times for very verbose)
+ -x=N,--compress-after=N compress pmie log files after N days
+ -X=PROGRAM,--compressor=PROGRAM use PROGRAM for pmie log file compression
+ -Y=REGEX,--regex=REGEX egrep filter when compressing files ["$COMPRESSREGEX"]
+ --help
+EOF
+
+_usage()
+{
+ pmgetopt --progname=$prog --config=$tmp/usage --usage
+ status=1
+ exit
+}
+
+# option parsing
+#
+SHOWME=false
+RM=rm
+KILL=pmsignal
+VERBOSE=false
+VERY_VERBOSE=false
+MYARGS=""
+
+ARGS=`pmgetopt --progname=$prog --config=$tmp/usage -- "$@"`
+[ $? != 0 ] && exit 1
+
+eval set -- "$ARGS"
+while [ $# -gt 0 ]
+do
+ case "$1"
+ in
+ -c) CONTROL="$2"
+ shift
+ ;;
+ -k) CULLAFTER="$2"
+ shift
+ check=`echo "$CULLAFTER" | sed -e 's/[0-9]//g'`
+ if [ ! -z "$check" -a X"$check" != Xforever ]
+ then
+ echo "Error: -k option ($CULLAFTER) must be numeric"
+ status=1
+ exit
+ fi
+ ;;
+ -m) MAILME="$2"
+ shift
+ ;;
+ -N) SHOWME=true
+ RM="echo + rm"
+ KILL="echo + kill"
+ MYARGS="$MYARGS -N"
+ ;;
+ -V) if $VERBOSE
+ then
+ VERY_VERBOSE=true
+ else
+ VERBOSE=true
+ fi
+ MYARGS="$MYARGS -V"
+ ;;
+ -x) COMPRESSAFTER="$2"
+ shift
+ check=`echo "$COMPRESSAFTER" | sed -e 's/[0-9]//g'`
+ if [ ! -z "$check" ]
+ then
+ echo "Error: -x option ($COMPRESSAFTER) must be numeric"
+ status=1
+ exit
+ fi
+ ;;
+ -X) COMPRESS="$2"
+ shift
+ ;;
+ -Y) COMPRESSREGEX="$2"
+ shift
+ ;;
+ --) shift
+ break
+ ;;
+ -\?) _usage
+ ;;
+ esac
+ shift
+done
+
+[ $# -ne 0 ] && _usage
+
+if [ ! -f "$CONTROL" ]
+then
+ echo "$prog: Error: cannot find control file ($CONTROL)"
+ status=1
+ exit
+fi
+
+SUMMARY_LOGNAME=`pmdate -1d %Y%m%d`
+
+_error()
+{
+ _report Error "$1"
+}
+
+_warning()
+{
+ _report Warning "$1"
+}
+
+_report()
+{
+ echo "$prog: $1: $2"
+ echo "[$CONTROL:$line] ... inference engine for host \"$host\" unchanged"
+ touch $tmp/err
+}
+
+_unlock()
+{
+ rm -f lock
+ echo >$tmp/lock
+}
+
+# filter for pmie log files in working directory -
+# pass in the number of days to skip over (backwards) from today
+#
+# pv:821339 too many sed commands for IRIX ... split into groups
+# of at most 200 days
+#
+_date_filter()
+{
+ # start with all files whose names match the patterns used by
+ # the PCP pmie log file management scripts ... this list may be
+ # reduced by the sed filtering later on
+ #
+ ls | sed -n >$tmp/in -e '/[-.][12][0-9][0-9][0-9][0-1][0-9][0-3][0-9]/p'
+
+ i=0
+ while [ $i -le $1 ]
+ do
+ dmax=`expr $i + 200`
+ [ $dmax -gt $1 ] && dmax=$1
+ echo "/[-.][12][0-9][0-9][0-9][0-1][0-9][0-3][0-9]/{" >$tmp/sed1
+ while [ $i -le $dmax ]
+ do
+ x=`pmdate -${i}d %Y%m%d`
+ echo "/[-.]$x/d" >>$tmp/sed1
+ i=`expr $i + 1`
+ done
+ echo "p" >>$tmp/sed1
+ echo "}" >>$tmp/sed1
+
+ # cull file names with matching dates, keep other file names
+ #
+ sed -n -f $tmp/sed1 <$tmp/in >$tmp/tmp
+ mv $tmp/tmp $tmp/in
+ done
+
+ cat $tmp/in
+}
+
+
+rm -f $tmp/err $tmp/mail
+line=0
+version=''
+cat $CONTROL \
+| sed -e "s;PCP_LOG_DIR;$PCP_LOG_DIR;g" \
+| while read host socks logfile args
+do
+ # start in one place for each iteration (beware relative paths)
+ cd "$here"
+ line=`expr $line + 1`
+
+ # NB: FQDN cleanup: substitute the LOCALHOSTNAME marker in the config line
+ # differently for the directory and the pcp -h HOST arguments.
+ logfile_hostname=`hostname || echo localhost`
+ logfile=`echo $logfile | sed -e "s;LOCALHOSTNAME;$logfile_hostname;"`
+ logfile=`_unsymlink_path $logfile`
+ [ "x$host" = "xLOCALHOSTNAME" ] && host=local:
+
+ $VERY_VERBOSE && echo "[control:$line] host=\"$host\" socks=\"$socks\" log=\"$logfile\" args=\"$args\""
+
+ case "$host"
+ in
+ \#*|'') # comment or empty
+ continue
+ ;;
+
+ \$*) # in-line variable assignment
+ $SHOWME && echo "# $host $socks $logfile $args"
+ cmd=`echo "$host $socks $logfile $args" \
+ | sed -n \
+ -e "/='/s/\(='[^']*'\).*/\1/" \
+ -e '/="/s/\(="[^"]*"\).*/\1/' \
+ -e '/=[^"'"'"']/s/[;&<>|].*$//' \
+ -e '/^\\$[A-Za-z][A-Za-z0-9_]*=/{
+s/^\\$//
+s/^\([A-Za-z][A-Za-z0-9_]*\)=/export \1; \1=/p
+}'`
+ if [ -z "$cmd" ]
+ then
+ # in-line command, not a variable assignment
+ _warning "in-line command is not a variable assignment, line ignored"
+ else
+ case "$cmd"
+ in
+ 'export PATH;'*)
+ _warning "cannot change \$PATH, line ignored"
+ ;;
+ 'export IFS;'*)
+ _warning "cannot change \$IFS, line ignored"
+ ;;
+ *)
+ $SHOWME && echo "+ $cmd"
+ eval $cmd
+ ;;
+ esac
+ fi
+ continue
+ ;;
+ esac
+
+ if [ -z "$socks" -o -z "$logfile" -o -z "$args" ]
+ then
+ _error "insufficient fields in control file record"
+ continue
+ fi
+
+ dir=`dirname $logfile`
+ $VERY_VERBOSE && echo "Check pmie -h $host ... in $dir ..."
+
+ if [ ! -d "$dir" ]
+ then
+ if [ "$PMIE_CTL" = "on" ]
+ then
+ _error "logfile directory ($dir) does not exist"
+ fi
+ continue
+ fi
+
+ cd $dir
+ dir=`$PWDCMND`
+ $SHOWME && echo "+ cd $dir"
+
+ if $VERBOSE
+ then
+ echo
+ echo "=== daily maintenance of pmie log files for host $host ==="
+ echo
+ fi
+
+ if [ ! -w $dir ]
+ then
+ echo "$prog: Warning: no write access in $dir, skip lock file processing"
+ else
+ # demand mutual exclusion
+ #
+ fail=true
+ rm -f $tmp/stamp
+ for try in 1 2 3 4
+ do
+ if pmlock -v lock >$tmp/out
+ then
+ echo $dir/lock >$tmp/lock
+ fail=false
+ break
+ else
+ if [ ! -f $tmp/stamp ]
+ then
+ touch -t `pmdate -30M %Y%m%d%H%M` $tmp/stamp
+ fi
+ if [ ! -z "`find lock -newer $tmp/stamp -print 2>/dev/null`" ]
+ then
+ :
+ else
+ echo "$prog: Warning: removing lock file older than 30 minutes"
+ LC_TIME=POSIX ls -l $dir/lock
+ rm -f lock
+ fi
+ fi
+ sleep 5
+ done
+
+ if $fail
+ then
+ # failed to gain mutex lock
+ #
+ if [ -f lock ]
+ then
+ echo "$prog: Warning: is another PCP cron job running concurrently?"
+ LC_TIME=POSIX ls -l $dir/lock
+ else
+ echo "$prog: `cat $tmp/out`"
+ fi
+ _warning "failed to acquire exclusive lock ($dir/lock) ..."
+ continue
+ fi
+ fi
+
+ # match $logfile from control file to running pmies
+ pid=""
+ $VERY_VERBOSE && echo "Looking for logfile=$logfile"
+ for file in `ls "$PCP_TMP_DIR/pmie"`
+ do
+ p_id=$file
+ file="$PCP_TMP_DIR/pmie/$file"
+ p_logfile=""
+ p_pmcd_host=""
+
+ $VERY_VERBOSE && $PCP_ECHO_PROG $PCP_ECHO_N "Check p_id=$p_id ... ""$PCP_ECHO_C"
+ if ps -p "$p_id" >/dev/null 2>&1
+ then
+ eval `$PCP_BINADM_DIR/pmiestatus $file | $PCP_AWK_PROG '
+NR == 2 { printf "p_logfile=\"%s\"\n", $0; next }
+NR == 3 { printf "p_pmcd_host=\"%s\"\n", $0; next }
+ { next }'`
+ $VERY_VERBOSE && $PCP_ECHO_PROG $PCP_ECHO_N "p_pmcd_host=$p_pmcd_host p_logfile=$p_logfile""$PCP_ECHO_C"
+ p_logfile=`_unsymlink_path $p_logfile`
+ $VERY_VERBOSE && $PCP_ECHO_PROG $PCP_ECHO_N "->$p_logfile ... ""$PCP_ECHO_C"
+ if [ "$p_logfile" = $logfile ]
+ then
+ pid=$p_id
+ $VERY_VERBOSE && $PCP_ECHO_PROG match
+ break
+ fi
+ $VERY_VERBOSE && $PCP_ECHO_PROG "no match"
+ else
+ # ignore, its not a running process
+ eval $RM -f $file
+ $VERY_VERBOSE && $PCP_ECHO_PROG "process has vanished"
+ fi
+ done
+
+ if [ -z "$pid" ]
+ then
+ if [ "$PMIE_CTL" = "on" ]
+ then
+ _error "no pmie instance running for host \"$host\""
+ fi
+ else
+ # now move current logfile name aside and SIGHUP to "roll the logs"
+ # creating a new logfile with the old name in the process.
+ #
+ $SHOWME && echo "+ mv $logfile ${logfile}.{SUMMARY_LOGNAME}"
+ if mv $logfile ${logfile}.${SUMMARY_LOGNAME}
+ then
+ $VERY_VERBOSE && echo "+ $KILL -s HUP $pid"
+ eval $KILL -s HUP $pid
+ echo ${logfile}.${SUMMARY_LOGNAME} >> $tmp/mail
+ else
+ _error "problems moving logfile \"$logfile\" for host \"$host\""
+ touch $tmp/err
+ fi
+ fi
+
+ # and cull old logfiles
+ #
+ if [ X"$CULLAFTER" != X"forever" ]
+ then
+ _date_filter $CULLAFTER >$tmp/list
+ if [ -s $tmp/list ]
+ then
+ if $VERBOSE
+ then
+ echo "Log files older than $CULLAFTER days being removed ..."
+ fmt <$tmp/list | sed -e 's/^/ /'
+ fi
+ if $SHOWME
+ then
+ cat $tmp/list | xargs echo + rm -f
+ else
+ cat $tmp/list | xargs rm -f
+ fi
+ fi
+ fi
+
+ # finally, compress old log files
+ # (after cull - don't compress unnecessarily)
+ #
+ if [ ! -z "$COMPRESSAFTER" ]
+ then
+ _date_filter $COMPRESSAFTER | egrep -v "$COMPRESSREGEX" >$tmp/list
+ if [ -s $tmp/list ]
+ then
+ if $VERBOSE
+ then
+ echo "Log files older than $COMPRESSAFTER days being compressed ..."
+ fmt <$tmp/list | sed -e 's/^/ /'
+ fi
+ if $SHOWME
+ then
+ cat $tmp/list | xargs echo + $COMPRESS
+ else
+ cat $tmp/list | xargs $COMPRESS
+ fi
+ fi
+ fi
+
+ _unlock
+
+done
+
+if [ -n "$MAILME" -a -s $tmp/mail ]
+then
+ logs=""
+ for file in `cat $tmp/mail`
+ do
+ [ -f $file ] && logs="$logs $file"
+ done
+ egrep -v '( OK | OK$|^$|^Log |^pmie: PID)' $logs > $tmp/logmail
+ if [ ! -s "$tmp/logmail" ]
+ then
+ :
+ elif [ ! -z "$MAIL" ]
+ then
+ egrep -v '( OK | OK$|^$)' $logs | \
+ $MAIL -s "PMIE summary for $LOCALHOSTNAME" $MAILME
+ else
+ echo "$prog: PMIE summary for $LOCALHOSTNAME ..."
+ egrep -v '( OK | OK$|^$)' $logs
+ fi
+ rm -f $tmp/mail $tmp/logmail
+fi
+
+[ -f $tmp/err ] && status=1
+exit
diff --git a/src/pmie/rc_pmie b/src/pmie/rc_pmie
new file mode 100644
index 0000000..acef291
--- /dev/null
+++ b/src/pmie/rc_pmie
@@ -0,0 +1,273 @@
+#!/bin/sh
+#
+# Copyright (c) 2012 Red Hat.
+# Copyright (c) 2000 Silicon Graphics, Inc. All Rights Reserved.
+#
+# This program is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by the
+# Free Software Foundation; either version 2 of the License, or (at your
+# option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+# for more details.
+#
+# The following is for chkconfig on RedHat based systems
+# chkconfig: 2345 94 06
+# description: pmie is a performance inference engine for the Performance Co-Pilot (PCP)
+#
+# The following is for insserv(1) based systems,
+# e.g. SuSE, where chkconfig is a perl script.
+### BEGIN INIT INFO
+# Provides: pmie
+# Required-Start: $remote_fs
+# Should-Start: $local_fs $network $syslog $time $pmcd
+# Required-Stop: $remote_fs
+# Should-Stop: $local_fs $network $syslog $pmcd
+# Default-Start: 2 3 4 5
+# Default-Stop: 0 1 6
+# Short-Description: Control pmie (performance inference engine for PCP)
+# Description: Configure and control pmie (the performance inference engine for the Performance Co-Pilot)
+### END INIT INFO
+
+. $PCP_DIR/etc/pcp.env
+. $PCP_SHARE_DIR/lib/rc-proc.sh
+
+PMIECTRL=$PCP_PMIECONTROL_PATH
+pmprog=$PCP_RC_DIR/pmie
+prog=$PCP_RC_DIR/`basename $0`
+
+# search for your mail agent of choice ...
+#
+MAIL=''
+for try in Mail mail email
+do
+ if which $try >/dev/null 2>&1
+ then
+ MAIL=$try
+ break
+ fi
+done
+
+tmp=`mktemp -d /var/tmp/pcp.XXXXXXXXX` || exit 1
+status=1
+trap "rm -rf $tmp; exit \$status" 0 1 2 3 15
+
+if [ $pmprog = $prog ]
+then
+ VERBOSE_CTL=on
+else
+ VERBOSE_CTL=off
+fi
+
+id=`id | sed -e "s/(.*//" -e "s/.*=//"`
+if [ "$id" != 0 -a "$1" != "status" ]
+then
+ if [ -n "$PCP_DIR" ]
+ then
+ : running in a non-default installation, do not need to be root
+ else
+ echo "$prog:"'
+Error: You must be root (uid 0) to start or stop pmie.'
+ exit
+ fi
+fi
+
+_usage()
+{
+ echo "Usage: $pmprog [-v] {start|restart|condrestart|stop|status|reload|force-reload}"
+}
+
+_reboot_setup()
+{
+ # base directories and house-keeping for pmie instances
+ #
+ if [ ! -d "$PCP_TMP_DIR/pmie" ]
+ then
+ mkdir -p -m 0775 "$PCP_TMP_DIR/pmie"
+ chown $PCP_USER:$PCP_GROUP "$PCP_TMP_DIR/pmie"
+ else
+ rm -rf $tmp/ent $tmp/pid
+ here=`pwd`
+ cd "$PCP_TMP_DIR/pmie"
+ _get_pids_by_name pmie | sort >$tmp/pid
+ ls [0-9]* 2>&1 | sed -e '/\[0-9]\*/d' \
+ | sed -e 's/[ ][ ]*//g' | sort >$tmp/ent
+ # remove entries without a pmie process
+ rm -f `comm -23 $tmp/ent $tmp/pid`
+ rm -f $tmp/ent $tmp/pid
+ cd "$here"
+ fi
+}
+
+# Note: _start_pmie is running in the background, in parallel with
+# the rest of the script. It might complete well after the caller
+# so tmpfile handling is especially problematic. Goal is to speed
+# bootup by starting potentially slow (remote monitoring) processes
+# in the background.
+#
+_start_pmie()
+{
+ bgstatus=0
+ bgtmp=`mktemp -d /var/tmp/pcp.XXXXXXXXX` || exit 1
+ trap "rm -rf $bgtmp; exit \$bgstatus" 0 1 2 3 15
+
+ wait_option=''
+ [ ! -z "$PMCD_WAIT_TIMEOUT" ] && wait_option="-t $PMCD_WAIT_TIMEOUT"
+
+ if pmcd_wait $wait_option
+ then
+ pmie_check >$bgtmp/pmie 2>&1
+ bgstatus=$?
+ if [ -s $bgtmp/pmie ]
+ then
+ $PCP_BINADM_DIR/pmpost "pmie_check start failed in $prog, mailing output to root"
+ if [ ! -z "$MAIL" ]
+ then
+ $MAIL -s "pmie_check start failed in $prog" root <$bgtmp/pmie >/dev/null 2>&1
+ else
+ echo "$prog: pmie_check start failed ..."
+ cat $bgtmp/pmie
+ fi
+ fi
+ else
+ bgstatus=$?
+ $PCP_BINADM_DIR/pmpost "pmcd_wait failed in $prog: exit status: $bgstatus"
+ if [ ! -z "$MAIL" ]
+ then
+ echo "pmcd_wait: exit status: $bgstatus" | $MAIL -s "pmcd_wait failed in $prog" root
+ else
+ echo "$prog: pmcd_wait failed ..."
+ echo "pmcd_wait: exit status: $bgstatus"
+ fi
+ fi
+ exit $bgstatus # co-process is now complete
+}
+
+_shutdown()
+{
+ _get_pids_by_name pmie >$tmp/pmies 2>&1
+ if [ ! -s $tmp/pmies ]
+ then
+ [ "$1" = verbose ] && echo "$pmprog: PMIE not running"
+ return 0
+ fi
+
+ [ "$1" = quietly ] || \
+ $ECHO $PCP_ECHO_N "Waiting for pmie process(es) to terminate ..." "$PCP_ECHO_C"
+
+ pmie_check -s >$tmp/pmie 2>&1
+ if [ -s $tmp/pmie ]
+ then
+ $PCP_BINADM_DIR/pmpost "pmie_check stop failed in $prog, mailing output to root"
+ if [ ! -z "$MAIL" ]
+ then
+ $MAIL -s "pmie_check stop failed in $prog" root <$tmp/pmie
+ else
+ echo "$prog: pmie_check stop failed ..."
+ cat $tmp/pmie
+ fi
+ fi
+
+ true
+ $RC_STATUS -v
+ rm -fr "$tmp/pmie" "$PCP_TMP_DIR"/pmie/*
+ $PCP_BINADM_DIR/pmpost "stop pmie from $pmprog"
+}
+
+while getopts v c
+do
+ case $c
+ in
+ v) # force verbose
+ VERBOSE_CTL=on
+ ;;
+ *)
+ _usage
+ exit 1
+ ;;
+ esac
+done
+shift `expr $OPTIND - 1`
+
+if [ $VERBOSE_CTL = on ]
+then # For a verbose startup and shutdown
+ ECHO=$PCP_ECHO_PROG
+else # For a quiet startup and shutdown
+ ECHO=:
+fi
+
+case "$1" in
+ 'start'|'restart'|'condrestart'|'reload'|'force-reload')
+ if [ "$1" = "condrestart" ] && ! is_chkconfig_on pmie
+ then
+ status=0
+ exit
+ fi
+
+ _shutdown quietly
+
+ # messages should go to stderr, not the GUI notifiers
+ #
+ unset PCP_STDERR
+
+ _reboot_setup
+
+ if which pmie >/dev/null 2>&1
+ then
+ if is_chkconfig_on pmie
+ then
+ if which pmie_check >/dev/null 2>&1
+ then
+ if [ ! -f $PMIECTRL ]
+ then
+ echo "$prog:"'
+Error: PCP inference engine control file '$PMIECTRL'
+ is missing! Cannot start any Performance Co-Pilot inference engine(s).'
+ else
+ $ECHO $PCP_ECHO_N "Starting pmie ..." "$PCP_ECHO_C"
+ _start_pmie &
+ $RC_STATUS -v
+ fi
+ fi
+ elif [ "$0" = "$PCP_RC_DIR/pmie" ]
+ then
+ echo "$prog: Warning: Performance Co-Pilot Inference Engine (pmie) is disabled."
+ chkconfig_on_msg pmie
+ fi
+ fi
+ status=0
+ ;;
+
+ 'stop')
+ _shutdown verbose
+ status=0
+ ;;
+
+ 'status')
+ # NOTE: $RC_CHECKPROC returns LSB compliant status values.
+ $ECHO $PCP_ECHO_N "Checking for pmie:" "$PCP_ECHO_C"
+ if [ -r /etc/rc.status ]
+ then
+ # SuSE
+ $RC_CHECKPROC pmie
+ $RC_STATUS -v
+ status=$?
+ else
+ # not SuSE
+ $RC_CHECKPROC pmie
+ status=$?
+ if [ $status -eq 0 ]
+ then
+ $ECHO running
+ else
+ $ECHO stopped
+ fi
+ fi
+ ;;
+ *)
+ _usage
+ ;;
+esac
+
diff --git a/src/pmie/src/GNUmakefile b/src/pmie/src/GNUmakefile
new file mode 100644
index 0000000..86984bb
--- /dev/null
+++ b/src/pmie/src/GNUmakefile
@@ -0,0 +1,79 @@
+#
+# 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
+
+TARGET = pmie$(EXECSUFFIX)
+
+CFILES = pmie.c symbol.c dstruct.c lexicon.c syntax.c pragmatics.c eval.c \
+ show.c match_inst.c systemlog.c stomp.c andor.c
+
+HFILES = fun.h dstruct.h eval.h lexicon.h pragmatics.h stats.h \
+ show.h symbol.h syntax.h systemlog.h stomp.h andor.h
+
+SKELETAL = hdr.sk fetch.sk misc.sk aggregate.sk unary.sk binary.sk \
+ merge.sk act.sk
+
+LSRCFILES = $(SKELETAL) meta logger.h
+
+LDIRT += $(YFILES:%.y=%.tab.?) fun.c fun.o $(TARGET) grammar.h
+
+LLDLIBS = $(PCPLIB) $(LIB_FOR_MATH) $(LIB_FOR_REGEX)
+
+LCFLAGS += $(PIECFLAGS)
+LLDFLAGS += $(PIELDFLAGS)
+
+default: $(TARGET)
+
+YFILES = grammar.y
+
+.NOTPARALLEL:
+grammar.h grammar.tab.c: grammar.y
+ $(YACC) -d -b `basename $< .y` $< && cp `basename $@ .h`.tab.h $@
+
+pmie$(EXECSUFFIX): $(OBJECTS) fun.o
+ $(CCF) -o $@ $(LDFLAGS) $(OBJECTS) fun.o $(LDLIBS)
+
+install: default
+ $(INSTALL) -m 755 $(TARGET) $(PCP_BIN_DIR)/$(TARGET)
+
+lexicon.o syntax.o: grammar.h
+
+fun.o: fun.h
+
+fun.c: $(SKELETAL) meta
+ @echo $@
+ ./meta
+
+include $(BUILDRULES)
+
+default_pcp: default
+
+install_pcp: install
+
+fun.h: andor.h
+andor.o dstruct.o eval.o fun.o grammar.tab.o lexicon.o match_inst.o pmie.o pragmatics.o show.o syntax.o systemlog.o: dstruct.h
+dstruct.o eval.o pmie.o pragmatics.o syntax.o systemlog.o: eval.h
+andor.o dstruct.o eval.o fun.o match_inst.o: fun.h
+lexicon.o syntax.o: grammar.h
+grammar.tab.o lexicon.o show.o syntax.o: lexicon.h
+systemlog.o: logger.h
+andor.o dstruct.o eval.o fun.o grammar.tab.o lexicon.o pmie.o pragmatics.o show.o syntax.o: pragmatics.h
+andor.o dstruct.o eval.o fun.o grammar.tab.o match_inst.o pmie.o show.o syntax.o: show.h
+andor.o fun.o grammar.tab.o pmie.o stomp.o: stomp.h
+andor.o dstruct.o eval.o fun.o grammar.tab.o lexicon.o match_inst.o pmie.o pragmatics.o show.o symbol.o syntax.o systemlog.o: symbol.h
+grammar.tab.o lexicon.o pmie.o syntax.o systemlog.o: syntax.h
+fun.o grammar.tab.o systemlog.o: systemlog.h
+
diff --git a/src/pmie/src/README.DEBUG b/src/pmie/src/README.DEBUG
new file mode 100644
index 0000000..52eca13
--- /dev/null
+++ b/src/pmie/src/README.DEBUG
@@ -0,0 +1,19 @@
+Notes to assist in debugging pmie
+
+pmie command line debug flags
+ APPL0 - lexical scanning
+ APPL1 - parse/expression tree construction
+ APPL2 - expression execution
+
+macro EVALARG(x) expands to
+ if ((x)->op < NOP) ((x)->eval)(x);
+
+The source file fun.c is generated by expansion of all of the *.sk
+"skeletal" files ... so changes need to be made in the *.sk files and
+fun.c will be recreated in the make.
+
+The Expr structure (defined in dstruct.h) is at the core of the pmie
+expression evaluation.
+ valid must be > 0 for there to be any values able to be
+ used in the expression evaluation
+
diff --git a/src/pmie/src/act.sk b/src/pmie/src/act.sk
new file mode 100644
index 0000000..358ef53
--- /dev/null
+++ b/src/pmie/src/act.sk
@@ -0,0 +1,376 @@
+/*
+ * Copyright (c) 1995-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.
+ *
+ * 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
+ */
+
+/***********************************************************************
+ * skeleton: act.sk - actions
+ *
+ ***********************************************************************/
+
+/*
+ * operator: actAnd
+ */
+void
+actAnd(Expr *x)
+{
+ Expr *arg1 = x->arg1;
+ Expr *arg2 = x->arg2;
+
+ EVALARG(arg1)
+ EVALARG(arg2)
+ *(Boolean *)x->ring = (*(Boolean *)arg1->ring == B_TRUE) && (*(Boolean *)arg2->ring == B_TRUE);
+}
+
+
+/*
+ * operator: actOr
+ */
+void
+actOr(Expr *x)
+{
+ Expr *arg1 = x->arg1;
+ Expr *arg2 = x->arg2;
+
+ EVALARG(arg1)
+ if (*(Boolean *)arg1->ring == B_FALSE) {
+ EVALARG(arg2)
+ *(Boolean *)x->ring = *(Boolean *)arg2->ring;
+ }
+ else *(Boolean *)x->ring = B_TRUE;
+}
+
+
+/*
+ * operator: actShell
+ */
+void
+actShell(Expr *x)
+{
+ Expr *arg1 = x->arg1;
+ Expr *arg2 = x->arg2;
+#ifndef IS_MINGW
+ pid_t pid;
+#endif
+ int sts;
+
+ if ((arg2 == NULL) ||
+ (x->smpls[0].stamp == 0) ||
+ (now >= *(RealTime *)arg2->ring + x->smpls[0].stamp))
+ {
+ EVALARG(arg1)
+ fflush(stdout);
+ fflush(stderr);
+#ifdef IS_MINGW
+ putenv("IFS=\t\n");
+ sts = system((char *)arg1->ring);
+ need_wait = 1;
+ if (sts < 0) {
+ __pmNotifyErr(LOG_ERR, "spawn for shell failed\n");
+ *(Boolean *)x->ring = B_FALSE;
+ }
+ else {
+ *(Boolean *)x->ring = B_TRUE;
+ x->smpls[0].stamp = now;
+ x->valid = 0;
+ }
+#else /*POSIX*/
+ pid = fork();
+ if (pid == 0) {
+ /* child, run the command */
+ setsid();
+ putenv("PATH=/usr/sbin:/sbin:/usr/bin:/bin");
+ putenv("IFS=\t\n");
+ sts = system((char *)arg1->ring);
+ _exit(WEXITSTATUS(sts)); /* avoid atexit() handler */
+ }
+ else if (pid > 0) {
+ /* parent, wait for child to exit to catch status */
+#if PCP_DEBUG
+ if (pmDebug & DBG_TRACE_APPL2) {
+ fprintf(stderr, "actShell: fork: pid=%" FMT_PID "\n", pid);
+ }
+#endif
+ sts = waitpid(pid, &x->valid, 0);
+#if PCP_DEBUG
+ if (pmDebug & DBG_TRACE_APPL2) {
+ fprintf(stderr, "actShell: wait: pid=%" FMT_PID " status=0x%x", pid, x->valid);
+ if (WIFEXITED(x->valid))
+ fprintf(stderr, " exit=%d", WEXITSTATUS(x->valid));
+ if (WIFSIGNALED(x->valid))
+ fprintf(stderr, " signal=%d", WTERMSIG(x->valid));
+ fprintf(stderr, " (wait returns %d)\n", sts);
+ }
+#endif
+ if (WIFEXITED(x->valid))
+ x->valid = WEXITSTATUS(x->valid);
+ else
+ /* if no exit, then assume non-zero exit, hence failure! */
+ x->valid = 1;
+ if (sts < 0 || x->valid != 0)
+ *(Boolean *)x->ring = B_FALSE;
+ else
+ *(Boolean *)x->ring = B_TRUE;
+ x->smpls[0].stamp = now;
+ }
+ else {
+ __pmNotifyErr(LOG_ERR, "fork for shell failed\n");
+ *(Boolean *)x->ring = B_FALSE;
+ }
+#endif
+ perf->actions++;
+ }
+}
+
+
+/*
+ * operator: actAlarm
+ */
+void
+actAlarm(Expr *x)
+{
+ static char *alarmv[] = {
+ NULL, /* path to PCP_XCONFIRM_PROG inserted here */
+ "-header", "Performance Co-Pilot Alarm",
+ "-b", "Cancel",
+ "-icon", "warning",
+ "-t", NULL,
+ "-t", NULL,
+ NULL};
+
+ char ctime[26];
+ Expr *arg1 = x->arg1;
+ Expr *arg2 = x->arg2;
+ time_t clock;
+ int sts;
+
+ if (alarmv[0] == NULL) {
+ /*
+ * one trip to get path for xconfirm(1)
+ */
+ alarmv[0] = pmGetConfig("PCP_XCONFIRM_PROG");
+ if (strcmp(alarmv[0], "") == 0) {
+ __pmNotifyErr(LOG_ERR, "PCP_XCONFIRM_PROG not found, using echo(1)\n");
+ alarmv[0] = "/bin/echo";
+ }
+ }
+
+#ifndef IS_MINGW
+ /* if old alarm still active, don't post new one */
+ if (x->valid != 0) {
+ pid_t pid;
+ pid = waitpid((pid_t)x->valid, &sts, WNOHANG);
+ if (pid <= 0) {
+#if PCP_DEBUG
+ if (pmDebug & DBG_TRACE_APPL2) {
+ fprintf(stderr, "actAlarm: wait: pid=%d not done (wait returns %" FMT_PID ")\n", x->valid, pid);
+ }
+#endif
+ return;
+ }
+#if PCP_DEBUG
+ if (pmDebug & DBG_TRACE_APPL2) {
+ fprintf(stderr, "actAlarm: wait: pid=%d done status=0x%x", x->valid, sts);
+ if (WIFEXITED(sts))
+ fprintf(stderr, " exit=%d", WEXITSTATUS(sts));
+ if (WIFSIGNALED(sts))
+ fprintf(stderr, " signal=%d", WTERMSIG(sts));
+ fprintf(stderr, " (wait returns %" FMT_PID ")\n", pid);
+ }
+#endif
+ x->valid = 0;
+ }
+#endif
+
+ if ((arg2 == NULL) ||
+ (x->smpls[0].stamp == 0) ||
+ (now >= *(RealTime *)arg2->ring + x->smpls[0].stamp))
+ {
+ EVALARG(arg1)
+ clock = (time_t)(now+0.5);
+ pmCtime(&clock, ctime);
+#ifdef IS_MINGW
+ alarmv[8] = ctime;
+ alarmv[10] = (char *)arg1->ring;
+ sts = spawnvp(_P_DETACH, alarmv[0], (const char * const *)alarmv);
+ if (sts < 0) {
+ __pmNotifyErr(LOG_ERR, "spawn for PCP_XCONFIRM_PROG failed\n");
+ *(Boolean *)x->ring = B_FALSE;
+ }
+ else {
+ *(Boolean *)x->ring = B_TRUE;
+ x->smpls[0].stamp = now;
+ x->valid = 0;
+ }
+#else
+ sts = fork();
+ if (sts == 0) {
+ alarmv[8] = ctime;
+ alarmv[10] = (char *)arg1->ring;
+ setsid();
+ if (strcmp(alarmv[0], "/bin/echo") != 0) {
+ /* only echo needs stdio, when xconfirm cannot be found */
+ fclose(stdin);
+ fclose(stdout);
+ fclose(stderr);
+ }
+ execvp(alarmv[0], alarmv);
+ _exit(1); /* avoid atexit() handler */
+ }
+ else if (sts > 0) {
+ need_wait = 1;
+#if PCP_DEBUG
+ if (pmDebug & DBG_TRACE_APPL2) {
+ fprintf(stderr, "actAlarm: fork: pid=%d\n", sts);
+ }
+#endif
+ x->valid = sts;
+ *(Boolean *)x->ring = B_TRUE;
+ x->smpls[0].stamp = now;
+ }
+ else {
+ __pmNotifyErr(LOG_ERR, "fork for alarm failed\n");
+ *(Boolean *)x->ring = B_FALSE;
+ }
+#endif
+ perf->actions++;
+ }
+}
+
+
+/*
+ * operator: actSyslog
+ */
+void
+actSyslog(Expr *x)
+{
+ Expr *arg1 = x->arg1;
+ Expr *arg2 = x->arg2;
+ int *pri;
+ char *tag;
+
+ if ((arg2 == NULL) ||
+ (x->smpls[0].stamp == 0) ||
+ (now >= *(RealTime *)arg2->ring + x->smpls[0].stamp))
+ {
+ pri = (int *)arg1->arg2->ring;
+ tag = &((char *)arg1->arg2->ring)[sizeof(int)];
+ EVALARG(arg1)
+ openlog(tag, LOG_PID|LOG_CONS, LOG_DAEMON);
+ if (arg1->ring == NULL)
+ syslog(*pri, "%s", "");
+ else
+ syslog(*pri, "%s", (char *)arg1->ring);
+ closelog();
+ *(Boolean *)x->ring = B_TRUE;
+ x->smpls[0].stamp = now;
+ perf->actions++;
+ }
+}
+
+
+/*
+ * operator: actPrint
+ */
+void
+actPrint(Expr *x)
+{
+ Expr *arg1 = x->arg1;
+ Expr *arg2 = x->arg2;
+ time_t clock = (time_t)now;
+ char bfr[26];
+
+ if ((arg2 == NULL) ||
+ (x->smpls[0].stamp == 0) ||
+ (now >= *(RealTime *)arg2->ring + x->smpls[0].stamp))
+ {
+ EVALARG(arg1)
+ *(Boolean *)x->ring = B_TRUE;
+ x->smpls[0].stamp = now;
+ pmCtime(&clock, bfr);
+ bfr[24] = '\0';
+ printf("%s: %s\n", bfr, (char *)arg1->ring);
+ fflush(stdout);
+ perf->actions++;
+ }
+}
+
+
+/*
+ * operator: actStomp
+ */
+void
+actStomp(Expr *x)
+{
+ Expr *arg1 = x->arg1;
+ Expr *arg2 = x->arg2;
+
+ if ((arg2 == NULL) ||
+ (x->smpls[0].stamp == 0) ||
+ (now >= *(RealTime *)arg2->ring + x->smpls[0].stamp))
+ {
+ EVALARG(arg1)
+ x->smpls[0].stamp = now;
+ if (stompSend((const char *)arg1->ring) != 0)
+ *(Boolean *)x->ring = B_FALSE;
+ else
+ *(Boolean *)x->ring = B_TRUE;
+ perf->actions++;
+ }
+}
+
+
+/*
+ * action argument handling ... including %h, %v and %i substitution
+ */
+void
+actArg(Expr *x)
+{
+ Expr *sp = x->arg1;
+ char *string = (char *)0;
+ size_t length = 0;
+
+ for (sp = x->arg1; sp != NULL; sp = sp->arg1)
+ length = formatSatisfyingValue((char *)sp->ring, length, &string);
+
+ newStringBfr(x, length, string);
+}
+
+
+/*
+ * fake actions for archive mode
+ */
+void
+actFake(Expr *x)
+{
+ Expr *arg1 = x->arg1;
+ Expr *arg2 = x->arg2;
+ time_t clock = (time_t)now;
+ char bfr[26];
+
+ if ((arg2 == NULL) ||
+ (x->smpls[0].stamp == 0) ||
+ (now >= *(RealTime *)arg2->ring + x->smpls[0].stamp))
+ {
+ EVALARG(arg1)
+ *(Boolean *)x->ring = B_TRUE;
+ x->smpls[0].stamp = now;
+ pmCtime(&clock, bfr);
+ bfr[24] = '\0';
+ printf("%s %s: %s\n", opStrings(x->op), bfr, (char *)arg1->ring);
+ }
+}
+
diff --git a/src/pmie/src/aggregate.sk b/src/pmie/src/aggregate.sk
new file mode 100644
index 0000000..98bf4c0
--- /dev/null
+++ b/src/pmie/src/aggregate.sk
@@ -0,0 +1,176 @@
+/*
+ * Copyright (c) 1995-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.
+ *
+ * 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
+ */
+
+/***********************************************************************
+ * skeleton: aggregate.sk - aggregation and quantification
+ ***********************************************************************/
+
+/***********************************************************************
+ * operator: @FUN
+ ***********************************************************************/
+
+void
+@FUN_host(Expr *x)
+{
+ Expr *arg1 = x->arg1;
+ Sample *is = &arg1->smpls[0];
+ Sample *os = &x->smpls[0];
+ @ITYPE *ip;
+ @OTYPE *op;
+ @TTYPE a;
+ int n;
+ int i;
+
+ EVALARG(arg1)
+ ROTATE(x)
+
+ if (arg1->valid && arg1->hdom > 0) {
+ ip = (@ITYPE *)is->ptr;
+ op = (@OTYPE *)os->ptr;
+ n = arg1->hdom;
+ @TOP
+ for (i = 1; i < n; i++) {
+ ip++;
+ @LOOP
+ }
+ @BOT
+ os->stamp = is->stamp;
+ x->valid++;
+ }
+ else {
+ x->valid = 0;
+ }
+
+#if PCP_DEBUG
+ if (pmDebug & DBG_TRACE_APPL2) {
+ fprintf(stderr, "@FUN_host(" PRINTF_P_PFX "%p) ...\n", x);
+ dumpExpr(x);
+ }
+#endif
+}
+
+void
+@FUN_inst(Expr *x)
+{
+ Expr *arg1 = x->arg1;
+ Sample *is = &arg1->smpls[0];
+ Sample *os = &x->smpls[0];
+ @ITYPE *ip;
+ @OTYPE *op;
+ @TTYPE a;
+ Metric *m;
+ int n;
+ int i, j;
+
+ EVALARG(arg1)
+ ROTATE(x)
+
+ if (arg1->valid && x->hdom != 0) {
+ ip = (@ITYPE *)is->ptr;
+ op = (@OTYPE *)os->ptr;
+ if (abs(x->hdom) == 1) {
+ n = arg1->e_idom;
+ if (n < 1) {
+ @NOTVALID
+ goto done;
+ }
+ @TOP
+ for (i = 1; i < n; i++) {
+ ip++;
+ @LOOP
+ }
+ @BOT
+ }
+ else {
+ m = x->metrics;
+ for (i = 0; i < x->hdom; i++) {
+ n = m->m_idom;
+ if (n < 1) {
+ @NOTVALID
+ goto done;
+ }
+ @TOP
+ for (j = 1; j < n; j++){
+ /* Note! no break allowed in this loop */
+ ip++;
+ @LOOP
+ }
+ @BOT
+ m++;
+ }
+ }
+ os->stamp = is->stamp;
+ x->valid++;
+ }
+ else {
+ x->valid = 0;
+ }
+
+done:
+#if PCP_DEBUG
+ if (pmDebug & DBG_TRACE_APPL2) {
+ fprintf(stderr, "@FUN_inst(" PRINTF_P_PFX "%p) ...\n", x);
+ dumpExpr(x);
+ }
+#endif
+}
+
+void
+@FUN_time(Expr *x)
+{
+ Expr *arg1 = x->arg1;
+ Sample *is = &arg1->smpls[0];
+ Sample *os = &x->smpls[0];
+ @ITYPE *ring = (@ITYPE *)arg1->ring;
+ @ITYPE *ip;
+ @OTYPE *op;
+ @TTYPE a;
+ int n = arg1->tdom;
+ int tspan;
+ int i, j;
+
+ EVALARG(arg1)
+ ROTATE(x)
+
+ if (arg1->valid >= n && x->tspan > 0 && arg1->tdom > 0) {
+ op = (@OTYPE *)os->ptr;
+ tspan = x->tspan;
+ for (i = 0; i < tspan; i++) {
+ ip = ring + i;
+ @TOP
+ for (j = 1; j < n; j++){
+ ip += tspan;
+ @LOOP
+ }
+ @BOT
+ }
+ os->stamp = is->stamp;
+ x->valid++;
+ }
+ else {
+ x->valid = 0;
+ }
+
+#if PCP_DEBUG
+ if (pmDebug & DBG_TRACE_APPL2) {
+ fprintf(stderr, "@FUN_time(" PRINTF_P_PFX "%p) ...\n", x);
+ dumpExpr(x);
+ }
+#endif
+}
+
diff --git a/src/pmie/src/andor.c b/src/pmie/src/andor.c
new file mode 100644
index 0000000..d0e2696
--- /dev/null
+++ b/src/pmie/src/andor.c
@@ -0,0 +1,457 @@
+/*
+ * Copyright (c) 1995-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.
+ *
+ * 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
+ */
+
+/***********************************************************************
+ * andor.c
+ *
+ * These functions were originally generated from skeletons <file>.sk
+ * by the shell-script './meta', then modified to support the semantics
+ * of the boolean AND/OR operators correctly. These are different to
+ * every other operator in that they do not always require both sides
+ * of the expression to be available in order to be evaluated, i.e.
+ * OR: if either side of the expression is true, expr is true
+ * AND: if either side of the expression is false, expr is false
+ ***********************************************************************/
+
+#include "pmapi.h"
+#include "dstruct.h"
+#include "pragmatics.h"
+#include "fun.h"
+#include "show.h"
+#include "stomp.h"
+
+
+/*
+ * operator: cndOr
+ */
+
+#define OR(x,y) (((x) == B_TRUE || (y) == B_TRUE) ? B_TRUE : (((x) == B_FALSE && (y) == B_FALSE) ? B_FALSE : B_UNKNOWN))
+#define OR1(x) ((x) == B_TRUE ? B_TRUE : B_UNKNOWN)
+
+void
+cndOr_n_n(Expr *x)
+{
+ Expr *arg1 = x->arg1;
+ Expr *arg2 = x->arg2;
+ Sample *is1 = &arg1->smpls[0];
+ Sample *is2 = &arg2->smpls[0];
+ Sample *os = &x->smpls[0];
+ Boolean *ip1;
+ Boolean *ip2;
+ Boolean *op;
+ int i;
+
+ EVALARG(arg1)
+ EVALARG(arg2)
+ ROTATE(x)
+
+ if (arg1->valid && arg2->valid && x->tspan > 0) {
+ ip1 = (Boolean *)is1->ptr;
+ ip2 = (Boolean *)is2->ptr;
+ op = (Boolean *)os->ptr;
+ for (i = 0; i < x->tspan; i++) {
+ *op++ = OR(*ip1, *ip2);
+ ip1++;
+ ip2++;
+ }
+ os->stamp = (is1->stamp > is2->stamp) ? is1->stamp : is2->stamp;
+ x->valid++;
+ }
+ else if (arg1->valid && x->tspan > 0) {
+ ip1 = (Boolean *)is1->ptr;
+ op = (Boolean *)os->ptr;
+ for (i = 0; i < x->tspan; i++) {
+ *op++ = OR1(*ip1);
+ ip1++;
+ }
+ os->stamp = is1->stamp;
+ x->valid++;
+ }
+ else if (arg2->valid && x->tspan > 0) {
+ ip2 = (Boolean *)is2->ptr;
+ op = (Boolean *)os->ptr;
+ for (i = 0; i < x->tspan; i++) {
+ *op++ = OR1(*ip2);
+ ip2++;
+ }
+ os->stamp = is2->stamp;
+ x->valid++;
+ }
+ else x->valid = 0;
+
+#if PCP_DEBUG
+ if (pmDebug & DBG_TRACE_APPL2) {
+ fprintf(stderr, "cndOr_n_n(" PRINTF_P_PFX "%p) ...\n", x);
+ dumpExpr(x);
+ }
+#endif
+}
+
+void
+cndOr_n_1(Expr *x)
+{
+ Expr *arg1 = x->arg1;
+ Expr *arg2 = x->arg2;
+ Sample *is1 = &arg1->smpls[0];
+ Sample *is2 = &arg2->smpls[0];
+ Sample *os = &x->smpls[0];
+ Boolean *ip1;
+ Boolean iv2;
+ Boolean *op;
+ int i;
+
+ EVALARG(arg1)
+ EVALARG(arg2)
+ ROTATE(x)
+
+ if (arg1->valid && arg2->valid && x->tspan > 0) {
+ ip1 = (Boolean *)is1->ptr;
+ iv2 = *(Boolean *)is2->ptr;
+ op = (Boolean *)os->ptr;
+ for (i = 0; i < x->tspan; i++) {
+ *op++ = OR(*ip1, iv2);
+ ip1++;
+ }
+ os->stamp = (is1->stamp > is2->stamp) ? is1->stamp : is2->stamp;
+ x->valid++;
+ }
+ else if (arg1->valid && x->tspan > 0) {
+ ip1 = (Boolean *)is1->ptr;
+ op = (Boolean *)os->ptr;
+ for (i = 0; i < x->tspan; i++) {
+ *op++ = OR1(*ip1);
+ ip1++;
+ }
+ os->stamp = is1->stamp;
+ x->valid++;
+ }
+ else if (arg2->valid && x->tspan > 0) {
+ *(Boolean *)os->ptr = OR1(*(Boolean *)is2->ptr);
+ os->stamp = is2->stamp;
+ x->valid++;
+ }
+ else x->valid = 0;
+
+#if PCP_DEBUG
+ if (pmDebug & DBG_TRACE_APPL2) {
+ fprintf(stderr, "cndOr_n_1(" PRINTF_P_PFX "%p) ...\n", x);
+ dumpExpr(x);
+ }
+#endif
+}
+
+void
+cndOr_1_n(Expr *x)
+{
+ Expr *arg1 = x->arg1;
+ Expr *arg2 = x->arg2;
+ Sample *is1 = &arg1->smpls[0];
+ Sample *is2 = &arg2->smpls[0];
+ Sample *os = &x->smpls[0];
+ Boolean iv1;
+ Boolean *ip2;
+ Boolean *op;
+ int i;
+
+ EVALARG(arg1)
+ EVALARG(arg2)
+ ROTATE(x)
+
+ if (arg1->valid && arg2->valid && x->tspan > 0) {
+ iv1 = *(Boolean *)is1->ptr;
+ ip2 = (Boolean *)is2->ptr;
+ op = (Boolean *)os->ptr;
+ for (i = 0; i < x->tspan; i++) {
+ *op++ = OR(iv1, *ip2);
+ ip2++;
+ }
+ os->stamp = (is1->stamp > is2->stamp) ? is1->stamp : is2->stamp;
+ x->valid++;
+ }
+ else if (arg1->valid && x->tspan > 0) {
+ *(Boolean *)os->ptr = OR1(*(Boolean *)is1->ptr);
+ os->stamp = is1->stamp;
+ x->valid++;
+ }
+ else if (arg2->valid && x->tspan > 0) {
+ ip2 = (Boolean *)is2->ptr;
+ op = (Boolean *)os->ptr;
+ for (i = 0; i < x->tspan; i++) {
+ *op++ = OR1(*ip2);
+ ip2++;
+ }
+ os->stamp = is2->stamp;
+ x->valid++;
+ }
+ else x->valid = 0;
+
+#if PCP_DEBUG
+ if (pmDebug & DBG_TRACE_APPL2) {
+ fprintf(stderr, "cndOr_1_n(" PRINTF_P_PFX "%p) ...\n", x);
+ dumpExpr(x);
+ }
+#endif
+}
+
+void
+cndOr_1_1(Expr *x)
+{
+ Expr *arg1 = x->arg1;
+ Expr *arg2 = x->arg2;
+ Sample *is1 = &arg1->smpls[0];
+ Sample *is2 = &arg2->smpls[0];
+ Sample *os = &x->smpls[0];
+
+ EVALARG(arg1)
+ EVALARG(arg2)
+ ROTATE(x)
+
+ if (arg1->valid && arg2->valid) {
+ *(Boolean *)os->ptr = OR(*(Boolean *)is1->ptr, *(Boolean *)is2->ptr);
+ os->stamp = (is1->stamp > is2->stamp) ? is1->stamp : is2->stamp;
+ x->valid++;
+ }
+ else if (arg1->valid) {
+ *(Boolean *)os->ptr = OR1(*(Boolean *)is1->ptr);
+ os->stamp = is1->stamp;
+ x->valid++;
+ }
+ else if (arg2->valid) {
+ *(Boolean *)os->ptr = OR1(*(Boolean *)is2->ptr);
+ os->stamp = is2->stamp;
+ x->valid++;
+ }
+ else x->valid = 0;
+
+#if PCP_DEBUG
+ if (pmDebug & DBG_TRACE_APPL2) {
+ fprintf(stderr, "cndOr_1_1(" PRINTF_P_PFX "%p) ...\n", x);
+ dumpExpr(x);
+ }
+#endif
+}
+
+/*
+ * operator: cndAnd
+ */
+
+#define AND(x,y) (((x) == B_TRUE && (y) == B_TRUE) ? B_TRUE : (((x) == B_FALSE || (y) == B_FALSE) ? B_FALSE : B_UNKNOWN))
+#define AND1(x) (((x) == B_FALSE) ? B_FALSE : B_UNKNOWN)
+
+void
+cndAnd_n_n(Expr *x)
+{
+ Expr *arg1 = x->arg1;
+ Expr *arg2 = x->arg2;
+ Sample *is1 = &arg1->smpls[0];
+ Sample *is2 = &arg2->smpls[0];
+ Sample *os = &x->smpls[0];
+ Boolean *ip1;
+ Boolean *ip2;
+ Boolean *op;
+ int i;
+
+ EVALARG(arg1)
+ EVALARG(arg2)
+ ROTATE(x)
+
+ if (arg1->valid && arg2->valid) {
+ ip1 = (Boolean *)is1->ptr;
+ ip2 = (Boolean *)is2->ptr;
+ op = (Boolean *)os->ptr;
+ for (i = 0; i < x->tspan; i++) {
+ *op++ = AND(*ip1, *ip2);
+ ip1++;
+ ip2++;
+ }
+ os->stamp = (is1->stamp > is2->stamp) ? is1->stamp : is2->stamp;
+ x->valid++;
+ }
+ else if (arg1->valid) {
+ ip1 = (Boolean *)is1->ptr;
+ op = (Boolean *)os->ptr;
+ for (i = 0; i < x->tspan; i++) {
+ *op++ = AND1(*ip1);
+ ip1++;
+ }
+ os->stamp = is1->stamp;
+ x->valid++;
+ }
+ else if (arg2->valid) {
+ ip2 = (Boolean *)is2->ptr;
+ op = (Boolean *)os->ptr;
+ for (i = 0; i < x->tspan; i++) {
+ *op++ = AND1(*ip2);
+ ip2++;
+ }
+ os->stamp = is2->stamp;
+ x->valid++;
+ }
+ else x->valid = 0;
+
+#if PCP_DEBUG
+ if (pmDebug & DBG_TRACE_APPL2) {
+ fprintf(stderr, "cndAnd_n_n(" PRINTF_P_PFX "%p) ...\n", x);
+ dumpExpr(x);
+ }
+#endif
+}
+
+void
+cndAnd_n_1(Expr *x)
+{
+ Expr *arg1 = x->arg1;
+ Expr *arg2 = x->arg2;
+ Sample *is1 = &arg1->smpls[0];
+ Sample *is2 = &arg2->smpls[0];
+ Sample *os = &x->smpls[0];
+ Boolean *ip1;
+ Boolean iv2;
+ Boolean *op;
+ int i;
+
+ EVALARG(arg1)
+ EVALARG(arg2)
+ ROTATE(x)
+
+ if (arg1->valid && arg2->valid && x->tspan > 0) {
+ ip1 = (Boolean *)is1->ptr;
+ iv2 = *(Boolean *)is2->ptr;
+ op = (Boolean *)os->ptr;
+ for (i = 0; i < x->tspan; i++) {
+ *op++ = AND(*ip1, iv2);
+ ip1++;
+ }
+ os->stamp = (is1->stamp > is2->stamp) ? is1->stamp : is2->stamp;
+ x->valid++;
+ }
+ else if (arg1->valid && x->tspan > 0) {
+ ip1 = (Boolean *)is1->ptr;
+ op = (Boolean *)os->ptr;
+ for (i = 0; i < x->tspan; i++) {
+ *op++ = AND1(*ip1);
+ ip1++;
+ }
+ os->stamp = is1->stamp;
+ x->valid++;
+ }
+ else if (arg2->valid && x->tspan > 0) {
+ *(Boolean *)os->ptr = AND1(*(Boolean *)is2->ptr);
+ os->stamp = is2->stamp;
+ x->valid++;
+ }
+ else x->valid = 0;
+
+#if PCP_DEBUG
+ if (pmDebug & DBG_TRACE_APPL2) {
+ fprintf(stderr, "cndAnd_n_1(" PRINTF_P_PFX "%p) ...\n", x);
+ dumpExpr(x);
+ }
+#endif
+}
+
+void
+cndAnd_1_n(Expr *x)
+{
+ Expr *arg1 = x->arg1;
+ Expr *arg2 = x->arg2;
+ Sample *is1 = &arg1->smpls[0];
+ Sample *is2 = &arg2->smpls[0];
+ Sample *os = &x->smpls[0];
+ Boolean iv1;
+ Boolean *ip2;
+ Boolean *op;
+ int i;
+
+ EVALARG(arg1)
+ EVALARG(arg2)
+ ROTATE(x)
+
+ if (arg1->valid && arg2->valid && x->tspan > 0) {
+ iv1 = *(Boolean *)is1->ptr;
+ ip2 = (Boolean *)is2->ptr;
+ op = (Boolean *)os->ptr;
+ for (i = 0; i < x->tspan; i++) {
+ *op++ = AND(iv1, *ip2);
+ ip2++;
+ }
+ os->stamp = (is1->stamp > is2->stamp) ? is1->stamp : is2->stamp;
+ x->valid++;
+ }
+ else if (arg1->valid && x->tspan > 0) {
+ *(Boolean *)os->ptr = AND1(*(Boolean *)is1->ptr);
+ os->stamp = is1->stamp;
+ x->valid++;
+ }
+ else if (arg2->valid && x->tspan > 0) {
+ ip2 = (Boolean *)is2->ptr;
+ op = (Boolean *)os->ptr;
+ for (i = 0; i < x->tspan; i++) {
+ *op++ = AND1(*ip2);
+ ip2++;
+ }
+ os->stamp = is2->stamp;
+ x->valid++;
+ }
+ else x->valid = 0;
+
+#if PCP_DEBUG
+ if (pmDebug & DBG_TRACE_APPL2) {
+ fprintf(stderr, "cndAnd_1_n(" PRINTF_P_PFX "%p) ...\n", x);
+ dumpExpr(x);
+ }
+#endif
+}
+
+void
+cndAnd_1_1(Expr *x)
+{
+ Expr *arg1 = x->arg1;
+ Expr *arg2 = x->arg2;
+ Sample *is1 = &arg1->smpls[0];
+ Sample *is2 = &arg2->smpls[0];
+ Sample *os = &x->smpls[0];
+
+ EVALARG(arg1)
+ EVALARG(arg2)
+ ROTATE(x)
+
+ if (arg1->valid && arg2->valid) {
+ *(Boolean *)os->ptr = AND(*(Boolean *)is1->ptr, *(Boolean *)is2->ptr);
+ os->stamp = (is1->stamp > is2->stamp) ? is1->stamp : is2->stamp;
+ x->valid++;
+ }
+ else if (arg1->valid) {
+ *(Boolean *)os->ptr = AND1(*(Boolean *)is1->ptr);
+ os->stamp = is1->stamp;
+ x->valid++;
+ }
+ else if (arg2->valid) {
+ *(Boolean *)os->ptr = AND1(*(Boolean *)is2->ptr);
+ os->stamp = is2->stamp;
+ x->valid++;
+ }
+ else x->valid = 0;
+
+#if PCP_DEBUG
+ if (pmDebug & DBG_TRACE_APPL2) {
+ fprintf(stderr, "cndAnd_1_1(" PRINTF_P_PFX "%p) ...\n", x);
+ dumpExpr(x);
+ }
+#endif
+}
diff --git a/src/pmie/src/andor.h b/src/pmie/src/andor.h
new file mode 100644
index 0000000..bc213b8
--- /dev/null
+++ b/src/pmie/src/andor.h
@@ -0,0 +1,34 @@
+/***********************************************************************
+ * andor.h - Logical AND/OR expression evaluator functions
+ ***********************************************************************
+ *
+ * Copyright (c) 1995 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#ifndef ANDOR_H
+#define ANDOR_H
+
+/* expression evaluator function prototypes */
+void cndOr_n_n(Expr *);
+void cndOr_1_n(Expr *);
+void cndOr_n_1(Expr *);
+void cndOr_1_1(Expr *);
+void cndAnd_n_n(Expr *);
+void cndAnd_1_n(Expr *);
+void cndAnd_n_1(Expr *);
+void cndAnd_1_1(Expr *);
+
+#endif /* ANDOR_H */
diff --git a/src/pmie/src/binary.sk b/src/pmie/src/binary.sk
new file mode 100644
index 0000000..26ada2a
--- /dev/null
+++ b/src/pmie/src/binary.sk
@@ -0,0 +1,179 @@
+/*
+ * Copyright (c) 1995-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.
+ *
+ * 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
+ */
+
+/***********************************************************************
+ * skeleton: binary.sk - binary operator
+ ***********************************************************************/
+
+/*
+ * operator: @FUN
+ */
+
+#define @OP
+
+void
+@FUN_n_n(Expr *x)
+{
+ Expr *arg1 = x->arg1;
+ Expr *arg2 = x->arg2;
+ Sample *is1 = &arg1->smpls[0];
+ Sample *is2 = &arg2->smpls[0];
+ Sample *os = &x->smpls[0];
+ @ITYPE *ip1;
+ @ITYPE *ip2;
+ @OTYPE *op;
+ int n;
+ int i;
+
+ EVALARG(arg1)
+ EVALARG(arg2)
+ ROTATE(x)
+
+ if (arg1->valid && arg2->valid && x->tspan > 0 && x->tspan == arg1->tspan && x->tspan == arg2->tspan) {
+ ip1 = (@ITYPE *)is1->ptr;
+ ip2 = (@ITYPE *)is2->ptr;
+ op = (@OTYPE *)os->ptr;
+ n = x->tspan;
+ for (i = 0; i < n; i++) {
+ *op++ = OP(*ip1, *ip2);
+ ip1++;
+ ip2++;
+ }
+ os->stamp = (is1->stamp > is2->stamp) ? is1->stamp : is2->stamp;
+ x->valid++;
+ }
+ else x->valid = 0;
+
+#if PCP_DEBUG
+ if (pmDebug & DBG_TRACE_APPL2) {
+ fprintf(stderr, "@FUN_n_n(" PRINTF_P_PFX "%p) ...\n", x);
+ dumpExpr(x);
+ }
+#endif
+}
+
+void
+@FUN_n_1(Expr *x)
+{
+ Expr *arg1 = x->arg1;
+ Expr *arg2 = x->arg2;
+ Sample *is1 = &arg1->smpls[0];
+ Sample *is2 = &arg2->smpls[0];
+ Sample *os = &x->smpls[0];
+ @ITYPE *ip1;
+ @ITYPE iv2;
+ @OTYPE *op;
+ int n;
+ int i;
+
+ EVALARG(arg1)
+ EVALARG(arg2)
+ ROTATE(x)
+
+ if (arg1->valid && arg2->valid && x->tspan > 0 && x->tspan == arg1->tspan) {
+ ip1 = (@ITYPE *)is1->ptr;
+ iv2 = *(@ITYPE *)is2->ptr;
+ op = (@OTYPE *)os->ptr;
+ n = x->tspan;
+ for (i = 0; i < n; i++) {
+ *op++ = OP(*ip1, iv2);
+ ip1++;
+ }
+ os->stamp = (is1->stamp > is2->stamp) ? is1->stamp : is2->stamp;
+ x->valid++;
+ }
+ else x->valid = 0;
+
+#if PCP_DEBUG
+ if (pmDebug & DBG_TRACE_APPL2) {
+ fprintf(stderr, "@FUN_n_1(" PRINTF_P_PFX "%p) ...\n", x);
+ dumpExpr(x);
+ }
+#endif
+}
+
+void
+@FUN_1_n(Expr *x)
+{
+ Expr *arg1 = x->arg1;
+ Expr *arg2 = x->arg2;
+ Sample *is1 = &arg1->smpls[0];
+ Sample *is2 = &arg2->smpls[0];
+ Sample *os = &x->smpls[0];
+ @ITYPE iv1;
+ @ITYPE *ip2;
+ @OTYPE *op;
+ int n;
+ int i;
+
+ EVALARG(arg1)
+ EVALARG(arg2)
+ ROTATE(x)
+
+ if (arg1->valid && arg2->valid && x->tspan > 0 && x->tspan == arg2->tspan) {
+ iv1 = *(@ITYPE *)is1->ptr;
+ ip2 = (@ITYPE *)is2->ptr;
+ op = (@OTYPE *)os->ptr;
+ n = x->tspan;
+ for (i = 0; i < n; i++) {
+ *op++ = OP(iv1, *ip2);
+ ip2++;
+ }
+ os->stamp = (is1->stamp > is2->stamp) ? is1->stamp : is2->stamp;
+ x->valid++;
+ }
+ else x->valid = 0;
+
+#if PCP_DEBUG
+ if (pmDebug & DBG_TRACE_APPL2) {
+ fprintf(stderr, "@FUN_1_n(" PRINTF_P_PFX "%p) ...\n", x);
+ dumpExpr(x);
+ }
+#endif
+}
+
+void
+@FUN_1_1(Expr *x)
+{
+ Expr *arg1 = x->arg1;
+ Expr *arg2 = x->arg2;
+ Sample *is1 = &arg1->smpls[0];
+ Sample *is2 = &arg2->smpls[0];
+ Sample *os = &x->smpls[0];
+
+ EVALARG(arg1)
+ EVALARG(arg2)
+ ROTATE(x)
+
+ if (arg1->valid && arg2->valid) {
+ *(@OTYPE *)os->ptr = OP(*(@ITYPE *)is1->ptr, *(@ITYPE *)is2->ptr);
+ os->stamp = (is1->stamp > is2->stamp) ? is1->stamp : is2->stamp;
+ x->valid++;
+ }
+ else x->valid = 0;
+
+#if PCP_DEBUG
+ if (pmDebug & DBG_TRACE_APPL2) {
+ fprintf(stderr, "@FUN_1_1(" PRINTF_P_PFX "%p) ...\n", x);
+ dumpExpr(x);
+ }
+#endif
+}
+
+#undef OP
+
diff --git a/src/pmie/src/dstruct.c b/src/pmie/src/dstruct.c
new file mode 100644
index 0000000..5d9b8bb
--- /dev/null
+++ b/src/pmie/src/dstruct.c
@@ -0,0 +1,1307 @@
+/***********************************************************************
+ * dstruct.c - central data structures and associated operations
+ ***********************************************************************
+ *
+ * Copyright (c) 2013-2014 Red Hat.
+ * Copyright (c) 1995-2003 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+#include "pmapi.h"
+#include "impl.h"
+#include <math.h>
+#include <ctype.h>
+#include <limits.h>
+#ifdef HAVE_SYS_WAIT_H
+#include <sys/wait.h>
+#endif
+#include "dstruct.h"
+#include "symbol.h"
+#include "pragmatics.h"
+#include "fun.h"
+#include "eval.h"
+#include "show.h"
+
+#if defined(HAVE_VALUES_H)
+#include <values.h>
+#endif
+#if defined(HAVE_IEEEFP_H)
+#include <ieeefp.h>
+#endif
+
+/***********************************************************************
+ * constants
+ ***********************************************************************/
+
+double mynan; /* not-a-number run time initialized */
+
+
+/***********************************************************************
+ * user supplied parameters
+ ***********************************************************************/
+
+char *pmnsfile = PM_NS_DEFAULT; /* alternate name space */
+Archive *archives; /* list of open archives */
+RealTime first = -1; /* archive starting point */
+RealTime last = 0.0; /* archive end point */
+char *dfltHostConn; /* default host connection string */
+char *dfltHostName; /* default host name */
+RealTime dfltDelta = DELTA_DFLT; /* default sample interval */
+char *startFlag; /* start time specified? */
+char *stopFlag; /* end time specified? */
+char *alignFlag; /* align time specified? */
+char *offsetFlag; /* offset time specified? */
+RealTime runTime; /* run time interval */
+int hostZone; /* timezone from host? */
+char *timeZone; /* timezone from command line */
+int verbose; /* verbosity 0, 1 or 2 */
+int interactive; /* interactive mode, -d */
+int isdaemon; /* run as a daemon */
+int agent; /* secret agent mode? */
+int applet; /* applet mode? */
+int dowrap; /* counter wrap? default no */
+int doexit; /* time to exit stage left? */
+int dorotate; /* is a log rotation pending? */
+pmiestats_t *perf; /* live performance data */
+pmiestats_t instrument; /* used if no mmap (archive) */
+
+
+/***********************************************************************
+ * this is where the action is
+ ***********************************************************************/
+
+Task *taskq = NULL; /* evaluator task queue */
+Expr *curr; /* current executing rule expression */
+
+SymbolTable hosts; /* currently known hosts */
+SymbolTable metrics; /* currently known metrics */
+SymbolTable rules; /* currently known rules */
+SymbolTable vars; /* currently known variables */
+
+
+/***********************************************************************
+ * time
+ ***********************************************************************/
+
+RealTime now; /* current time */
+RealTime start; /* start evaluation time */
+RealTime stop; /* stop evaluation time */
+
+Symbol symDelta; /* current sample interval */
+Symbol symMinute; /* minutes after the hour 0..59 */
+Symbol symHour; /* hours since midnight 0..23 */
+Symbol symDay; /* day of the month 1..31 */
+Symbol symMonth; /* month of the year 1..12 */
+Symbol symYear; /* year 1996.. */
+Symbol symWeekday; /* days since Sunday 0..6 */
+
+static double delta; /* current sample interval */
+static double second; /* seconds after the minute 0..59 */
+static double minute; /* minutes after the hour 0..59 */
+static double hour; /* hours since midnight 0..23 */
+static double day; /* day of the month 1..31 */
+static double month; /* month of the year 1..12 */
+static double year; /* year 1996.. */
+static double weekday; /* days since Sunday 0..6 */
+
+/***********************************************************************
+ * process creation control
+ ***********************************************************************/
+int need_wait;
+
+/* return real time */
+RealTime
+getReal(void)
+{
+ struct timeval t;
+
+ __pmtimevalNow(&t);
+ return realize(t);
+}
+
+
+/* update time variables to reflect current time */
+void
+reflectTime(RealTime d)
+{
+ static time_t then = 0; /* previous time */
+ int skip = now - then;
+ struct tm tm;
+
+ then = (time_t)now;
+
+ /* sample interval */
+ delta = d;
+
+ /* try short path for current time */
+ if (skip >= 0 && skip < 24 * 60 * 60) {
+ second += skip;
+ if (second < 60)
+ return;
+ skip = (int)(second / 60);
+ second -= (double)(60 * skip);
+ minute += (double)skip;
+ if (minute < 60)
+ return;
+ skip = (int)(minute / 60);
+ minute -= (double)(60 * skip);
+ hour += (double)skip;
+ if (hour < 24)
+ return;
+ }
+
+ /* long path for current time */
+ pmLocaltime(&then, &tm);
+ second = (double) tm.tm_sec;
+ minute = (double) tm.tm_min;
+ hour = (double) tm.tm_hour;
+ day = (double) tm.tm_mday;
+ month = (double) tm.tm_mon;
+ /* tm_year is years since 1900, so this is Y2K safe */
+ year = (double) tm.tm_year + 1900;
+ weekday = (double) tm.tm_wday;
+}
+
+
+/* convert RealTime to timeval */
+void
+unrealize(RealTime rt, struct timeval *tv)
+{
+ tv->tv_sec = (time_t)rt;
+ tv->tv_usec = (int)(1000000 * (rt - tv->tv_sec));
+}
+
+
+/* convert RealTime to timespec */
+void
+unrealizenano(RealTime rt, struct timespec *ts)
+{
+ ts->tv_sec = (time_t)rt;
+ ts->tv_nsec = (int)(1000000000 * (rt - ts->tv_sec));
+}
+
+#define SLEEP_EVAL 0
+#define SLEEP_RETRY 1
+
+/* sleep until eval or retry RealTime */
+void
+sleepTight(Task *t, int type)
+{
+ RealTime sched;
+ RealTime delay; /* interval to sleep */
+ int sts;
+ RealTime cur_entry = getReal();
+#ifdef HAVE_WAITPID
+ pid_t pid;
+
+ if (need_wait) {
+ /* harvest terminated children */
+ while ((pid = waitpid(-1, &sts, WNOHANG)) > (pid_t)0) {
+#if PCP_DEBUG
+ if (pmDebug & DBG_TRACE_APPL2) {
+ fprintf(stderr, "sleepTight: wait: pid=%" FMT_PID " done status=0x%x", pid, sts);
+ if (WIFEXITED(sts))
+ fprintf(stderr, " exit=%d", WEXITSTATUS(sts));
+ if (WIFSIGNALED(sts))
+ fprintf(stderr, " signal=%d", WTERMSIG(sts));
+ fprintf(stderr, "\n");
+ }
+#endif
+ ;
+ }
+ need_wait = 0;
+ }
+#endif
+
+ if (!archives) {
+ struct timespec ts, tleft;
+ static RealTime last_sched = -1;
+ static Task *last_t;
+ static int last_type;
+ RealTime cur = getReal();
+
+ sched = type == SLEEP_EVAL ? t->eval : t->retry;
+
+ delay = sched - cur;
+ if (delay < 0) {
+ int show_detail = 0;
+ if (delay <= -1) {
+ fprintf(stderr, "sleepTight: negative delay (%f). sched=%f, cur=%f\n",
+ delay, sched, cur);
+ show_detail = 1;
+ }
+#if PCP_DEBUG
+ else {
+ if (pmDebug & DBG_TRACE_APPL2) {
+ fprintf(stderr, "sleepTight: small negative delay (%f). sched=%f, cur=%f\n",
+ delay, sched, cur);
+ show_detail = 1;
+ }
+ }
+#endif
+ if (show_detail) {
+ if (last_sched > 0) {
+ fprintf(stderr, "Last sleepTight (%s) until: ", last_type == SLEEP_EVAL ? "eval" : "retry");
+ showFullTime(stderr, last_sched);
+ fputc('\n', stderr);
+ fprintf(stderr, "Last ");
+ dumpTask(last_t);
+ }
+ fprintf(stderr, "This sleepTight() entry: ");
+ showFullTime(stderr, cur_entry);
+ fputc('\n', stderr);
+ fprintf(stderr, "Harvest children done: ");
+ showFullTime(stderr, cur);
+ fputc('\n', stderr);
+ fprintf(stderr, "Want sleepTight (%s) until: ", type == SLEEP_EVAL ? "eval" : "retry");
+ showFullTime(stderr, sched);
+ fputc('\n', stderr);
+ fprintf(stderr, "This ");
+ dumpTask(t);
+ }
+ }
+ else {
+ unrealizenano(delay, &ts);
+ for (;;) { /* loop to catch early wakeup from nanosleep */
+ if (ts.tv_sec < 0 || ts.tv_nsec > 999999999) {
+ fprintf(stderr, "sleepTight: invalid args: %ld %ld\n",
+ ts.tv_sec, ts.tv_nsec);
+ break;
+ }
+ sts = nanosleep(&ts, &tleft);
+ /* deferred signal handling done immediately */
+ if (doexit)
+ exit(doexit);
+ if (dorotate) {
+ logRotate();
+ dorotate = 0;
+ }
+ if (sts == 0 || (sts < 0 && oserror() != EINTR))
+ break;
+ ts = tleft;
+ }
+ }
+ last_t = t;
+ last_type = type;
+ last_sched = sched;
+ }
+}
+
+
+/***********************************************************************
+ * ring buffer management
+ ***********************************************************************/
+
+void
+newRingBfr(Expr *x)
+{
+ size_t sz;
+ char *p;
+ int i;
+
+ sz = ((x->sem == SEM_BOOLEAN) || (x->sem == SEM_CHAR)) ?
+ sizeof(char) * x->tspan :
+ sizeof(double) * x->tspan;
+ if (x->ring) free(x->ring);
+ x->ring = alloc(x->nsmpls * sz);
+ p = (char *) x->ring;
+ for (i = 0; i < x->nsmpls; i++) {
+ x->smpls[i].ptr = (void *) p;
+ p += sz;
+ }
+}
+
+
+void
+newStringBfr(Expr *x, size_t length, char *bfr)
+{
+ if (x->e_idom != (int)length) {
+ x->e_idom = (int)length;
+ x->tspan = (int)length;
+ x->nvals = (int)length;
+ }
+ if (x->ring)
+ free(x->ring);
+ x->ring = bfr;
+ x->smpls[0].ptr = (void *) bfr;
+}
+
+
+/* Rotate ring buffer - safe to call only if x->nsmpls > 1 */
+void
+rotate(Expr *x)
+{
+ int n = x->nsmpls-1;
+ Sample *q = &x->smpls[n];
+ Sample *p = q - 1;
+ void *t = q->ptr;
+ int i;
+
+ for (i = n; i > 0; i--)
+ *q-- = *p--;
+ x->smpls[0].ptr = t;
+}
+
+
+/***********************************************************************
+ * memory allocation
+ ***********************************************************************/
+
+void *
+alloc(size_t size)
+{
+ void *p;
+
+ if ((p = malloc(size)) == NULL) {
+ __pmNoMem("pmie.alloc", size, PM_FATAL_ERR);
+ }
+ return p;
+}
+
+
+void *
+zalloc(size_t size)
+{
+ void *p;
+
+ if ((p = calloc(1, size)) == NULL) {
+ __pmNoMem("pmie.zalloc", size, PM_FATAL_ERR);
+ }
+ return p;
+}
+
+
+void *
+aalloc(size_t align, size_t size)
+{
+ void *p;
+ int sts = 0;
+#ifdef HAVE_POSIX_MEMALIGN
+ sts = posix_memalign(&p, align, size);
+#else
+#ifdef HAVE_MEMALIGN
+ p = memalign(align, size);
+ if (p == NULL) sts = -1;
+#else
+ p = malloc(size);
+ if (p == NULL) sts = -1;
+#endif
+#endif
+ if (sts != 0) {
+ __pmNoMem("pmie.aalloc", size, PM_FATAL_ERR);
+ }
+ return p;
+}
+
+
+void *
+ralloc(void *p, size_t size)
+{
+ void *q;
+
+ if ((q = realloc(p, size)) == NULL) {
+ __pmNoMem("pmie.ralloc", size, PM_FATAL_ERR);
+ }
+ return q;
+}
+
+char *
+sdup(char *p)
+{
+ char *q;
+
+ if ((q = strdup(p)) == NULL) {
+ __pmNoMem("pmie.sdup", strlen(p), PM_FATAL_ERR);
+ }
+ return q;
+}
+
+
+Expr *
+newExpr(int op, Expr *arg1, Expr *arg2,
+ int hdom, int idom, int tdom, int nsmpls,
+ int sem)
+{
+ Expr *x;
+ Expr *arg;
+
+ x = (Expr *) zalloc(sizeof(Expr) + (nsmpls - 1) * sizeof(Sample));
+ x->op = op;
+ if (arg1) {
+ x->arg1 = arg1;
+ arg1->parent = x;
+ }
+ if (arg2) {
+ x->arg2 = arg2;
+ arg2->parent = x;
+ }
+ x->hdom = hdom;
+ x->e_idom = idom;
+ x->tdom = tdom;
+ x->nsmpls = nsmpls;
+ x->tspan = (x->e_idom >= 0) ? x->e_idom : abs(x->hdom);
+ x->nvals = x->tspan * nsmpls;
+ if (arg1) {
+ arg = primary(arg1, arg2);
+ x->metrics = arg->metrics;
+ }
+ if (sem == SEM_NUMVAR || sem == SEM_NUMCONST || sem == SEM_BOOLEAN ||
+ sem == SEM_CHAR || sem == SEM_REGEX)
+ x->units = noUnits;
+ else {
+ x->units = noUnits;
+ SET_UNITS_UNKNOWN(x->units);
+ }
+ x->sem = sem;
+ return x;
+}
+
+
+Profile *
+newProfile(Fetch *owner, pmInDom indom)
+{
+ Profile *p = (Profile *) zalloc(sizeof(Profile));
+ p->indom = indom;
+ p->fetch = owner;
+ return p;
+}
+
+
+Fetch *
+newFetch(Host *owner)
+{
+ Fetch *f = (Fetch *) zalloc(sizeof(Fetch));
+ f->host = owner;
+ return f;
+}
+
+
+Host *
+newHost(Task *owner, Symbol name)
+{
+ Host *h = (Host *) zalloc(sizeof(Host));
+
+ h->name = symCopy(name);
+ h->task = owner;
+ return h;
+}
+
+
+Task *
+newTask(RealTime delta, int nth)
+{
+ Task *t = (Task *) zalloc(sizeof(Task));
+ t->nth = nth;
+ t->delta = delta;
+ return t;
+}
+
+/* translate new metric name to internal pmid for agent mode */
+static pmID
+agentId(char *name)
+{
+ int sts;
+ pmID pmid;
+
+ if ((sts = pmLookupName(1, &name, &pmid)) < 0) {
+ fprintf(stderr, "%s: agentId: metric %s not found in namespace: %s\n",
+ pmProgname, name, pmErrStr(sts));
+ exit(1);
+ }
+ return pmid;
+}
+
+
+void
+newResult(Task *t)
+{
+ pmResult *rslt;
+ Symbol *sym;
+ pmValueSet *vset;
+ pmValueBlock *vblk;
+ int i;
+ int len;
+
+ /* allocate pmResult */
+ rslt = (pmResult *) zalloc(sizeof(pmResult) + (t->nrules - 1) * sizeof(pmValueSet *));
+ rslt->numpmid = t->nrules;
+
+ /* allocate pmValueSet's */
+ sym = t->rules;
+ for (i = 0; i < t->nrules; i++) {
+ vset = (pmValueSet *)alloc(sizeof(pmValueSet));
+ vset->pmid = agentId(symName(*sym));
+ vset->numval = 0;
+ vset->valfmt = PM_VAL_DPTR;
+ vset->vlist[0].inst = PM_IN_NULL;
+ len = PM_VAL_HDR_SIZE + sizeof(double);
+ vblk = (pmValueBlock *)zalloc(len);
+ vblk->vlen = len;
+ vblk->vtype = PM_TYPE_DOUBLE;
+ vset->vlist[0].value.pval = vblk;
+ rslt->vset[i] = vset;
+ sym++;
+ }
+
+ t->rslt = rslt;
+}
+
+
+/***********************************************************************
+ * memory deallocation
+ *
+ * IMPORTANT: These functions free the argument structure plus any
+ * structures it owns below it in the expression tree.
+ ***********************************************************************/
+
+void
+freeTask(Task *t)
+{
+ if ((t->hosts == NULL) && (t->rules == NULL)) {
+ if (t->next) t->next->prev = t->prev;
+ if (t->prev) t->prev->next = t->next;
+ else taskq = t->next;
+ free(t);
+ }
+}
+
+
+void
+freeHost(Host *h)
+{
+ if ((h->fetches == NULL) && (h->waits == NULL)) {
+ if (h->next) h->next->prev = h->prev;
+ if (h->prev) h->prev->next = h->next;
+ else {
+ h->task->hosts = h->next;
+ freeTask(h->task);
+ }
+ symFree(h->name);
+ free(h);
+ }
+}
+
+
+void
+freeFetch(Fetch *f)
+{
+ if (f->profiles == NULL) {
+ if (f->next) f->next->prev = f->prev;
+ if (f->prev) f->prev->next = f->next;
+ else {
+ f->host->fetches = f->next;
+ freeHost(f->host);
+ }
+ pmDestroyContext(f->handle);
+ if (f->result) pmFreeResult(f->result);
+ if (f->pmids) free(f->pmids);
+ free(f);
+ }
+}
+
+
+void
+FreeProfile(Profile *p)
+{
+ if (p->metrics == NULL) {
+ if (p->next) p->next->prev = p->prev;
+ if (p->prev) p->prev->next = p->next;
+ else {
+ p->fetch->profiles = p->next;
+ freeFetch(p->fetch);
+ }
+ free(p);
+ }
+}
+
+
+void
+freeMetric(Metric *m)
+{
+ int numinst;
+
+ /* Metric is on fetch list */
+ if (m->profile) {
+ if (m->prev) {
+ m->prev->next = m->next;
+ if (m->next) m->next->prev = m->prev;
+ }
+ else {
+ m->host->waits = m->next;
+ if (m->next) m->next->prev = NULL;
+ }
+ if (m->host) freeHost(m->host);
+ }
+
+ symFree(m->hname);
+ numinst = m->specinst == 0 ? m->m_idom : m->specinst;
+ if (numinst > 0 && m->inames) {
+ int i;
+ for (i = 0; i < numinst; i++) {
+ if (m->inames[i] != NULL) free(m->inames[i]);
+ }
+ free(m->inames);
+ }
+ if (numinst && m->iids) free(m->iids);
+ if (m->vals) free(m->vals);
+}
+
+
+void
+freeExpr(Expr *x)
+{
+ Metric *m;
+ int i;
+
+ if (x) {
+ if (x->arg1 && x->arg1->parent == x)
+ freeExpr(x->arg1);
+ if (x->arg2 && x->arg2->parent == x)
+ freeExpr(x->arg2);
+ if (x->metrics && x->op == CND_FETCH) {
+ for (m = x->metrics, i = 0; i < x->hdom; m++, i++)
+ freeMetric(m);
+ /*
+ * x->metrics allocated in a block, one element per host, so
+ * free as one after all other freeing has been done.
+ */
+ free(x->metrics);
+ }
+ if (x->ring) free(x->ring);
+ free(x);
+ }
+}
+
+
+/***********************************************************************
+ * comparison functions (for use by qsort)
+ ***********************************************************************/
+
+/* Compare two instance identifiers.
+ - This function is passed as an argument to qsort, hence the casts. */
+int /* -1 less, 0 equal, 1 greater */
+compid(const void *i1, const void *i2)
+{
+ if (*(int *)i1 < *(int *)i2) return -1;
+ if (*(int *)i1 > *(int *)i2) return 1;
+ return 0;
+}
+
+
+/* Compare two pmValue's on their inst fields
+ - This function is passed as an argument to qsort, hence the casts. */
+int /* -1 less, 0 equal, 1 greater */
+compair(const void *pmv1, const void *pmv2)
+{
+ if (((pmValue *)pmv1)->inst < ((pmValue *)pmv2)->inst) return -1;
+ if (((pmValue *)pmv1)->inst > ((pmValue *)pmv2)->inst) return 1;
+ return 0;
+}
+
+
+/***********************************************************************
+ * Expr manipulation
+ ***********************************************************************/
+
+/* Decide primary argument for inheritance of Expr attributes */
+Expr *
+primary(Expr *arg1, Expr *arg2)
+{
+ if (arg2 == NULL || arg1->nvals > 1)
+ return arg1;
+ if (arg2->nvals > 1)
+ return arg2;
+ if (arg1->metrics &&
+ (arg1->hdom != -1 || arg1->e_idom != -1 || arg1->tdom != -1))
+ return arg1;
+ if (arg2->metrics &&
+ (arg2->hdom != -1 || arg2->e_idom != -1 || arg2->tdom != -1))
+ return arg2;
+ return arg1;
+}
+
+
+/* change number of samples allocated in ring buffer */
+void
+changeSmpls(Expr **p, int nsmpls)
+{
+ Expr *x = *p;
+ Metric *m;
+ int i;
+
+ if (nsmpls == x->nsmpls) return;
+ *p = x = (Expr *) ralloc(x, sizeof(Expr) + (nsmpls - 1) * sizeof(Sample));
+ x->nsmpls = nsmpls;
+ x->nvals = x->tspan * nsmpls;
+ x->valid = 0;
+ if (x->op == CND_FETCH) {
+ m = x->metrics;
+ for (i = 0; i < x->hdom; i++) {
+ m->expr = x;
+ m++;
+ }
+ }
+ newRingBfr(x);
+}
+
+
+/* propagate instance domain, semantics and units from
+ argument expressions to parents */
+static void
+instExpr(Expr *x)
+{
+ int up = 0;
+ Expr *arg1 = x->arg1;
+ Expr *arg2 = x->arg2;
+ Expr *arg = primary(arg1, arg2);
+
+ /* semantics ... */
+ if (x->sem == SEM_UNKNOWN) {
+ if (arg2 == NULL) {
+ /* unary expression */
+ if (arg1->sem != SEM_UNKNOWN) {
+ up = 1;
+ x->sem = arg1->sem;
+ }
+ }
+ else if ((arg1->sem != SEM_UNKNOWN) &&
+ (arg2->sem != SEM_UNKNOWN)) {
+ /* binary expression with known args */
+ up = 1;
+ x->sem = arg->sem;
+ }
+ /* binary expression with unknown arg */
+ else
+ return;
+ }
+
+ /* units ... */
+ if (UNITS_UNKNOWN(x->units)) {
+ if (arg2 == NULL) {
+ /* unary expression */
+ if (!UNITS_UNKNOWN(arg1->units)) {
+ up = 1;
+ x->units = arg1->units;
+ }
+ }
+ else if (!UNITS_UNKNOWN(arg1->units) &&
+ !UNITS_UNKNOWN(arg2->units)) {
+ /* binary expression with known args */
+ up = 1;
+ x->units = arg->units;
+ if (x->op == CND_MUL) {
+ x->units.dimSpace = arg1->units.dimSpace + arg2->units.dimSpace;
+ x->units.dimTime = arg1->units.dimTime + arg2->units.dimTime;
+ x->units.dimCount = arg1->units.dimCount + arg2->units.dimCount;
+ }
+ else if (x->op == CND_DIV) {
+ x->units.dimSpace = arg1->units.dimSpace - arg2->units.dimSpace;
+ x->units.dimTime = arg1->units.dimTime - arg2->units.dimTime;
+ x->units.dimCount = arg1->units.dimCount - arg2->units.dimCount;
+ }
+ }
+ }
+
+ /* instance domain */
+ if ((x->e_idom != -1) && (x->e_idom != arg->e_idom)) {
+ up = 1;
+ x->e_idom = arg->e_idom;
+ x->tspan = (x->e_idom >= 0) ? x->e_idom : abs(x->hdom);
+ x->nvals = x->tspan * x->nsmpls;
+ x->valid = 0;
+ newRingBfr(x);
+ }
+
+ if (up && x->parent)
+ instExpr(x->parent);
+}
+
+
+/* propagate instance domain, semantics and units from given
+ fetch expression to its parents */
+void
+instFetchExpr(Expr *x)
+{
+ Metric *m;
+ int ninst;
+ int up = 0;
+ int i;
+
+ /* update semantics and units */
+ if (x->sem == SEM_UNKNOWN) {
+ m = x->metrics;
+ for (i = 0; i < x->hdom; i++) {
+ if (m->desc.sem != SEM_UNKNOWN) {
+ if (m->desc.sem == PM_SEM_COUNTER) {
+ x->sem = PM_SEM_INSTANT;
+ x->units = canon(m->desc.units);
+ x->units.dimTime--;
+ }
+ else {
+ x->sem = m->desc.sem;
+ x->units = canon(m->desc.units);
+ }
+ up = 1;
+ break;
+ }
+ }
+ }
+
+ /*
+ * update number of instances ... need to be careful because may be more
+ * than one host, and instances may not be fully available ...
+ * m_idom < 0 => no idea how many instances there might be (cannot
+ * contact pmcd, unknown metric, can't get indom, ...)
+ * m_idom == 0 => no values, but otherwise OK
+ * m_idom > 0 => have this many values (and hence instances)
+ */
+ m = x->metrics;
+ ninst = -1;
+ for (i = 0; i < x->hdom; i++) {
+ m->offset = ninst;
+ if (m->m_idom >= 0) {
+ if (ninst == -1)
+ ninst = m->m_idom;
+ else
+ ninst += m->m_idom;
+ }
+ m++;
+ }
+ if (x->e_idom != ninst) {
+ /* number of instances is different */
+ x->e_idom = ninst;
+ x->tspan = (x->e_idom >= 0) ? x->e_idom : abs(x->hdom);
+ x->nvals = x->nsmpls * x->tspan;
+ x->valid = 0;
+ newRingBfr(x);
+ up = 1;
+ }
+ if (x->parent) {
+ /* do we need to propagate changes? */
+ if (up ||
+ (UNITS_UNKNOWN(x->parent->units) && !UNITS_UNKNOWN(x->units))) {
+ instExpr(x->parent);
+ }
+ }
+}
+
+
+/***********************************************************************
+ * compulsory initialization
+ ***********************************************************************/
+
+void dstructInit(void)
+{
+ Expr *x;
+ double zero = 0.0;
+
+ /* not-a-number initialization */
+ mynan = zero / zero;
+
+ /* don't initialize dfltHost*; let pmie.c do it after getopt. */
+
+ /* set up symbol tables */
+ symSetTable(&hosts);
+ symSetTable(&metrics);
+ symSetTable(&rules);
+ symSetTable(&vars);
+
+ /* set yp inter-sample interval (delta) symbol */
+ symDelta = symIntern(&vars, "delta");
+ x = newExpr(OP_VAR, NULL,NULL, -1, -1, -1, 1, SEM_NUMVAR);
+ x->smpls[0].ptr = &delta;
+ x->valid = 1;
+ symValue(symDelta) = x;
+
+ /* set up time symbols */
+ symMinute = symIntern(&vars, "minute");
+ x = newExpr(OP_VAR, NULL,NULL, -1, -1, -1, 1, SEM_NUMVAR);
+ x->smpls[0].ptr = &minute;
+ x->valid = 1;
+ symValue(symMinute) = x;
+ symHour = symIntern(&vars, "hour");
+ x = newExpr(OP_VAR, NULL,NULL, -1, -1, -1, 1, SEM_NUMVAR);
+ x->smpls[0].ptr = &hour;
+ x->valid = 1;
+ symValue(symHour) = x;
+ symDay = symIntern(&vars, "day");
+ x = newExpr(OP_VAR, NULL,NULL, -1, -1, -1, 1, SEM_NUMVAR);
+ x->smpls[0].ptr = &day;
+ x->valid = 1;
+ symValue(symDay) = x;
+ symMonth = symIntern(&vars, "month");
+ x = newExpr(OP_VAR, NULL,NULL, -1, -1, -1, 1, SEM_NUMVAR);
+ x->smpls[0].ptr = &month;
+ x->valid = 1;
+ symValue(symMonth) = x;
+ symYear = symIntern(&vars, "year");
+ x = newExpr(OP_VAR, NULL,NULL, -1, -1, -1, 1, SEM_NUMVAR);
+ x->smpls[0].ptr = &year;
+ x->valid = 1;
+ symValue(symYear) = x;
+ symWeekday = symIntern(&vars, "day_of_week");
+ x = newExpr(OP_VAR, NULL,NULL, -1, -1, -1, 1, SEM_NUMVAR);
+ x->smpls[0].ptr = &weekday;
+ x->valid = 1;
+ symValue(symWeekday) = x;
+}
+
+
+/* get ready to run evaluator */
+void
+agentInit(void)
+{
+ Task *t;
+ int sts;
+
+ /* Set up local name space for agent */
+ /* Only load PMNS if it's default and hence not already loaded */
+ if (pmnsfile == PM_NS_DEFAULT && (sts = pmLoadNameSpace(pmnsfile)) < 0) {
+ fprintf(stderr, "%s: agentInit: cannot load metric namespace: %s\n",
+ pmProgname, pmErrStr(sts));
+ exit(1);
+ }
+
+ /* allocate pmResult's and send pmDescs for secret agent mode */
+ t = taskq;
+ while (t) {
+ newResult(t);
+ sendDescs(t);
+ t = t->next;
+ }
+}
+
+/*
+ * useful for diagnostics and with dbx
+ */
+
+static struct {
+ void (*addr)(Expr *);
+ char *name;
+} fn_map[] = {
+ { actAlarm, "actAlarm" },
+ { actAnd, "actAnd" },
+ { actArg, "actArg" },
+ { actFake, "actFake" },
+ { actOr, "actOr" },
+ { actPrint, "actPrint" },
+ { actShell, "actShell" },
+ { actStomp, "actStomp" },
+ { actSyslog, "actSyslog" },
+ { cndAdd_1_1, "cndAdd_1_1" },
+ { cndAdd_1_n, "cndAdd_1_n" },
+ { cndAdd_n_1, "cndAdd_n_1" },
+ { cndAdd_n_n, "cndAdd_n_n" },
+ { cndAll_host, "cndAll_host" },
+ { cndAll_inst, "cndAll_inst" },
+ { cndAll_time, "cndAll_time" },
+ { cndAnd_1_1, "cndAnd_1_1" },
+ { cndAnd_1_n, "cndAnd_1_n" },
+ { cndAnd_n_1, "cndAnd_n_1" },
+ { cndAnd_n_n, "cndAnd_n_n" },
+ { cndAvg_host, "cndAvg_host" },
+ { cndAvg_inst, "cndAvg_inst" },
+ { cndAvg_time, "cndAvg_time" },
+ { cndCount_host, "cndCount_host" },
+ { cndCount_inst, "cndCount_inst" },
+ { cndCount_time, "cndCount_time" },
+ { cndDelay_1, "cndDelay_1" },
+ { cndDelay_n, "cndDelay_n" },
+ { cndDiv_1_1, "cndDiv_1_1" },
+ { cndDiv_1_n, "cndDiv_1_n" },
+ { cndDiv_n_1, "cndDiv_n_1" },
+ { cndDiv_n_n, "cndDiv_n_n" },
+ { cndEq_1_1, "cndEq_1_1" },
+ { cndEq_1_n, "cndEq_1_n" },
+ { cndEq_n_1, "cndEq_n_1" },
+ { cndEq_n_n, "cndEq_n_n" },
+ { cndFall_1, "cndFall_1" },
+ { cndFall_n, "cndFall_n" },
+ { cndFetch_1, "cndFetch_1" },
+ { cndFetch_all, "cndFetch_all" },
+ { cndFetch_n, "cndFetch_n" },
+ { cndGt_1_1, "cndGt_1_1" },
+ { cndGt_1_n, "cndGt_1_n" },
+ { cndGt_n_1, "cndGt_n_1" },
+ { cndGt_n_n, "cndGt_n_n" },
+ { cndGte_1_1, "cndGte_1_1" },
+ { cndGte_1_n, "cndGte_1_n" },
+ { cndGte_n_1, "cndGte_n_1" },
+ { cndGte_n_n, "cndGte_n_n" },
+ { cndLt_1_1, "cndLt_1_1" },
+ { cndLt_1_n, "cndLt_1_n" },
+ { cndLt_n_1, "cndLt_n_1" },
+ { cndLt_n_n, "cndLt_n_n" },
+ { cndLte_1_1, "cndLte_1_1" },
+ { cndLte_1_n, "cndLte_1_n" },
+ { cndLte_n_1, "cndLte_n_1" },
+ { cndLte_n_n, "cndLte_n_n" },
+ { cndMax_host, "cndMax_host" },
+ { cndMax_inst, "cndMax_inst" },
+ { cndMax_time, "cndMax_time" },
+ { cndMin_host, "cndMin_host" },
+ { cndMin_inst, "cndMin_inst" },
+ { cndMin_time, "cndMin_time" },
+ { cndMul_1_1, "cndMul_1_1" },
+ { cndMul_1_n, "cndMul_1_n" },
+ { cndMul_n_1, "cndMul_n_1" },
+ { cndMul_n_n, "cndMul_n_n" },
+ { cndNeg_1, "cndNeg_1" },
+ { cndNeg_n, "cndNeg_n" },
+ { cndNeq_1_1, "cndNeq_1_1" },
+ { cndNeq_1_n, "cndNeq_1_n" },
+ { cndNeq_n_1, "cndNeq_n_1" },
+ { cndNeq_n_n, "cndNeq_n_n" },
+ { cndNot_1, "cndNot_1" },
+ { cndNot_n, "cndNot_n" },
+ { cndOr_1_1, "cndOr_1_1" },
+ { cndOr_1_n, "cndOr_1_n" },
+ { cndOr_n_1, "cndOr_n_1" },
+ { cndOr_n_n, "cndOr_n_n" },
+ { cndPcnt_host, "cndPcnt_host" },
+ { cndPcnt_inst, "cndPcnt_inst" },
+ { cndPcnt_time, "cndPcnt_time" },
+ { cndRate_1, "cndRate_1" },
+ { cndRate_n, "cndRate_n" },
+ { cndRise_1, "cndRise_1" },
+ { cndRise_n, "cndRise_n" },
+ { cndSome_host, "cndSome_host" },
+ { cndSome_inst, "cndSome_inst" },
+ { cndSome_time, "cndSome_time" },
+ { cndSub_1_1, "cndSub_1_1" },
+ { cndSub_1_n, "cndSub_1_n" },
+ { cndSub_n_1, "cndSub_n_1" },
+ { cndSub_n_n, "cndSub_n_n" },
+ { cndSum_host, "cndSum_host" },
+ { cndSum_inst, "cndSum_inst" },
+ { cndSum_time, "cndSum_time" },
+ { rule, "rule" },
+ { NULL, NULL },
+};
+
+static struct {
+ int val;
+ char *name;
+} sem_map[] = {
+ { SEM_UNKNOWN, "UNKNOWN" },
+ { SEM_NUMVAR, "NUMVAR" },
+ { SEM_NUMCONST, "NUMCONST" },
+ { SEM_BOOLEAN, "TRUTH" },
+ { SEM_CHAR, "CHAR" },
+ { SEM_REGEX, "REGEX" },
+ { PM_SEM_COUNTER, "COUNTER" },
+ { PM_SEM_INSTANT, "INSTANT" },
+ { PM_SEM_DISCRETE, "DISCRETE" },
+ { 0, NULL },
+};
+
+void
+__dumpExpr(int level, Expr *x)
+{
+ int i;
+ int j;
+ int k;
+
+ for (i = 0; i < level; i++) fprintf(stderr, ".. ");
+ fprintf(stderr, "Expr dump @ " PRINTF_P_PFX "%p\n", x);
+ if (x == NULL) return;
+ for (i = 0; i < level; i++) fprintf(stderr, ".. ");
+ fprintf(stderr, " op=%d (%s) arg1=" PRINTF_P_PFX "%p arg2=" PRINTF_P_PFX "%p parent=" PRINTF_P_PFX "%p\n",
+ x->op, opStrings(x->op), x->arg1, x->arg2, x->parent);
+ for (i = 0; i < level; i++) fprintf(stderr, ".. ");
+ fprintf(stderr, " eval=");
+ for (j = 0; fn_map[j].addr; j++) {
+ if (x->eval == fn_map[j].addr) {
+ fprintf(stderr, "%s", fn_map[j].name);
+ break;
+ }
+ }
+ if (fn_map[j].addr == NULL)
+ fprintf(stderr, "" PRINTF_P_PFX "%p()", x->eval);
+ fprintf(stderr, " metrics=" PRINTF_P_PFX "%p ring=" PRINTF_P_PFX "%p\n", x->metrics, x->ring);
+ for (i = 0; i < level; i++) fprintf(stderr, ".. ");
+ fprintf(stderr, " valid=%d cardinality[H,I,T]=[%d,%d,%d] tspan=%d\n",
+ x->valid, x->hdom, x->e_idom, x->tdom, x->tspan);
+ for (i = 0; i < level; i++) fprintf(stderr, ".. ");
+ fprintf(stderr, " nsmpls=%d nvals=%d sem=", x->nsmpls, x->nvals);
+ for (j = 0; sem_map[j].name; j++) {
+ if (x->sem == sem_map[j].val) {
+ fprintf(stderr, "%s", sem_map[j].name);
+ break;
+ }
+ }
+ if (sem_map[j].name == NULL)
+ fprintf(stderr, "%d", x->sem);
+ if (UNITS_UNKNOWN(x->units))
+ fprintf(stderr, " units=UNKNOWN\n");
+ else
+ fprintf(stderr, " units=%s\n", pmUnitsStr(&x->units));
+ if (x->valid > 0) {
+ if (x->sem == SEM_BOOLEAN || x->sem == SEM_CHAR ||
+ x->sem == SEM_NUMVAR || x->sem == SEM_NUMCONST ||
+ x->sem == PM_SEM_COUNTER || x->sem == PM_SEM_INSTANT ||
+ x->sem == PM_SEM_DISCRETE) {
+ for (j = 0; j < x->nsmpls; j++) {
+ for (i = 0; i < level; i++) fprintf(stderr, ".. ");
+ fprintf(stderr, " smpls[%d].ptr " PRINTF_P_PFX "%p ", j, x->smpls[j].ptr);
+ for (k = 0; k < x->tspan; k++) {
+ if (x->tspan > 1 && x->sem != SEM_CHAR) {
+ if (k > 0)
+ fprintf(stderr, ", ");
+ fprintf(stderr, "{%d} ", k);
+ }
+ if (x->sem == SEM_BOOLEAN) {
+ char c = *((char *)x->smpls[j].ptr+k);
+ if ((int)c == B_TRUE)
+ fprintf(stderr, "true");
+ else if ((int)c == B_FALSE)
+ fprintf(stderr, "false");
+ else if ((int)c == B_UNKNOWN)
+ fprintf(stderr, "unknown");
+ else
+ fprintf(stderr, "bogus (0x%x)", c & 0xff);
+ }
+ else if (x->sem == SEM_CHAR) {
+ if (k == 0)
+ fprintf(stderr, "\"%s\"", (char *)x->smpls[j].ptr);
+ }
+ else {
+ double v = *((double *)x->smpls[j].ptr+k);
+ int fp_bad = 0;
+#ifdef HAVE_FPCLASSIFY
+ fp_bad = fpclassify(v) == FP_NAN;
+#else
+#ifdef HAVE_ISNAN
+ fp_bad = isnan(v);
+#endif
+#endif
+ if (fp_bad)
+ fputc('?', stderr);
+ else
+ fprintf(stderr, "%g", v);
+ }
+ }
+ fputc('\n', stderr);
+ }
+ }
+ else if (x->sem == SEM_REGEX) {
+ for (i = 0; i < level; i++) fprintf(stderr, ".. ");
+ fprintf(stderr, " handle=" PRINTF_P_PFX "%p\n", x->ring);
+ }
+ }
+}
+
+void
+__dumpMetric(int level, Metric *m)
+{
+ int i;
+ int j;
+ int numinst;
+
+ for (i = 0; i < level; i++) fprintf(stderr, ".. ");
+ fprintf(stderr, "Metric dump @ " PRINTF_P_PFX "%p\n", m);
+ for (i = 0; i < level; i++) fprintf(stderr, ".. ");
+ fprintf(stderr, "expr=" PRINTF_P_PFX "%p profile=" PRINTF_P_PFX "%p host=" PRINTF_P_PFX "%p next=" PRINTF_P_PFX "%p prev=" PRINTF_P_PFX "%p\n",
+ m->expr, m->profile, m->host, m->next, m->prev);
+ for (i = 0; i < level; i++) fprintf(stderr, ".. ");
+ fprintf(stderr, "metric=%s host=%s conv=%g specinst=%d m_indom=%d\n",
+ symName(m->mname), symName(m->hname), m->conv, m->specinst, m->m_idom);
+ if (m->desc.indom != PM_INDOM_NULL) {
+ numinst = m->specinst == 0 ? m->m_idom : m->specinst;
+ for (j = 0; j < numinst; j++) {
+ for (i = 0; i < level; i++) fprintf(stderr, ".. ");
+ fprintf(stderr, "[%d] iid=", j);
+ if (m->iids[j] == PM_IN_NULL)
+ fprintf(stderr, "?missing");
+ else
+ fprintf(stderr, "%d", m->iids[j]);
+ fprintf(stderr, " iname=\"%s\"\n", m->inames[j]);
+ }
+ }
+ for (i = 0; i < level; i++) fprintf(stderr, ".. ");
+ fputc('\n', stderr);
+
+#if 0
+ pmDesc desc; /* pmAPI metric description */
+ RealTime stamp; /* time stamp for current values */
+ pmValueSet *vset; /* current values */
+ RealTime stomp; /* previous time stamp for rate calculation */
+ double *vals; /* vector of values for rate computation */
+ int offset; /* offset within sample in expr ring buffer */
+... Metric;
+#endif
+
+}
+
+
+void
+__dumpTree(int level, Expr *x)
+{
+ __dumpExpr(level, x);
+ if (x->arg1 != NULL) __dumpTree(level+1, x->arg1);
+ if (x->arg2 != NULL) __dumpTree(level+1, x->arg2);
+}
+
+void
+dumpTree(Expr *x)
+{
+ __dumpTree(0, x);
+}
+
+void
+dumpRules(void)
+{
+ Task *t;
+ Symbol *s;
+ int i;
+
+ for (t = taskq; t != NULL; t = t->next) {
+ s = t->rules;
+ for (i = 0; i < t->nrules; i++, s++) {
+ fprintf(stderr, "\nRule: %s\n", symName(*s));
+ dumpTree((Expr *)symValue(*s));
+ }
+ }
+}
+
+void
+dumpExpr(Expr *x)
+{
+ __dumpExpr(0, x);
+}
+
+void
+dumpMetric(Metric *m)
+{
+ __dumpMetric(0, m);
+}
+
+void
+dumpTask(Task *t)
+{
+ int i;
+ fprintf(stderr, "Task dump @ " PRINTF_P_PFX "%p\n", t);
+ fprintf(stderr, " nth=%d delta=%.3f tick=%d next=" PRINTF_P_PFX "%p prev=" PRINTF_P_PFX "%p\n", t->nth, t->delta, t->tick, t->next, t->prev);
+ fprintf(stderr, " eval time: ");
+ showFullTime(stderr, t->eval);
+ fputc('\n', stderr);
+ fprintf(stderr, " retry time: ");
+ showFullTime(stderr, t->retry);
+ fputc('\n', stderr);
+ if (t->hosts == NULL)
+ fprintf(stderr, " host=<null>\n");
+ else
+ fprintf(stderr, " host=%s (%s)\n", symName(t->hosts->name), t->hosts->down ? "down" : "up");
+ fprintf(stderr, " rules:\n");
+ for (i = 0; i < t->nrules; i++) {
+ fprintf(stderr, " %s\n", symName(t->rules[i]));
+ }
+}
diff --git a/src/pmie/src/dstruct.h b/src/pmie/src/dstruct.h
new file mode 100644
index 0000000..919de35
--- /dev/null
+++ b/src/pmie/src/dstruct.h
@@ -0,0 +1,461 @@
+/*
+ * Copyright (c) 2013-2014 Red Hat.
+ * Copyright (c) 1995 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.
+ */
+
+/***********************************************************************
+ * dstruct.h - central data structures snd associated operations
+ ***********************************************************************/
+
+#ifndef DSTRUCT_H
+#define DSTRUCT_H
+
+#include <stddef.h>
+#include <sys/types.h>
+#include "pmapi.h"
+#include "impl.h"
+#include "symbol.h"
+#include "stats.h"
+
+
+/***********************************************************************
+ * forward reference
+ ***********************************************************************/
+
+struct expr;
+struct metric;
+struct profile;
+struct fetch;
+struct host;
+struct task;
+
+
+/***********************************************************************
+ * (Kleene) 3-valued boolean values
+ ***********************************************************************/
+
+typedef char Boolean;
+#define B_FALSE 0
+#define B_TRUE 1
+#define B_UNKNOWN 2
+
+extern double mynan; /* definitely not-a-number */
+
+
+/***********************************************************************
+ * time
+ ***********************************************************************/
+
+typedef unsigned int TickTime; /* time counted in deltas */
+
+/* following in usec */
+typedef double RealTime; /* wall clock time or interval */
+#define MINUTE 60 /* one minute of real time */
+#define DELAY_MAX 32 /* maximum initial evaluation delay */
+#define RETRY 5 /* retry interval */
+#define DELTA_DFLT 10 /* default sample interval */
+#define DELTA_MIN 0.1 /* minimum sample interval */
+
+
+/***********************************************************************
+ * evaluator functions
+ ***********************************************************************/
+
+/* evaluator function */
+typedef void (Eval)(struct expr *);
+
+
+/***********************************************************************
+ * internal representation of rule expressions and their values
+ ***********************************************************************/
+
+/* pointer to and timestamp for sample in ring buffer */
+typedef struct {
+ void *ptr; /* pointer into value ring buffer */
+ RealTime stamp; /* timestamp for sample */
+} Sample;
+
+/* Expression Tree Node:
+ * The parser fills in most of the fields, pragmatics analysis
+ * fills in the rest. The parser may leave 0 in e_idom or nvals
+ * and NULL in ring to indicate it did not have enough information
+ * to fill in the correct values. These values are then patched
+ * up during pragmatics analysis and dynamically if/when the number
+ * of instances changes.
+ *
+ * Warning: The semantics of the hdom, e_idom and tdom fields are
+ * quite subtle. A value of 1 or greater is the cardinality of
+ * the corresponding domain. -1 indicates that the corresponding
+ * domain has collapsed, and there is one value. 0 indicates
+ * that there is no value (only used for e_idom to indicate an
+ * empty instance domain).
+ */
+typedef struct expr {
+ /* expression syntax */
+ int op; /* operator */
+ struct expr *arg1; /* NULL || (Expr *) */
+ struct expr *arg2; /* NULL || (Expr *) */
+ struct expr *parent; /* parent of this Expr */
+
+ /* evaluator */
+ Eval *eval; /* evaluator function */
+ int valid; /* number of valid samples */
+
+ /* description of value matrix */
+ int hdom; /* cardinality of host dimension */
+ int e_idom; /* cardinality of instance dimension */
+ int tdom; /* cardinality of time dimension */
+ int tspan; /* number of values per sample */
+ int nsmpls; /* number of samples in ring buffer */
+ int nvals; /* total number of values in ring buffer */
+ struct metric *metrics; /* array of per host metric info */
+
+ /* description of single value */
+ int sem; /* value semantics, see below */
+ pmUnits units; /* value units, as in pmDesc */
+
+ /* value buffer */
+ void *ring; /* base address of value ring buffer */
+ Sample smpls[1]; /* array dynamically allocated */
+} Expr;
+
+/* per-host description of a performance metric */
+typedef struct metric {
+ struct expr *expr; /* Expr owning this Metric */
+ struct profile *profile; /* Profile owning this Metric */
+ struct host *host; /* Host owning this Metric */
+ struct metric *next; /* fetch/wait list forward pointer */
+ struct metric *prev; /* fetch/wait list backward pointer */
+ Symbol mname; /* metric name */
+ Symbol hname; /* host name */
+ pmDesc desc; /* pmAPI metric description */
+ double conv; /* conversion factor into canonical units */
+ int specinst; /* count of specific instances in rule and */
+ /* 0 if all instances are to be considered */
+ int m_idom; /* cardinality of available instance domain */
+ char **inames; /* array of instance names */
+ int *iids; /* array of instance ids */
+ RealTime stamp; /* time stamp for current values */
+ pmValueSet *vset; /* current values */
+ RealTime stomp; /* previous time stamp for rate calculation */
+ double *vals; /* vector of values for rate computation */
+ int offset; /* offset within sample in expr ring buffer */
+} Metric;
+
+/*
+ * Note on instances in Metric:
+ *
+ * if specinst == 0, then m_idom, inames[] and iids[] are the
+ * currently available instances
+ * otherwise, m_idom is the number of the specified instances currently
+ * available (and identified in inames[] and iids[] for 0 .. m_idom-1)
+ * and the unavailable instances are after that, i.e. elements
+ * m_idom ... specinst-1 of inames[] and iids[]
+ */
+
+/* per instance-domain part of bundled fetch request */
+typedef struct profile {
+ struct metric *metrics; /* list of Metrics for this Profile */
+ struct fetch *fetch; /* Fetch bundle owning this Profile */
+ struct profile *next; /* Profile list forward link */
+ struct profile *prev; /* Profile list backward link */
+ pmInDom indom; /* instance domain */
+ int need_all; /* all instances required */
+} Profile;
+
+/* bundled fetch request for multiple metrics */
+typedef struct fetch {
+ struct profile *profiles; /* list of Profiles for this Fetch */
+ struct host *host; /* Host owning this Fetch */
+ struct fetch *next; /* fetch list forward pointer */
+ struct fetch *prev; /* fetch list backward pointer */
+ int handle; /* PMCS context handle */
+ int npmids; /* number of metrics in fetch */
+ pmID *pmids; /* array of metric ids to fetch */
+ pmResult *result; /* result of fetch */
+} Fetch;
+
+/* set of bundled fetches for single host (may be archive or live):
+ The field waits contains a list of Metrics for which descriptors
+ were not available during pragmatics analysis. */
+typedef struct host {
+ struct fetch *fetches; /* list of Fetches for this Host */
+ struct task *task; /* Task owning this host */
+ struct host *next; /* Host list forward pointer */
+ struct host *prev; /* Host list backward pointer */
+ Symbol name; /* host machine */
+ int down; /* host is not delivering metrics */
+ Metric *waits; /* wait list of Metrics */
+ Metric *duds; /* bad Metrics discovered during evaluation */
+} Host;
+
+/* element of evaluator task queue */
+typedef struct task {
+ int nth; /* initial (syntactic) position in task queue */
+ struct task *next; /* task list forward link */
+ struct task *prev; /* task list backward link */
+ RealTime epoch; /* bottom-line for timing calculations */
+ RealTime delta; /* sample interval */
+ TickTime tick; /* count up deltas */
+ RealTime eval; /* scheduled evaluation time */
+ RealTime retry; /* scheduled retry down Hosts and Metrics */
+ int nrules; /* number of rules in this task */
+ Symbol *rules; /* array of rules to be evaluated */
+ Host *hosts; /* fetches to be executed and waiting */
+ pmResult *rslt; /* for secret agent mode */
+} Task;
+
+/* value semantics - as in pmDesc plus following */
+#define SEM_UNKNOWN 0 /* semantics not yet available */
+#define SEM_NUMVAR 10 /* numeric variable value */
+#define SEM_BOOLEAN 11 /* boolean (3-state) value */
+#define SEM_CHAR 12 /* character (string) */
+#define SEM_NUMCONST 13 /* numeric constant value */
+#define SEM_REGEX 14 /* compiled regular expression */
+
+/* Expr operator (op) tokens */
+typedef int Op;
+#define RULE 0
+/* basic conditions */
+#define CND_FETCH 1
+#define CND_DELAY 2
+#define CND_RATE 3
+/* arithmetic */
+#define CND_NEG 4
+#define CND_ADD 5
+#define CND_SUB 6
+#define CND_MUL 7
+#define CND_DIV 8
+/* aggregation */
+#define CND_SUM_HOST 10
+#define CND_SUM_INST 11
+#define CND_SUM_TIME 12
+#define CND_AVG_HOST 13
+#define CND_AVG_INST 14
+#define CND_AVG_TIME 15
+#define CND_MAX_HOST 16
+#define CND_MAX_INST 17
+#define CND_MAX_TIME 18
+#define CND_MIN_HOST 19
+#define CND_MIN_INST 20
+#define CND_MIN_TIME 21
+/* relational */
+#define CND_EQ 30
+#define CND_NEQ 31
+#define CND_LT 32
+#define CND_LTE 33
+#define CND_GT 34
+#define CND_GTE 35
+/* boolean */
+#define CND_NOT 40
+#define CND_RISE 41
+#define CND_FALL 42
+#define CND_AND 43
+#define CND_OR 44
+#define CND_MATCH 45
+#define CND_NOMATCH 46
+#define CND_RULESET 47
+#define CND_OTHER 48
+/* quantification */
+#define CND_ALL_HOST 50
+#define CND_ALL_INST 51
+#define CND_ALL_TIME 52
+#define CND_SOME_HOST 53
+#define CND_SOME_INST 54
+#define CND_SOME_TIME 55
+#define CND_PCNT_HOST 56
+#define CND_PCNT_INST 57
+#define CND_PCNT_TIME 58
+#define CND_COUNT_HOST 59
+#define CND_COUNT_INST 60
+#define CND_COUNT_TIME 61
+/* actions */
+#define ACT_SEQ 70
+#define ACT_ALT 71
+#define ACT_SHELL 72
+#define ACT_ALARM 73
+#define ACT_SYSLOG 74
+#define ACT_PRINT 75
+#define ACT_ARG 76
+#define ACT_STOMP 77
+/* no operation (extension) */
+#define NOP 80
+/* dereferenced variable */
+#define OP_VAR 90
+
+int unary(Op); /* unary operator */
+int binary(Op); /* binary operator */
+
+/***********************************************************************
+ * archives
+ ***********************************************************************/
+
+typedef struct archive {
+ struct archive *next; /* list link */
+ char *fname; /* file name */
+ char *hname; /* host name */
+ RealTime first; /* timestamp for first pmResult */
+ RealTime last; /* timestamp for last pmResult */
+} Archive;
+
+
+/***********************************************************************
+ * memory allocation / deallocation
+ ***********************************************************************/
+
+void *alloc(size_t);
+void *zalloc(size_t);
+void *ralloc(void *, size_t);
+void *aalloc(size_t, size_t);
+char *sdup(char *);
+
+Expr *newExpr(int, Expr *, Expr *, int, int, int, int, int);
+Profile *newProfile(Fetch *, pmInDom);
+Fetch *newFetch(Host *);
+Host *newHost(Task *, Symbol);
+Task *newTask(RealTime, int);
+void newResult(Task *);
+
+void freeExpr(Expr *);
+
+void freeMetric(Metric *);
+
+void FreeProfile(Profile *);
+void freeFetch(Fetch *);
+void freeTask(Task *);
+
+
+/***********************************************************************
+ * ring buffer management
+ ***********************************************************************/
+
+void newRingBfr(Expr *);
+void newStringBfr(Expr *, size_t, char *);
+void rotate(Expr *);
+
+
+/***********************************************************************
+ * Expr manipulation
+ ***********************************************************************/
+
+Expr *primary(Expr *, Expr *);
+void changeSmpls(Expr **, int);
+void instFetchExpr(Expr *);
+
+/***********************************************************************
+ * time methods
+ ***********************************************************************/
+
+/* convert timeval to RealTime */
+#define realize(t) (1.0e-6 * (RealTime)(t).tv_usec + (RealTime)(t).tv_sec)
+/* convert RealTime to timeval */
+void unrealize(RealTime, struct timeval *);
+RealTime getReal(void); /* return current time */
+void reflectTime(RealTime); /* update time vars to reflect now */
+#define SLEEP_EVAL 0
+#define SLEEP_RETRY 1
+void sleepTight(Task *, int); /* sleep until retry or eval time */
+void logRotate(void); /* close current, start a new log */
+
+/*
+ * diagnostic tracing
+ */
+void dumpRules(void);
+void dumpExpr(Expr *);
+void dumpTree(Expr *);
+void dumpMetric(Metric *);
+void dumpTask(Task *);
+void __dumpExpr(int, Expr *);
+void __dumpTree(int, Expr *);
+void __dumpMetric(int, Metric *);
+
+/***********************************************************************
+ * comparison functions (for use by qsort)
+ ***********************************************************************/
+
+/* compare two instance identifiers. */
+int compid(const void *, const void *);
+
+/* compare two pmValue's on their inst fields */
+int compair(const void *, const void *);
+
+
+/***********************************************************************
+ * global data structures
+ ***********************************************************************/
+
+extern char *pmnsfile; /* alternate namespace */
+extern Archive *archives; /* archives given on command line */
+extern RealTime first; /* archive starting point */
+extern RealTime last; /* archive end point */
+extern char *dfltHostConn; /* default PM_CONTEXT_HOST parameter */
+extern char *dfltHostName; /* pmContextGetHostName of host name */
+extern RealTime dfltDelta; /* default sample interval */
+extern RealTime runTime; /* run time interval */
+extern int hostZone; /* timezone from host? */
+extern char *timeZone; /* timezone from command line */
+extern int verbose; /* verbosity 0, 1 or 2 */
+extern int interactive; /* interactive mode, -d */
+extern int isdaemon; /* run as a daemon */
+extern int agent; /* secret agent mode? */
+extern int applet; /* applet mode? */
+extern int dowrap; /* counter wrap? default no */
+extern int doexit; /* signalled its time to exit */
+extern int dorotate; /* log rotation was requested */
+extern pmiestats_t *perf; /* pmie performance data ptr */
+extern pmiestats_t instrument; /* pmie performance data struct */
+
+
+extern SymbolTable rules; /* currently known rules */
+extern SymbolTable vars; /* currently known variables */
+extern SymbolTable hosts; /* currently known hosts */
+extern SymbolTable metrics; /* currently known metrics */
+
+extern Task *taskq; /* evaluator task queue */
+extern Expr *curr; /* current executing rule expression */
+
+extern RealTime now; /* current time */
+extern RealTime start; /* start evaluation */
+extern RealTime stop; /* stop evaluation */
+
+
+/***********************************************************************
+ * reserved symbols
+ ***********************************************************************/
+
+extern Symbol symDelta; /* current sample interval */
+extern Symbol symMinute; /* minutes after the hour 0..59 */
+extern Symbol symHour; /* hours since midnight 0..23 */
+extern Symbol symDay; /* day of the month 1..31 */
+extern Symbol symMonth; /* month of the year 1..12 */
+extern Symbol symYear; /* year 1996.. */
+extern Symbol symWeekday; /* days since Sunday 0..6 */
+
+
+/***********************************************************************
+ * compulsory initialization
+ ***********************************************************************/
+
+void dstructInit(void); /* initialize central data structures */
+void timeInit(void); /* initialize time keeping data structures */
+void agentInit(void); /* initialize evaluation parameters */
+
+/***********************************************************************
+ * unknown units handling
+ * We don't have a good sentinal value, but 1 / count x 10 ^ 7 does
+ * not appear in any PMDA and cannot come from the pmie lexical scanner
+ ***********************************************************************/
+#define SET_UNITS_UNKNOWN(u) { u.dimCount = -1; u.scaleCount = 7; }
+#define UNITS_UNKNOWN(u) (u.dimCount == -1 && u.scaleCount == 7)
+
+#endif /* DSTRUCT_H */
diff --git a/src/pmie/src/eval.c b/src/pmie/src/eval.c
new file mode 100644
index 0000000..e119868
--- /dev/null
+++ b/src/pmie/src/eval.c
@@ -0,0 +1,808 @@
+/***********************************************************************
+ * eval.c - task scheduling and expression evaluation
+ ***********************************************************************
+ *
+ * Copyright (c) 1995-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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <limits.h>
+#include "dstruct.h"
+#include "eval.h"
+#include "fun.h"
+#include "pragmatics.h"
+#include "show.h"
+
+/***********************************************************************
+ * scheduling
+ ***********************************************************************/
+
+/* enter Task into task queue */
+static void
+enque(Task *t)
+{
+ Task *q;
+ RealTime tt;
+ RealTime qt;
+
+ q = taskq;
+ if (q == NULL) {
+ taskq = t;
+ t->next = NULL;
+ t->prev = NULL;
+ }
+ else {
+ tt = (t->retry && t->retry < t->eval) ? t->retry : t->eval;
+ while (q) {
+ qt = (q->retry && q->retry < q->eval) ? q->retry : q->eval;
+ if (tt <= qt) {
+ t->next = q;
+ t->prev = q->prev;
+ if (q->prev) q->prev->next = t;
+ else taskq = t;
+ q->prev = t;
+ break;
+ }
+ if (q->next == NULL) {
+ q->next = t;
+ t->next = NULL;
+ t->prev = q;
+ break;
+ }
+ q = q->next;
+ }
+ }
+}
+
+
+/***********************************************************************
+ * reconnect
+ ***********************************************************************/
+
+/* any hosts down or unavailable metrics in this task? */
+static int
+waiting(Task *t)
+{
+ Host *h;
+
+ h = t->hosts;
+ while (h) {
+ if (h->down || h->waits)
+ return 1;
+ h = h->next;
+ }
+ return 0;
+}
+
+/*
+ * state values
+ * STATE_INIT
+ * STATE_FAILINIT
+ * STATE_RECONN
+ * STATE_LOSTCONN
+ */
+
+typedef struct hstate {
+ struct hstate *next;
+ char *name;
+ int state;
+} hstate_t;
+
+static hstate_t *host_map = NULL;
+
+int
+host_state_changed(char *host, int state)
+{
+ hstate_t *hsp;
+
+ for (hsp = host_map; hsp != NULL; hsp = hsp->next) {
+ if (strcmp(host, hsp->name) == 0)
+ break;
+ }
+
+ if (hsp == NULL) {
+ hsp = (hstate_t *)alloc(sizeof(*hsp));
+ hsp->next = host_map;
+ hsp->name = sdup(host);
+ hsp->state = STATE_INIT;
+ host_map = hsp;
+ }
+
+ if (state == hsp->state) return 0;
+
+ if (state == STATE_FAILINIT)
+ __pmNotifyErr(LOG_INFO, "Cannot connect to pmcd on host %s\n", host);
+ else if (state == STATE_RECONN && hsp->state != STATE_INIT)
+ __pmNotifyErr(LOG_INFO, "Re-established connection to pmcd on host %s\n", host);
+ else if (state == STATE_LOSTCONN)
+ __pmNotifyErr(LOG_INFO, "Lost connection to pmcd on host %s\n", host);
+
+ hsp->state = state;
+ return 1;
+}
+
+/* try to reconnect to hosts and initialize missing metrics */
+static void
+enable(Task *t)
+{
+ Host *h;
+ Metric *m;
+ Metric **p;
+
+ h = t->hosts;
+ while (h) {
+
+ /* reconnect to host */
+ if (h->down) {
+ if (reconnect(h)) {
+ h->down = 0;
+ host_state_changed(symName(h->name), STATE_RECONN);
+ }
+ }
+
+ /* reinitialize waiting Metrics */
+ if ((! h->down) && (h->waits)) {
+ p = &h->waits;
+ m = *p;
+ while (m) {
+ switch (reinitMetric(m)) {
+ case 1:
+ *p = m->next;
+ unwaitMetric(m);
+ bundleMetric(h,m);
+ break;
+ case 0:
+ p = &m->next;
+ break;
+ case -1:
+ *p = m->next;
+ m->next = h->duds;
+ h->duds = m;
+ break;
+ }
+ m = *p;
+ }
+ }
+
+ h = h->next;
+ }
+}
+
+
+/***********************************************************************
+ * evaluation
+ ***********************************************************************/
+
+int showTimeFlag = 0; /* set when -e used on the command line */
+
+/* evaluate Task */
+static void
+eval(Task *task)
+{
+ Symbol *s;
+ pmValueSet *vset;
+ int i;
+
+ /* fetch metrics */
+ taskFetch(task);
+
+ /* evaluate rule expressions */
+ s = task->rules;
+ for (i = 0; i < task->nrules; i++) {
+ curr = symValue(*s);
+ if (curr->op < NOP) {
+ (curr->eval)(curr);
+ perf->eval_actual++;
+ }
+ s++;
+ }
+
+ if (verbose) {
+
+ /* send binary values */
+ if (agent) {
+ int sts;
+ s = task->rules;
+ for (i = 0; i < task->nrules; i++) {
+ vset = task->rslt->vset[i];
+ fillVSet(symValue(*s), vset);
+ s++;
+ }
+ __pmOverrideLastFd(PDU_OVERRIDE2);
+ sts = __pmSendResult(STDOUT_FILENO, pmWhichContext(), task->rslt);
+ if (sts < 0) {
+ fprintf(stderr, "Error: __pmSendResult to summary agent failed: %s\n", pmErrStr(sts));
+ exit(0);
+ }
+
+ }
+
+ /* send values to applet */
+ else if (applet) {
+ s = task->rules;
+ for (i = 0; i < task->nrules; i++) {
+ showValue(stdout, symValue(*s));
+ putchar(' ');
+ s++;
+ }
+ putchar('\n');
+ }
+
+ /* print values in ASCII */
+ else {
+ s = task->rules;
+ for (i = 0; i < task->nrules; i++) {
+ printf("%s", symName(*s));
+ if (archives || showTimeFlag) {
+ printf(" (");
+ showTime(stdout, now);
+ putchar(')');
+ }
+ printf(": ");
+ switch (verbose) {
+ case 1:
+ showValue(stdout, symValue(*s));
+ break;
+ case 2:
+ showAnnotatedValue(stdout, symValue(*s));
+ break;
+ case 3:
+ showSatisfyingValue(stdout, symValue(*s));
+ break;
+ }
+ putchar('\n');
+ s++;
+ }
+ putchar('\n');
+ }
+ }
+}
+
+
+/* Mark expression as having invalid values */
+void
+clobber(Expr *x)
+{
+ int i;
+ Boolean *t;
+ double *d;
+
+ if (x->op < NOP) {
+ if (x->arg1)
+ clobber(x->arg1);
+ if (x->arg2)
+ clobber(x->arg2);
+ x->valid = 0;
+ /*
+ * numeric variable or variable?
+ */
+ if (x->sem == PM_SEM_COUNTER ||
+ x->sem == PM_SEM_INSTANT || x->sem == PM_SEM_DISCRETE ||
+ x->sem == SEM_NUMVAR) {
+ d = (double *) x->ring;
+ for (i = 0; i < x->nvals; i++)
+ *d++ = mynan;
+ }
+ else if (x->sem == SEM_BOOLEAN) {
+ t = (Boolean *) x->ring;
+ for (i = 0; i < x->nvals; i++)
+ *t++ = B_UNKNOWN;
+ }
+ }
+}
+
+
+/***********************************************************************
+ * exported functions
+ ***********************************************************************/
+
+/* fill in appropriate evaluator function for given Expr */
+void
+findEval(Expr *x)
+{
+ int arity = 0;
+ Metric *m;
+
+ /*
+ * arity values constructed from bit masks
+ * 1 arg1 has tspan 1, and must always have one metric value
+ * 2 arg2 has tspan 1, and must always have one metric value
+ */
+ if (x->arg1 && x->arg1->tspan == 1) {
+ for (m = x->arg1->metrics; m; m = m->next) {
+ if (m->desc.indom == PM_INDOM_NULL) continue;
+ if (m->specinst == 0) break;
+ }
+ if (m == NULL) arity |= 1;
+ }
+ if (x->arg2 && x->arg2->tspan == 1) {
+ for (m = x->arg2->metrics; m; m = m->next) {
+ if (m->desc.indom == PM_INDOM_NULL) continue;
+ if (m->specinst == 0) break;
+ }
+ if (m == NULL) arity |= 2;
+ }
+
+ /*
+ * never come here with x->op == NULL or OP_VAR
+ */
+ switch (x->op) {
+
+ case RULE:
+ x->eval = rule;
+ break;
+
+ case CND_RULESET:
+ x->eval = ruleset;
+ break;
+
+ case CND_FETCH:
+ if (x->metrics->desc.indom == PM_INDOM_NULL ||
+ x->metrics->conv == 0)
+ x->eval = cndFetch_1;
+ else if (x->metrics->specinst == 0)
+ x->eval = cndFetch_all;
+ else
+ x->eval = cndFetch_n;
+ break;
+
+ case CND_SUM_HOST:
+ x->eval = cndSum_host;
+ break;
+
+ case CND_SUM_INST:
+ x->eval = cndSum_inst;
+ break;
+
+ case CND_SUM_TIME:
+ x->eval = cndSum_time;
+ break;
+
+ case CND_AVG_HOST:
+ x->eval = cndAvg_host;
+ break;
+
+ case CND_AVG_INST:
+ x->eval = cndAvg_inst;
+ break;
+
+ case CND_AVG_TIME:
+ x->eval = cndAvg_time;
+ break;
+
+ case CND_MAX_HOST:
+ x->eval = cndMax_host;
+ break;
+
+ case CND_MAX_INST:
+ x->eval = cndMax_inst;
+ break;
+
+ case CND_MAX_TIME:
+ x->eval = cndMax_time;
+ break;
+
+ case CND_MIN_HOST:
+ x->eval = cndMin_host;
+ break;
+
+ case CND_MIN_INST:
+ x->eval = cndMin_inst;
+ break;
+
+ case CND_MIN_TIME:
+ x->eval = cndMin_time;
+ break;
+
+ case CND_ALL_HOST:
+ x->eval = cndAll_host;
+ break;
+
+ case CND_ALL_INST:
+ x->eval = cndAll_inst;
+ break;
+
+ case CND_ALL_TIME:
+ x->eval = cndAll_time;
+ break;
+
+ case CND_SOME_HOST:
+ x->eval = cndSome_host;
+ break;
+
+ case CND_SOME_INST:
+ x->eval = cndSome_inst;
+ break;
+
+ case CND_SOME_TIME:
+ x->eval = cndSome_time;
+ break;
+
+ case CND_PCNT_HOST:
+ x->eval = cndPcnt_host;
+ break;
+
+ case CND_PCNT_INST:
+ x->eval = cndPcnt_inst;
+ break;
+
+ case CND_PCNT_TIME:
+ x->eval = cndPcnt_time;
+ break;
+
+ case CND_COUNT_HOST:
+ x->eval = cndCount_host;
+ break;
+
+ case CND_COUNT_INST:
+ x->eval = cndCount_inst;
+ break;
+
+ case CND_COUNT_TIME:
+ x->eval = cndCount_time;
+ break;
+
+ case ACT_SEQ:
+ x->eval = actAnd;
+ break;
+
+ case ACT_ALT:
+ x->eval = actOr;
+ break;
+
+ case ACT_SHELL:
+ x->eval = actShell;
+ break;
+
+ case ACT_ALARM:
+ x->eval = actAlarm;
+ break;
+
+ case ACT_STOMP:
+ x->eval = actStomp;
+ break;
+
+ case ACT_SYSLOG:
+ x->eval = actSyslog;
+ break;
+
+ case ACT_PRINT:
+ x->eval = actPrint;
+ break;
+
+ case ACT_ARG:
+ x->eval = actArg;
+ break;
+
+ case CND_DELAY:
+ if (arity & 1)
+ x->eval = cndDelay_1;
+ else
+ x->eval = cndDelay_n;
+ break;
+
+ case CND_RATE:
+ if (arity & 1)
+ x->eval = cndRate_1;
+ else
+ x->eval = cndRate_n;
+ break;
+
+ case CND_NEG:
+ if (arity & 1)
+ x->eval = cndNeg_1;
+ else
+ x->eval = cndNeg_n;
+ break;
+
+ case CND_NOT:
+ if (arity & 1)
+ x->eval = cndNot_1;
+ else
+ x->eval = cndNot_n;
+ break;
+
+ case CND_RISE:
+ if (arity & 1)
+ x->eval = cndRise_1;
+ else
+ x->eval = cndRise_n;
+ break;
+
+ case CND_FALL:
+ if (arity & 1)
+ x->eval = cndFall_1;
+ else
+ x->eval = cndFall_n;
+ break;
+
+ case CND_ADD:
+ if (arity & 1) {
+ if (arity & 2)
+ x->eval = cndAdd_1_1;
+ else
+ x->eval = cndAdd_1_n;
+ }
+ else {
+ if (arity & 2)
+ x->eval = cndAdd_n_1;
+ else
+ x->eval = cndAdd_n_n;
+ }
+ break;
+
+ case CND_SUB:
+ if (arity & 1) {
+ if (arity & 2)
+ x->eval = cndSub_1_1;
+ else
+ x->eval = cndSub_1_n;
+ }
+ else {
+ if (arity & 2)
+ x->eval = cndSub_n_1;
+ else
+ x->eval = cndSub_n_n;
+ }
+ break;
+
+ case CND_MUL:
+ if (arity & 1) {
+ if (arity & 2)
+ x->eval = cndMul_1_1;
+ else
+ x->eval = cndMul_1_n;
+ }
+ else {
+ if (arity & 2)
+ x->eval = cndMul_n_1;
+ else
+ x->eval = cndMul_n_n;
+ }
+ break;
+
+ case CND_DIV:
+ if (arity & 1) {
+ if (arity & 2)
+ x->eval = cndDiv_1_1;
+ else
+ x->eval = cndDiv_1_n;
+ }
+ else {
+ if (arity & 2)
+ x->eval = cndDiv_n_1;
+ else
+ x->eval = cndDiv_n_n;
+ }
+ break;
+
+ case CND_EQ:
+ if (arity & 1) {
+ if (arity & 2)
+ x->eval = cndEq_1_1;
+ else
+ x->eval = cndEq_1_n;
+ }
+ else {
+ if (arity & 2)
+ x->eval = cndEq_n_1;
+ else
+ x->eval = cndEq_n_n;
+ }
+ break;
+
+ case CND_NEQ:
+ if (arity & 1) {
+ if (arity & 2)
+ x->eval = cndNeq_1_1;
+ else
+ x->eval = cndNeq_1_n;
+ }
+ else {
+ if (arity & 2)
+ x->eval = cndNeq_n_1;
+ else
+ x->eval = cndNeq_n_n;
+ }
+ break;
+
+ case CND_LT:
+ if (arity & 1) {
+ if (arity & 2)
+ x->eval = cndLt_1_1;
+ else
+ x->eval = cndLt_1_n;
+ }
+ else {
+ if (arity & 2)
+ x->eval = cndLt_n_1;
+ else
+ x->eval = cndLt_n_n;
+ }
+ break;
+
+ case CND_LTE:
+ if (arity & 1) {
+ if (arity & 2)
+ x->eval = cndLte_1_1;
+ else
+ x->eval = cndLte_1_n;
+ }
+ else {
+ if (arity & 2)
+ x->eval = cndLte_n_1;
+ else
+ x->eval = cndLte_n_n;
+ }
+ break;
+
+ case CND_GT:
+ if (arity & 1) {
+ if (arity & 2)
+ x->eval = cndGt_1_1;
+ else
+ x->eval = cndGt_1_n;
+ }
+ else {
+ if (arity & 2)
+ x->eval = cndGt_n_1;
+ else
+ x->eval = cndGt_n_n;
+ }
+ break;
+
+ case CND_GTE:
+ if (arity & 1) {
+ if (arity & 2)
+ x->eval = cndGte_1_1;
+ else
+ x->eval = cndGte_1_n;
+ }
+ else {
+ if (arity & 2)
+ x->eval = cndGte_n_1;
+ else
+ x->eval = cndGte_n_n;
+ }
+ break;
+
+ case CND_AND:
+ if (arity & 1) {
+ if (arity & 2)
+ x->eval = cndAnd_1_1;
+ else
+ x->eval = cndAnd_1_n;
+ }
+ else {
+ if (arity & 2)
+ x->eval = cndAnd_n_1;
+ else
+ x->eval = cndAnd_n_n;
+ }
+ break;
+
+ case CND_OR:
+ if (arity & 1) {
+ if (arity & 2)
+ x->eval = cndOr_1_1;
+ else
+ x->eval = cndOr_1_n;
+ }
+ else {
+ if (arity & 2)
+ x->eval = cndOr_n_1;
+ else
+ x->eval = cndOr_n_n;
+ }
+ break;
+
+ case CND_MATCH:
+ case CND_NOMATCH:
+ x->eval = cndMatch_inst;
+ break;
+
+ case CND_OTHER:
+ /* OTHER is not really evaluated in this sense, see ruleset() */
+ x->eval = NULL;
+ break;
+
+ default:
+ __pmNotifyErr(LOG_ERR, "findEval: internal error: bad op (%d) %s\n", x->op, opStrings(x->op));
+ dumpExpr(x);
+ exit(1);
+ }
+
+ /* patch in fake actions for archive mode */
+ if (archives &&
+ (x->op == ACT_SHELL || x->op == ACT_ALARM || x->op == ACT_SYSLOG ||
+ x->op == ACT_PRINT || x->op == ACT_STOMP)) {
+ x->eval = actFake;
+ }
+}
+
+
+/* run evaluator */
+void
+run(void)
+{
+ Task *t;
+
+ /* empty task queue */
+ if (taskq == NULL)
+ return;
+
+ /* initialize task scheduling */
+ t = taskq;
+ while (t) {
+ t->eval = t->epoch = start;
+ t->retry = 0;
+ t->tick = 0;
+ t = t->next;
+ }
+
+ /* evaluate and reschedule */
+ t = taskq;
+ for (;;) {
+ if (t->retry && t->retry < t->eval) {
+ now = t->retry;
+ if (now > stop)
+ break;
+ sleepTight(t, SLEEP_RETRY);
+ enable(t);
+ t->retry = waiting(t) ? now + RETRY : 0;
+ }
+ else {
+ now = t->eval;
+ if (now > stop)
+ break;
+ reflectTime(t->delta);
+ sleepTight(t, SLEEP_EVAL);
+ eval(t);
+ t->tick++;
+ t->eval = t->epoch + t->tick * t->delta;
+ if ((! t->retry) && waiting(t))
+ t->retry = now + RETRY;
+ }
+ taskq = t->next;
+ if (taskq) taskq->prev = NULL;
+ enque(t);
+ t = taskq;
+ }
+ __pmNotifyErr(LOG_INFO, "evaluator exiting\n");
+}
+
+
+/* invalidate all expressions being evaluated
+ i.e. mark values as unknown */
+void
+invalidate(void)
+{
+ Task *t;
+ Expr *x;
+ Symbol *s;
+ int i;
+
+ t = taskq;
+ while (t) {
+ s = t->rules;
+ for (i = 0; i < t->nrules; i++) {
+ x = symValue(*s);
+ clobber(x);
+ s++;
+ }
+ t = t->next;
+ }
+}
diff --git a/src/pmie/src/eval.h b/src/pmie/src/eval.h
new file mode 100644
index 0000000..0333725
--- /dev/null
+++ b/src/pmie/src/eval.h
@@ -0,0 +1,46 @@
+/***********************************************************************
+ * eval.h
+ ***********************************************************************
+ *
+ * Copyright (c) 1995 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#ifndef EVAL_H
+#define EVAL_H
+
+#include "dstruct.h"
+
+/* fill in apprpriate evaluator function for given Expr */
+void findEval(Expr *);
+
+/* run evaluator until specified time reached */
+void run(void);
+
+/* invalidate one expression (and descendents) */
+void clobber(Expr *);
+
+/* invalidate all expressions being evaluated */
+void invalidate(void);
+
+/* report changes in pmcd connection state */
+#define STATE_INIT 0
+#define STATE_FAILINIT 1
+#define STATE_RECONN 2
+#define STATE_LOSTCONN 3
+int host_state_changed(char *, int);
+
+#endif /* EVAL_H */
+
diff --git a/src/pmie/src/fetch.sk b/src/pmie/src/fetch.sk
new file mode 100644
index 0000000..76bcb74
--- /dev/null
+++ b/src/pmie/src/fetch.sk
@@ -0,0 +1,460 @@
+/*
+ * Copyright (c) 1995-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.
+ *
+ * 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
+ */
+
+/***********************************************************************
+ * skeleton: fetch.sk - fetch metric values
+ ***********************************************************************/
+
+/*
+ * operator: cndFetch
+ */
+
+static int
+indom_changed(Metric *m)
+{
+ int changed = 0;
+ int j;
+
+ /* check for changes in the instance domain */
+ if (m->vset == NULL || m->vset->numval <= 0) {
+ if (m->m_idom > 0)
+ changed = 1;
+ }
+ else {
+ if (m->vset->numval != m->m_idom)
+ changed = 1;
+ else {
+ for (j = 0; j < m->m_idom; j++) {
+ if (m->iids[j] != m->vset->vlist[j].inst) {
+ changed = 1;
+ break;
+ }
+ }
+ }
+ }
+
+ if (changed) {
+ int old;
+ int new;
+ int numval;
+ char **inames;
+ int *iids;
+ int sts;
+ int handle = -1;
+ int old_handle = -1;
+ char **get_inames;
+ int *get_iids;
+ int numinst = -1;
+
+ if (m->vset == NULL || m->vset->numval <= 0)
+ numval = 0;
+ else
+ numval = m->vset->numval;
+
+ /* build new inames[] and iids[] */
+ if (numval > 0) {
+ inames = (char **)alloc(numval * sizeof(char *));
+ iids = (int *)alloc(numval * sizeof(int));
+ if (numval > m->m_idom && m->desc.sem == PM_SEM_COUNTER) {
+ /* more instances, so expand the val array */
+ m->vals = (double *)ralloc(m->vals, numval * sizeof(double));
+ for (j = m->m_idom; j < numval; j++)
+ m->vals[j] = 0;
+ }
+ }
+ else {
+ inames = NULL;
+ iids = NULL;
+ }
+
+ for (new = 0; new < numval; new++) {
+ for (old = 0; old < m->m_idom; old++) {
+ if (m->iids[old] == m->vset->vlist[new].inst)
+ break;
+ }
+ if (old < m->m_idom) {
+ /* in both lists */
+ inames[new] = m->inames[old];
+ m->inames[old] = NULL;
+ iids[new] = m->iids[old];
+ if (m->desc.sem == PM_SEM_COUNTER && new != old) {
+ /* swap vals[] */
+ double d;
+ d = m->vals[new];
+ m->vals[new] = m->vals[old];
+ m->vals[old] = d;
+ }
+ }
+ else {
+ /* new one */
+ inames[new] = NULL;
+ iids[new] = m->vset->vlist[new].inst;
+ }
+ }
+
+ /*
+ * clean up old inames and iids, the install new ones
+ */
+ if (m->m_idom > 0 && m->inames != NULL) {
+ for (old = 0; old < m->m_idom; old++) {
+ if (m->inames[old] != NULL) free(m->inames[old]);
+ }
+ }
+ if (m->inames != NULL)
+ free(m->inames);
+ if (m->iids != NULL)
+ free(m->iids);
+ m->inames = inames;
+ m->iids = iids;
+ m->m_idom = numval;
+
+ for (new = 0; new < m->m_idom; new++) {
+ if (m->inames[new] == NULL) {
+ if (handle < 0) {
+ /* set up temporary context */
+ if (old_handle < 0)
+ old_handle = pmWhichContext();
+ handle = newContext(symName(m->hname));
+ }
+ if (handle < 0) {
+ sts = -1;
+ }
+ else {
+ if (archives) {
+ if ((sts = pmNameInDomArchive(m->desc.indom, m->iids[new], &m->inames[new])) < 0) {
+ __pmNotifyErr(LOG_ERR, "metric %s from %s: instance domain not "
+ "available in archive\npmNameInDomArchive failed: %s\n",
+ symName(m->mname), findsource(symName(m->hname)), pmErrStr(sts));
+ }
+ }
+ else {
+ if (numinst == -1) {
+ if ((sts = pmGetInDom(m->desc.indom, &get_iids, &get_inames)) < 0) {
+ __pmNotifyErr(LOG_ERR, "metric %s from %s: instance domain not (currently) available\n"
+ "pmGetInDom failed: %s\n",
+ symName(m->mname), findsource(symName(m->hname)), pmErrStr(sts));
+ }
+ else
+ numinst = sts;
+ }
+ sts = -1;
+ for (j = 0; j < numinst; j++) {
+ if (m->iids[new] == get_iids[j]) {
+ m->inames[new] = sdup(get_inames[j]);
+ sts = 0;
+ break;
+ }
+ }
+ }
+ }
+ if (sts < 0) {
+ /* ugly, but not much choice */
+ m->inames[new] = sdup("inst#xxxxxxxxxxxx?");
+ sprintf(m->inames[new], "inst#%d?", m->iids[new]);
+ }
+
+ if (m->desc.sem == PM_SEM_COUNTER)
+ m->vals[new] = 0;
+ }
+ }
+ if (handle >= 0) {
+ pmDestroyContext(handle);
+ if (old_handle >= 0)
+ pmUseContext(old_handle);
+ }
+ if (numinst > 0) {
+ /* pmGetInDom returned some instances above */
+ free(get_iids);
+ free(get_inames);
+ }
+#if PCP_DEBUG
+ if (pmDebug & DBG_TRACE_APPL2) {
+ fprintf(stderr, "indom_changed: %s from %s\n",
+ symName(m->mname), symName(m->hname));
+ if (m->m_idom < 1) fprintf(stderr, " %d instances!\n", m->m_idom);
+ for (j = 0; j < m->m_idom; j++) {
+ fprintf(stderr, " indom[%d] %d \"%s\"\n",
+ j, m->iids[j], m->inames[j]);
+ }
+ }
+#endif
+ }
+
+ return changed;
+}
+
+/* null instance domain - so 1 instance only */
+void
+cndFetch_1(Expr *x)
+{
+ Metric *m = x->metrics;
+ double *op;
+ RealTime stamp = 0;
+ pmAtomValue a;
+ double t;
+ int i, j;
+
+ ROTATE(x)
+ x->valid++;
+ op = (double *)x->smpls[0].ptr;
+
+ for (i = 0; i < x->hdom; i++) {
+ /* extract value */
+ if (m->vset && m->vset->numval == 1) {
+ pmExtractValue(m->vset->valfmt, &m->vset->vlist[0], m->desc.type, &a, PM_TYPE_DOUBLE);
+ *op++ = m->conv * a.d;
+#if PCP_DEBUG
+ if (pmDebug & DBG_TRACE_APPL2) {
+ fprintf(stderr, "cndFetch_1: %s from %s = %g",
+ symName(m->mname), symName(m->hname), m->conv * a.d);
+ if (m->conv != 1) fprintf(stderr, " (unconv = %g)", a.d);
+ fputc('\n', stderr);
+ }
+#endif
+ }
+
+ /* no value */
+ else {
+ x->valid = 0;
+ for (j = i; j < x->hdom; j++) {
+ m->stomp = 0;
+ m->vset = NULL;
+ m++;
+ }
+ return;
+ }
+ m->vset = NULL;
+
+ /* rate computation */
+ if (m->desc.sem == PM_SEM_COUNTER) {
+ op--;
+ if (m->m_idom > 0) {
+ t = *op - m->vals[0];
+ if (t < 0.0 && dowrap) {
+ switch (m->desc.type) {
+ case PM_TYPE_32:
+ case PM_TYPE_U32:
+ t += (double)UINT_MAX+1;
+ break;
+ case PM_TYPE_64:
+ case PM_TYPE_U64:
+ t += (double)ULONGLONG_MAX+1;
+ break;
+ }
+ }
+ t /= (m->stamp - m->stomp);
+ m->vals[0] = *op;
+ if (t < 0.0) x->valid = 0;
+ else *op = t;
+ op++;
+ }
+ if (m->stomp == 0) x->valid = 0;
+ m->stomp = m->stamp;
+ }
+
+ /* pick up most recent timestamp */
+ if (m->stamp > stamp) stamp = m->stamp;
+
+ m++;
+ }
+ x->smpls[0].stamp = stamp;
+}
+
+void
+cndFetch_all(Expr *x)
+{
+ Metric *m = x->metrics;
+ double *op;
+ RealTime stamp = 0;
+ pmAtomValue a;
+ double t;
+ int fix_idom = 0;
+ int i, j;
+
+ ROTATE(x)
+
+ /* preliminary scan through Metrics */
+ for (i = 0; i < x->hdom; i++) {
+ /* check for different instances */
+ if (indom_changed(m)) {
+ fix_idom = 1;
+ m->stomp = 0;
+ }
+ m++;
+ }
+
+ if (fix_idom) {
+ /*
+ * propagate indom changes up the expression tree
+ * and reshape the ring buffer if required
+ */
+ instFetchExpr(x);
+ }
+
+ /*
+ * even if ring buffer reshaped in instFetchExpr(), we're
+ * about to populate it with another set of values, so bump
+ * valid counter
+ */
+ x->valid++;
+
+ /* extract values */
+ op = (double *)x->smpls[0].ptr;
+ m = x->metrics;
+ for (i = 0; i < x->hdom; i++) {
+
+ /* extract values from m->vset */
+ for (j = 0; j < m->m_idom; j++) {
+ pmExtractValue(m->vset->valfmt, &m->vset->vlist[j], m->desc.type, &a, PM_TYPE_DOUBLE);
+ *op++ = m->conv * a.d;
+#if PCP_DEBUG
+ if (pmDebug & DBG_TRACE_APPL2) {
+ fprintf(stderr, "cndFetch_all: %s[%s] from %s = %g",
+ symName(m->mname), m->inames[j], symName(m->hname), m->conv * a.d);
+ if (m->conv != 1) fprintf(stderr, " (unconv = %g)", a.d);
+ fputc('\n', stderr);
+ }
+#endif
+ }
+ m->vset = NULL;
+
+ /* rate computation */
+ if (m->desc.sem == PM_SEM_COUNTER) {
+ op -= m->m_idom;
+ for (j = 0; j < m->m_idom; j++) {
+ t = *op - m->vals[j];
+ if (t < 0.0 && dowrap) {
+ switch (m->desc.type) {
+ case PM_TYPE_32:
+ case PM_TYPE_U32:
+ t += (double)UINT_MAX+1;
+ break;
+ case PM_TYPE_64:
+ case PM_TYPE_U64:
+ t += (double)ULONGLONG_MAX+1;
+ break;
+ }
+ }
+ t /= (m->stamp - m->stomp);
+ m->vals[j] = *op;
+ if (t < 0.0) x->valid = 0;
+ else *op = t;
+ op++;
+ }
+ if (m->stomp == 0) x->valid = 0;
+ m->stomp = m->stamp;
+ }
+
+ /* pick up most recent timestamp */
+ if (m->stamp > stamp) stamp = m->stamp;
+
+ m++;
+ }
+ x->smpls[0].stamp = stamp;
+}
+
+void
+cndFetch_n(Expr *x)
+{
+ Metric *m = x->metrics;
+ double *op;
+ RealTime stamp = 0;
+ pmAtomValue a;
+ double t;
+ int i, j, k;
+
+ ROTATE(x)
+ x->valid++;
+ op = (double *)x->smpls[0].ptr;
+
+ for (i = 0; i < x->hdom; i++) {
+ /* no values */
+ if ((m->vset == NULL) || (m->vset->numval < 0)) {
+ x->valid = 0;
+ for (j = i; j < x->hdom; j++) {
+ m->stomp = 0;
+ m->vset = NULL;
+ m++;
+ }
+ return;
+ }
+
+ /* extract values */
+ for (j = 0; j < m->m_idom; j++) {
+ for (k = 0; k < m->vset->numval; k++) {
+ if (m->iids[j] == m->vset->vlist[k].inst) {
+ pmExtractValue(m->vset->valfmt, &m->vset->vlist[k], m->desc.type, &a, PM_TYPE_DOUBLE);
+ *op++ = m->conv * a.d;
+#if PCP_DEBUG
+ if (pmDebug & DBG_TRACE_APPL2) {
+ fprintf(stderr, "cndFetch_n: %s[%s] from %s = %g",
+ symName(m->mname), m->inames[j], symName(m->hname), m->conv * a.d);
+ if (m->conv != 1) fprintf(stderr, " (unconv = %g)", a.d);
+ fputc('\n', stderr);
+ }
+#endif
+ break;
+ }
+ }
+
+ /* missing value */
+ if (k == m->vset->numval) {
+ x->valid = 0;
+ m->stomp = 0;
+ m->vset = NULL;
+ return;
+ }
+ }
+ m->vset = NULL;
+
+ /* rate computation */
+ if (m->desc.sem == PM_SEM_COUNTER) {
+ op -= m->m_idom;
+ for (j = 0; j < m->m_idom; j++) {
+ t = *op - m->vals[j];
+ if (t < 0.0 && dowrap) {
+ switch (m->desc.type) {
+ case PM_TYPE_32:
+ case PM_TYPE_U32:
+ t += (double)UINT_MAX+1;
+ break;
+ case PM_TYPE_64:
+ case PM_TYPE_U64:
+ t += (double)ULONGLONG_MAX+1;
+ break;
+ }
+ }
+ t /= (m->stamp - m->stomp);
+ m->vals[j] = *op;
+ if (t < 0.0) x->valid = 0;
+ else *op = t;
+ op++;
+ }
+ if (m->stomp == 0) x->valid = 0;
+ m->stomp = m->stamp;
+ }
+
+ /* pick up most recent timestamp */
+ if (m->stamp > stamp) stamp = m->stamp;
+
+ m++;
+ }
+ x->smpls[0].stamp = stamp;
+}
+
+
diff --git a/src/pmie/src/fun.h b/src/pmie/src/fun.h
new file mode 100644
index 0000000..532b4af
--- /dev/null
+++ b/src/pmie/src/fun.h
@@ -0,0 +1,124 @@
+/***********************************************************************
+ * fun.h - expression evaluator functions
+ ***********************************************************************
+ *
+ * Copyright (c) 1995 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#ifndef FUN_H
+#define FUN_H
+
+#include "dstruct.h"
+#include "andor.h"
+
+#define ROTATE(x) if ((x)->nsmpls > 1) rotate(x);
+#define EVALARG(x) if ((x)->op < NOP) ((x)->eval)(x);
+
+/* expression evaluator function prototypes */
+void rule(Expr *);
+void ruleset(Expr *);
+void cndFetch_all(Expr *);
+void cndFetch_n(Expr *);
+void cndFetch_1(Expr *);
+void cndDelay_n(Expr *);
+void cndDelay_1(Expr *);
+void cndRate_n(Expr *);
+void cndRate_1(Expr *);
+void cndSum_host(Expr *);
+void cndSum_inst(Expr *);
+void cndSum_time(Expr *);
+void cndAvg_host(Expr *);
+void cndAvg_inst(Expr *);
+void cndAvg_time(Expr *);
+void cndMax_host(Expr *);
+void cndMax_inst(Expr *);
+void cndMax_time(Expr *);
+void cndMin_host(Expr *);
+void cndMin_inst(Expr *);
+void cndMin_time(Expr *);
+void cndNeg_n(Expr *);
+void cndNeg_1(Expr *);
+void cndAdd_n_n(Expr *);
+void cndAdd_1_n(Expr *);
+void cndAdd_n_1(Expr *);
+void cndAdd_1_1(Expr *);
+void cndSub_n_n(Expr *);
+void cndSub_1_n(Expr *);
+void cndSub_n_1(Expr *);
+void cndSub_1_1(Expr *);
+void cndMul_n_n(Expr *);
+void cndMul_1_n(Expr *);
+void cndMul_n_1(Expr *);
+void cndMul_1_1(Expr *);
+void cndDiv_n_n(Expr *);
+void cndDiv_1_n(Expr *);
+void cndDiv_n_1(Expr *);
+void cndDiv_1_1(Expr *);
+void cndEq_n_n(Expr *);
+void cndEq_1_n(Expr *);
+void cndEq_n_1(Expr *);
+void cndEq_1_1(Expr *);
+void cndNeq_n_n(Expr *);
+void cndNeq_1_n(Expr *);
+void cndNeq_n_1(Expr *);
+void cndNeq_1_1(Expr *);
+void cndLt_n_n(Expr *);
+void cndLt_1_n(Expr *);
+void cndLt_n_1(Expr *);
+void cndLt_1_1(Expr *);
+void cndLte_n_n(Expr *);
+void cndLte_1_n(Expr *);
+void cndLte_n_1(Expr *);
+void cndLte_1_1(Expr *);
+void cndGt_n_n(Expr *);
+void cndGt_1_n(Expr *);
+void cndGt_n_1(Expr *);
+void cndGt_1_1(Expr *);
+void cndGte_n_n(Expr *);
+void cndGte_1_n(Expr *);
+void cndGte_n_1(Expr *);
+void cndGte_1_1(Expr *);
+void cndNot_n(Expr *);
+void cndNot_1(Expr *);
+void cndRise_n(Expr *);
+void cndRise_1(Expr *);
+void cndFall_n(Expr *);
+void cndFall_1(Expr *);
+void cndMatch_inst(Expr *);
+void cndAll_host(Expr *);
+void cndAll_inst(Expr *);
+void cndAll_time(Expr *);
+void cndSome_host(Expr *);
+void cndSome_inst(Expr *);
+void cndSome_time(Expr *);
+void cndPcnt_host(Expr *);
+void cndPcnt_inst(Expr *);
+void cndPcnt_time(Expr *);
+void cndCount_host(Expr *);
+void cndCount_inst(Expr *);
+void cndCount_time(Expr *);
+void actAnd(Expr *);
+void actOr(Expr *);
+void actShell(Expr *);
+void actAlarm(Expr *);
+void actSyslog(Expr *);
+void actPrint(Expr *);
+void actStomp(Expr *);
+void actArg(Expr *);
+void actFake(Expr *);
+
+#endif /* FUN_H */
+
diff --git a/src/pmie/src/grammar.y b/src/pmie/src/grammar.y
new file mode 100644
index 0000000..0760488
--- /dev/null
+++ b/src/pmie/src/grammar.y
@@ -0,0 +1,666 @@
+/***********************************************************************
+ * grammar.y - yacc grammar for rule language
+ ***********************************************************************
+ *
+ * Copyright (c) 1995 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+%{
+#include "dstruct.h"
+#include "syntax.h"
+#include "lexicon.h"
+#include "pragmatics.h"
+#include "systemlog.h"
+#include "stomp.h"
+#include "show.h"
+
+/* strings for error reporting */
+char precede[] = "precede";
+char follow[] = "follow";
+char act_str[] = "action";
+char bexp_str[] = "logical expression";
+char aexp_str[] = "arithmetic expression";
+char quant_str[] = "quantifier";
+char aggr_str[] = "aggregation operator";
+char pcnt_str[] = "percentage quantifier";
+char host_str[] = "host name";
+char inst_str[] = "instance name";
+char sample_str[] = "sample number(s)";
+char tstr_str[] = "(time interval optional) and string";
+char num_str[] = "number";
+
+/* report grammatical error */
+static void
+gramerr(char *phrase, char *pos, char *op)
+{
+ fprintf(stderr, "%s expected to %s %s\n", phrase, pos, op);
+ lexSync();
+}
+
+
+%}
+
+/***********************************************************************
+ * yacc token and operator declarations
+ ***********************************************************************/
+
+%expect 184
+%start stmnt
+
+%token ARROW
+%token SHELL
+%token ALARM
+%token SYSLOG
+%token PRINT
+%token STOMP
+%token SOME_QUANT
+%token ALL_QUANT
+%token PCNT_QUANT
+%token LEQ_REL
+%token GEQ_REL
+%token NEQ_REL
+%token EQ_REL
+%token AND
+%token SEQ
+%token OR
+%token ALT
+%token NOT
+%token RISE
+%token FALL
+%token MATCH
+%token NOMATCH
+%token RULESET
+%token ELSE
+%token UNKNOWN
+%token OTHERWISE
+%token MIN_AGGR
+%token MAX_AGGR
+%token AVG_AGGR
+%token SUM_AGGR
+%token COUNT_AGGR
+%token TIME_DOM
+%token INST_DOM
+%token HOST_DOM
+%token UNIT_SLASH
+%token INTERVAL
+%token <u> EVENT_UNIT
+%token <u> TIME_UNIT
+%token <u> SPACE_UNIT
+%token <d> NUMBER
+%token <s> IDENT
+%token <s> STRING
+%token TRU
+%token FALS
+%token <x> VAR
+
+%type <x> exp
+%type <x> rule
+%type <x> ruleset
+%type <x> rulelist
+%type <x> rulesetopt
+%type <x> act
+%type <x> bexp
+%type <x> rexp
+%type <x> actarg
+%type <x> arglist
+%type <x> aexp
+%type <x> quant
+%type <x> aggr
+%type <x> num
+%type <x> str
+%type <i> dom
+%type <x> fetch
+%type <s> metric
+%type <sa> hosts
+%type <sa> insts
+%type <t> times
+%type <u> units
+%type <u> unit
+
+%left NAME_DELIM
+%left ARROW
+%left AND OR SEQ ALT
+%left NOT RISE FALL
+%left ALL_QUANT SOME_QUANT PCNT_QUANT
+%left MATCH NOMATCH
+%left '>' '<' EQ_REL NEQ_REL GEQ_REL LEQ_REL
+%left '+' '-'
+%left '*' '/'
+%left UMINUS RATE
+%left SUM_AGGR AVG_AGGR MAX_AGGR MIN_AGGR COUNT_AGGR
+%left SHELL ALARM SYSLOG PRINT STOMP
+%left ':' '#' '@'
+%left UNIT_SLASH INTERVAL
+
+%%
+
+/***********************************************************************
+ * yacc productions
+ ***********************************************************************/
+
+stmnt : /* empty */
+ { parse = NULL; }
+ | IDENT '=' exp
+ { parse = statement($1, $3);
+ if ((agent || applet) && $3 != NULL &&
+ ($3->op == RULE || $3->op == ACT_SEQ ||
+ $3->op == ACT_ALT || $3->op == ACT_SHELL ||
+ $3->op == ACT_ALARM || $3->op == ACT_SYSLOG ||
+ $3->op == ACT_PRINT || $3->op == ACT_STOMP)) {
+ synerr();
+ fprintf(stderr, "operator %s not allowed in agent "
+ "mode\n", opStrings($3->op));
+ parse = NULL;
+ }
+ }
+ | exp
+ { parse = statement(NULL, $1);
+ if (agent) {
+ synerr();
+ fprintf(stderr, "expressions must be named in agent "
+ "mode\n");
+ parse = NULL;
+ }
+ }
+ ;
+
+exp : rule
+ { $$ = $1; }
+ | ruleset
+ { $$ = $1; }
+ | bexp
+ { $$ = $1; }
+ | aexp
+ { $$ = $1; }
+ | act
+ { $$ = $1; }
+ | str
+ { $$ = $1; }
+ ;
+
+rule : '(' rule ')'
+ { $$ = $2; }
+ | bexp ARROW act
+ { $$ = ruleExpr($1, $3); }
+
+ /* error reporting */
+ | error ARROW
+ { gramerr(bexp_str, precede, opStrings(RULE));
+ $$ = NULL; }
+ | bexp ARROW error
+ { gramerr(act_str, follow, opStrings(RULE));
+ $$ = NULL; }
+ ;
+
+ruleset : RULESET rulelist rulesetopt
+ { $$ = binaryExpr(CND_RULESET, $2, $3); }
+ ;
+
+rulelist : rule
+ { $$ = $1; }
+ | rule ELSE rulelist
+ /*
+ * use right recursion here so rules appear l-to-r in
+ * the expression tree to match the required order of
+ * evaluation
+ */
+ { $$ = binaryExpr(CND_OR, $1, $3); }
+ ;
+
+rulesetopt : UNKNOWN ARROW act
+ { $$ = binaryExpr(CND_OTHER, ruleExpr(boolConst(B_TRUE), $3), boolConst(B_FALSE)); }
+ | OTHERWISE ARROW act
+ { $$ = binaryExpr(CND_OTHER, boolConst(B_FALSE), ruleExpr(boolConst(B_TRUE), $3)); }
+ | UNKNOWN ARROW act OTHERWISE ARROW act
+ { $$ = binaryExpr(CND_OTHER, ruleExpr(boolConst(B_TRUE), $3), ruleExpr(boolConst(B_TRUE), $6)); }
+ | /* empty */
+ { $$ = NULL; }
+ ;
+
+act : '(' act ')'
+ { $$ = $2; }
+ | act SEQ act
+ { $$ = actExpr(ACT_SEQ, $1, $3); }
+ | act ALT act
+ { $$ = actExpr(ACT_ALT, $1, $3); }
+ | SHELL actarg
+ { $$ = actExpr(ACT_SHELL, $2, NULL); }
+ | SHELL num actarg /* holdoff format */
+ { $$ = actExpr(ACT_SHELL, $3, $2); }
+ | ALARM actarg
+ { $$ = actExpr(ACT_ALARM, $2, NULL); }
+ | ALARM num actarg /* holdoff format */
+ { $$ = actExpr(ACT_ALARM, $3, $2); }
+ | SYSLOG actarg
+ { do_syslog_args($2);
+ $$ = actExpr(ACT_SYSLOG, $2, NULL);
+ }
+ | SYSLOG num actarg /* holdoff format */
+ {
+ do_syslog_args($3);
+ $$ = actExpr(ACT_SYSLOG, $3, $2);
+ }
+ | PRINT actarg
+ { $$ = actExpr(ACT_PRINT, $2, NULL); }
+ | PRINT num actarg /* holdoff format */
+ { $$ = actExpr(ACT_PRINT, $3, $2); }
+ | STOMP actarg
+ {
+ stomping = 1;
+ $$ = actExpr(ACT_STOMP, $2, NULL);
+ }
+ | STOMP num actarg /* holdoff format */
+ {
+ stomping = 1;
+ $$ = actExpr(ACT_STOMP, $3, $2);
+ }
+
+ /* error reporting */
+ | error SEQ
+ { gramerr(act_str, precede, opStrings(ACT_SEQ));
+ $$ = NULL; }
+/*** following cause harmless shift/reduce conflicts ***/
+ | act SEQ error
+ { gramerr(act_str, follow, opStrings(ACT_SEQ));
+ $$ = NULL; }
+ | error ALT
+ { gramerr(act_str, precede, opStrings(ACT_ALT));
+ $$ = NULL; }
+ | act ALT error
+ { gramerr(act_str, follow, opStrings(ACT_ALT));
+ $$ = NULL; }
+/*** preceding cause harmless shift/reduce conflicts ***/
+ | SHELL error
+ { gramerr(tstr_str, follow, opStrings(ACT_SHELL));
+ $$ = NULL; }
+ | ALARM error
+ { gramerr(tstr_str, follow, opStrings(ACT_ALARM));
+ $$ = NULL; }
+ | SYSLOG error
+ { gramerr(tstr_str, follow, opStrings(ACT_SYSLOG));
+ $$ = NULL; }
+ | PRINT error
+ { gramerr(tstr_str, follow, opStrings(ACT_PRINT));
+ $$ = NULL; }
+ | STOMP error
+ { gramerr(tstr_str, follow, opStrings(ACT_STOMP));
+ $$ = NULL; }
+ ;
+
+actarg : arglist
+ { $$ = actArgExpr($1, NULL); }
+ ;
+
+arglist : STRING
+ { $$ = actArgList(NULL, $1); }
+ | STRING arglist
+ { $$ = actArgList($2, $1); }
+ ;
+
+bexp : '(' bexp ')'
+ { $$ = $2; }
+ | rexp
+ { $$ = $1; }
+ | quant
+ { $$ = $1; }
+ | TRU
+ { $$ = boolConst(B_TRUE); }
+ | FALS
+ { $$ = boolConst(B_FALSE); }
+ | NOT bexp
+ { $$ = unaryExpr(CND_NOT, $2); }
+ | RISE bexp
+ { $$ = boolMergeExpr(CND_RISE, $2); }
+ | FALL bexp
+ { $$ = boolMergeExpr(CND_FALL, $2); }
+ | bexp AND bexp
+ { $$ = binaryExpr(CND_AND, $1, $3); }
+ | bexp OR bexp
+ { $$ = binaryExpr(CND_OR, $1, $3); }
+ | MATCH str bexp
+ { /*
+ * note args are reversed so bexp is to the "left"
+ * of the operand node in the expr tree
+ */
+ $$ = binaryExpr(CND_MATCH, $3, $2); }
+ | NOMATCH str bexp
+ { $$ = binaryExpr(CND_NOMATCH, $3, $2); }
+
+ /* error reporting */
+ | NOT error
+ { gramerr(bexp_str, follow, opStrings(CND_NOT));
+ $$ = NULL; }
+ | RISE error
+ { gramerr(bexp_str, follow, opStrings(CND_RISE));
+ $$ = NULL; }
+ | FALL error
+ { gramerr(bexp_str, follow, opStrings(CND_FALL));
+ $$ = NULL; }
+ | MATCH error
+ { gramerr("regular expression", follow, opStrings(CND_MATCH));
+ $$ = NULL; }
+ | MATCH str error
+ { gramerr(bexp_str, follow, "regular expression");
+ $$ = NULL; }
+ | NOMATCH error
+ { gramerr("regular expression", follow, opStrings(CND_NOMATCH));
+ $$ = NULL; }
+ | NOMATCH str error
+ { gramerr(bexp_str, follow, "regular expression");
+ $$ = NULL; }
+/*** following cause harmless shift/reduce conflicts ***/
+ | error AND
+ { gramerr(bexp_str, precede, opStrings(CND_AND));
+ $$ = NULL; }
+ | bexp AND error
+ { gramerr(bexp_str, follow, opStrings(CND_AND));
+ $$ = NULL; }
+ | error OR
+ { gramerr(bexp_str, precede, opStrings(CND_OR));
+ $$ = NULL; }
+ | bexp OR error
+ { gramerr(bexp_str, follow, opStrings(CND_OR));
+ $$ = NULL; }
+/*** preceding cause harmless shift/reduce conflicts ***/
+ ;
+
+quant : ALL_QUANT dom bexp
+ { $$ = domainExpr(CND_ALL_HOST, $2, $3); }
+ | SOME_QUANT dom bexp
+ { $$ = domainExpr(CND_SOME_HOST, $2, $3); }
+ | NUMBER PCNT_QUANT dom bexp
+ { $$ = percentExpr($1, $3, $4); }
+
+ /* error reporting */
+ | ALL_QUANT dom error
+ { gramerr(bexp_str, follow, quant_str);
+ $$ = NULL; }
+ | SOME_QUANT dom error
+ { gramerr(bexp_str, follow, quant_str);
+ $$ = NULL; }
+ | NUMBER PCNT_QUANT dom error
+ { gramerr(bexp_str, follow, quant_str);
+ $$ = NULL; }
+ | error PCNT_QUANT
+ { gramerr(num_str, precede, pcnt_str);
+ $$ = NULL; }
+ ;
+
+rexp : aexp EQ_REL aexp
+ { $$ = relExpr(CND_EQ, $1, $3); }
+ | aexp NEQ_REL aexp
+ { $$ = relExpr(CND_NEQ, $1, $3); }
+ | aexp '<' aexp
+ { $$ = relExpr(CND_LT, $1, $3); }
+ | aexp '>' aexp
+ { $$ = relExpr(CND_GT, $1, $3); }
+ | aexp LEQ_REL aexp
+ { $$ = relExpr(CND_LTE, $1, $3); }
+ | aexp GEQ_REL aexp
+ { $$ = relExpr(CND_GTE, $1, $3); }
+
+ /* error reporting */
+ | error EQ_REL
+ { gramerr(aexp_str, precede, opStrings(CND_EQ));
+ $$ = NULL; }
+ | aexp EQ_REL error
+ { gramerr(aexp_str, follow, opStrings(CND_EQ));
+ $$ = NULL; }
+ | error NEQ_REL
+ { gramerr(aexp_str, precede, opStrings(CND_NEQ));
+ $$ = NULL; }
+ | aexp NEQ_REL error
+ { gramerr(aexp_str, follow, opStrings(CND_NEQ));
+ $$ = NULL; }
+ | error '<'
+ { gramerr(aexp_str, precede, opStrings(CND_LT));
+ $$ = NULL; }
+ | aexp '<' error
+ { gramerr(aexp_str, follow, opStrings(CND_LT));
+ $$ = NULL; }
+ | error '>'
+ { gramerr(aexp_str, precede, opStrings(CND_GT));
+ $$ = NULL; }
+ | aexp '>' error
+ { gramerr(aexp_str, follow, opStrings(CND_GT));
+ $$ = NULL; }
+ | error LEQ_REL
+ { gramerr(aexp_str, precede, opStrings(CND_LTE));
+ $$ = NULL; }
+ | aexp LEQ_REL error
+ { gramerr(aexp_str, follow, opStrings(CND_LTE));
+ $$ = NULL; }
+ | error GEQ_REL
+ { gramerr(aexp_str, precede, opStrings(CND_GTE));
+ $$ = NULL; }
+ | aexp GEQ_REL error
+ { gramerr(aexp_str, follow, opStrings(CND_GTE));
+ $$ = NULL; }
+ ;
+
+aexp : '(' aexp ')'
+ { $$ = $2; }
+ | fetch
+ { $$ = $1; }
+ | num
+ { $$ = $1; }
+ | VAR
+ { $$ = $1; }
+ | aggr
+ { $$ = $1; }
+ | RATE aexp
+ { $$ = numMergeExpr(CND_RATE, $2); }
+ | '-' aexp %prec UMINUS
+ { $$ = unaryExpr(CND_NEG, $2); }
+ | aexp '+' aexp
+ { $$ = binaryExpr(CND_ADD, $1, $3); }
+ | aexp '-' aexp
+ { $$ = binaryExpr(CND_SUB, $1, $3); }
+ | aexp '*' aexp
+ { $$ = binaryExpr(CND_MUL, $1, $3); }
+ | aexp '/' aexp
+ { $$ = binaryExpr(CND_DIV, $1, $3); }
+
+ /* error reporting */
+ | RATE error
+ { gramerr(aexp_str, follow, opStrings(CND_RATE));
+ $$ = NULL; }
+ | '-' error %prec UMINUS
+ { gramerr(aexp_str, follow, opStrings(CND_NEG));
+ $$ = NULL; }
+/*** following cause harmless shift/reduce conflicts ***/
+ | error '+'
+ { gramerr(aexp_str, precede, opStrings(CND_ADD));
+ $$ = NULL; }
+ | aexp '+' error
+ { gramerr(aexp_str, follow, opStrings(CND_ADD));
+ $$ = NULL; }
+ | error '-'
+ { gramerr(aexp_str, precede, opStrings(CND_SUB));
+ $$ = NULL; }
+ | aexp '-' error
+ { gramerr(aexp_str, follow, opStrings(CND_SUB));
+ $$ = NULL; }
+ | error '*'
+ { gramerr(aexp_str, precede, opStrings(CND_MUL));
+ $$ = NULL; }
+ | aexp '*' error
+ { gramerr(aexp_str, follow, opStrings(CND_MUL));
+ $$ = NULL; }
+ | error '/'
+ { gramerr(aexp_str, precede, opStrings(CND_DIV));
+ $$ = NULL; }
+ | aexp '/' error
+ { gramerr(aexp_str, follow, opStrings(CND_DIV));
+ $$ = NULL; }
+/*** preceding cause harmless shift/reduce conflicts ***/
+ ;
+
+aggr : SUM_AGGR dom aexp
+ { $$ = domainExpr(CND_SUM_HOST, $2, $3); }
+ | AVG_AGGR dom aexp
+ { $$ = domainExpr(CND_AVG_HOST, $2, $3); }
+ | MAX_AGGR dom aexp
+ { $$ = domainExpr(CND_MAX_HOST, $2, $3); }
+ | MIN_AGGR dom aexp
+ { $$ = domainExpr(CND_MIN_HOST, $2, $3); }
+ | COUNT_AGGR dom bexp
+ { $$ = domainExpr(CND_COUNT_HOST, $2, $3); }
+
+ /* error reporting */
+ | SUM_AGGR dom error
+ { gramerr(aexp_str, follow, aggr_str);
+ $$ = NULL; }
+ | AVG_AGGR dom error
+ { gramerr(aexp_str, follow, aggr_str);
+ $$ = NULL; }
+ | MAX_AGGR dom error
+ { gramerr(aexp_str, follow, aggr_str);
+ $$ = NULL; }
+ | MIN_AGGR dom error
+ { gramerr(aexp_str, follow, aggr_str);
+ $$ = NULL; }
+ ;
+
+dom : HOST_DOM
+ { $$ = HOST_DOM; }
+ | INST_DOM
+ { $$ = INST_DOM; }
+ | TIME_DOM
+ { $$ = TIME_DOM; }
+ ;
+
+fetch : metric hosts insts times
+ { $$ = fetchExpr($1, $2, $3, $4); }
+ ;
+
+metric : IDENT
+ { $$ = $1; }
+ ;
+
+hosts : /* empty */
+ { $$.n = 0;
+ $$.ss = NULL; }
+ | hosts ':' IDENT
+ { $$.n = $1.n + 1;
+ $$.ss = (char **) ralloc($1.ss, $$.n * sizeof(char *));
+ $$.ss[$1.n] = $3; }
+
+ /* error reporting */
+ | hosts ':' error
+ { gramerr(host_str, follow, ":");
+ $$.n = 0;
+ $$.ss = NULL; }
+ ;
+
+insts : /* empty */
+ { $$.n = 0;
+ $$.ss = NULL; }
+ | insts '#' IDENT
+ { $$.n = $1.n + 1;
+ $$.ss = (char **) ralloc($1.ss, $$.n * sizeof(char *));
+ $$.ss[$1.n] = $3; }
+
+ /* error reporting */
+ | insts '#' error
+ { gramerr(inst_str, follow, "#");
+ $$.n = 0;
+ $$.ss = NULL; }
+ ;
+
+times : /* empty */
+ { $$.t1 = 0;
+ $$.t2 = 0; }
+ | '@' NUMBER
+ { $$.t1 = $2;
+ $$.t2 = $2; }
+ | '@' NUMBER INTERVAL NUMBER
+ { if ($2 <= $4) {
+ $$.t1 = $2;
+ $$.t2 = $4;
+ }
+ else {
+ $$.t1 = $4;
+ $$.t2 = $2;
+ } }
+
+ /* error reporting */
+ | '@' error
+ { gramerr(sample_str, follow, "@");
+ $$.t1 = 0;
+ $$.t2 = 0; }
+ ;
+
+num : NUMBER units
+ { $$ = numConst($1, $2); }
+ ;
+
+units : /* empty */
+ { $$ = noUnits; }
+ | units unit
+ { $$ = $1;
+ if ($2.dimSpace) {
+ $$.dimSpace = $2.dimSpace;
+ $$.scaleSpace = $2.scaleSpace;
+ }
+ else if ($2.dimTime) {
+ $$.dimTime = $2.dimTime;
+ $$.scaleTime = $2.scaleTime;
+ }
+ else {
+ $$.dimCount = $2.dimCount;
+ $$.scaleCount = $2.scaleCount;
+ } }
+ | units UNIT_SLASH unit
+ { $$ = $1;
+ if ($3.dimSpace) {
+ $$.dimSpace = -$3.dimSpace;
+ $$.scaleSpace = $3.scaleSpace;
+ }
+ else if ($3.dimTime) {
+ $$.dimTime = -$3.dimTime;
+ $$.scaleTime = $3.scaleTime;
+ }
+ else {
+ $$.dimCount = -$3.dimCount;
+ $$.scaleCount = $3.scaleCount;
+ } }
+ ;
+
+unit : SPACE_UNIT
+ { $$ = $1; }
+ | SPACE_UNIT '^' NUMBER
+ { $$ = $1;
+ $$.dimSpace = $3; }
+ | TIME_UNIT
+ { $$ = $1; }
+ | TIME_UNIT '^' NUMBER
+ { $$ = $1;
+ $$.dimTime = $3; }
+ | EVENT_UNIT
+ { $$ = $1; }
+ | EVENT_UNIT '^' NUMBER
+ { $$ = $1;
+ $$.dimCount = $3; }
+ ;
+
+str : STRING
+ { $$ = strConst($1); }
+ ;
+
+%%
+
diff --git a/src/pmie/src/hdr.sk b/src/pmie/src/hdr.sk
new file mode 100644
index 0000000..98cd92b
--- /dev/null
+++ b/src/pmie/src/hdr.sk
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 1995-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.
+ *
+ * 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
+ */
+
+/***********************************************************************
+ * fun.c
+ *
+ * These functions are generated from skeletons <file>.sk
+ * by the shell-script './meta'.
+ ***********************************************************************/
+
+#include "pmapi.h"
+#include "impl.h"
+#if defined(HAVE_SYS_WAIT_H)
+#include <sys/wait.h>
+#endif
+#include "dstruct.h"
+#include "pragmatics.h"
+#include "fun.h"
+#include "show.h"
+#include "stomp.h"
+
+
diff --git a/src/pmie/src/lexicon.c b/src/pmie/src/lexicon.c
new file mode 100644
index 0000000..898205e
--- /dev/null
+++ b/src/pmie/src/lexicon.c
@@ -0,0 +1,925 @@
+/***********************************************************************
+ * lexicon.c - lexical scanner
+ *
+ * There is enough lookahead to enable use of '/' as both an arithmetic
+ * and units operator. Nested macro expansion is supported using a stack
+ * of input contexts (see definition of LexIn in file syntax.y).
+ ***********************************************************************
+ *
+ * Copyright (c) 1995-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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include <limits.h>
+#include <math.h>
+
+#include "dstruct.h"
+#include "syntax.h"
+#include "grammar.h"
+#include "lexicon.h"
+#include "pragmatics.h"
+
+
+/***********************************************************************
+ * type definitions
+ ***********************************************************************/
+
+/* for table of lexical items */
+typedef struct {
+ char *key;
+ int token;
+} LexEntry1;
+
+/* for another table of lexical items */
+typedef struct {
+ char *key;
+ int token;
+ int scale;
+} LexEntry2;
+
+
+/***********************************************************************
+ * constants
+ ***********************************************************************/
+
+static LexEntry1 domtab[] = {
+ {"host", HOST_DOM},
+ {"inst", INST_DOM},
+ {"sample", TIME_DOM},
+ {NULL, 0}
+};
+
+static LexEntry1 quantab[] = {
+ {"sum", SUM_AGGR},
+ {"avg", AVG_AGGR},
+ {"max", MAX_AGGR},
+ {"min", MIN_AGGR},
+ {"count", COUNT_AGGR},
+ {"all", ALL_QUANT},
+ {"some", SOME_QUANT},
+ {"%", PCNT_QUANT},
+ {NULL, 0}
+};
+
+static LexEntry1 optab[] = {
+ {"true", TRU},
+ {"false", FALS},
+ {"rate", RATE},
+ {"shell", SHELL},
+ {"alarm", ALARM},
+ {"syslog", SYSLOG},
+ {"print", PRINT},
+ {"stomp", STOMP},
+ {"rising", RISE},
+ {"falling", FALL},
+ {"match_inst", MATCH},
+ {"nomatch_inst",NOMATCH},
+ {"ruleset", RULESET},
+ {"else", ELSE},
+ {"unknown", UNKNOWN},
+ {"otherwise", OTHERWISE},
+ {NULL, 0}
+};
+
+static LexEntry2 unitab[] = {
+ {"byte", SPACE_UNIT, PM_SPACE_BYTE},
+ {"Kbyte", SPACE_UNIT, PM_SPACE_KBYTE},
+ {"Mbyte", SPACE_UNIT, PM_SPACE_MBYTE},
+ {"Gbyte", SPACE_UNIT, PM_SPACE_GBYTE},
+ {"Tbyte", SPACE_UNIT, PM_SPACE_TBYTE},
+ {"nsec", TIME_UNIT, PM_TIME_NSEC},
+ {"usec", TIME_UNIT, PM_TIME_USEC},
+ {"msec", TIME_UNIT, PM_TIME_MSEC},
+ {"sec", TIME_UNIT, PM_TIME_SEC},
+ {"min", TIME_UNIT, PM_TIME_MIN},
+ {"hour", TIME_UNIT, PM_TIME_HOUR},
+ {"count", EVENT_UNIT, 0},
+ {"Kcount", EVENT_UNIT, 3},
+ {"Mcount", EVENT_UNIT, 6},
+ {"nanosec", TIME_UNIT, PM_TIME_NSEC},
+ {"nanosecond", TIME_UNIT, PM_TIME_NSEC},
+ {"microsec", TIME_UNIT, PM_TIME_USEC},
+ {"microsecond", TIME_UNIT, PM_TIME_USEC},
+ {"millisec", TIME_UNIT, PM_TIME_MSEC},
+ {"millisecond", TIME_UNIT, PM_TIME_MSEC},
+ {"second", TIME_UNIT, PM_TIME_SEC},
+ {"minute", TIME_UNIT, PM_TIME_MIN},
+ {NULL, 0, 0}
+};
+
+
+
+/***********************************************************************
+ * variables
+ ***********************************************************************/
+
+ LexIn *lin; /* current input context */
+static char *token; /* current token buffer */
+
+
+
+/***********************************************************************
+ * local functions
+ ***********************************************************************/
+
+/* unwind input context */
+static void
+unwind(void)
+{
+ LexIn *tmp;
+
+ if (lin->name) {
+ free(lin->name);
+ if (! lin->macro)
+ fclose(lin->stream);
+ }
+ tmp = lin;
+ lin = lin->prev;
+ free(tmp);
+}
+
+
+/* next input character */
+static int
+nextc(void)
+{
+ int c = '\0';
+
+ if (lin) {
+ if (lin->lookin != lin->lookout) {
+ c = lin->look[lin->lookout++];
+ if (lin->lookout >= LEX_MAX + 2)
+ lin->lookout = 0;
+ }
+ else {
+ if (lin->macro)
+ c = *lin->macro++;
+ else {
+ c = getc(lin->stream);
+ if (c == EOF)
+ c = '\0';
+ }
+ if (c == '\0') {
+ unwind();
+ return nextc();
+ }
+ lin->cno++;
+ if (c == '\n') {
+ lin->lno++;
+ lin->cno = 0;
+ }
+ }
+#if PCP_DEBUG && PCP_DESPERATE
+ if (pmDebug & DBG_TRACE_APPL0)
+ fprintf(stderr, "nextc() -> \'%c\'\n", c);
+#endif
+ return c;
+ }
+ return EOF;
+}
+
+
+/* new file input context */
+static int
+inFile(char *name)
+{
+ FILE *f;
+ LexIn *t;
+
+ t = (LexIn *) zalloc(sizeof(LexIn));
+
+ if (name == NULL)
+ f = stdin;
+ else {
+ if ((f = fopen(name, "r")) == NULL) {
+ fprintf(stderr, "%s: cannot open config file %s\n", pmProgname, name);
+ free(t);
+ return 0;
+ }
+ t->name = (char *) alloc(strlen(name) + 1);
+ strcpy(t->name, name);
+ }
+
+ t->stream = f;
+ t->lno = 1;
+ t->cno = 0;
+ t->prev = lin;
+ lin = t;
+ return 1;
+}
+
+#define DEREF_ERROR 0
+#define DEREF_STRING 1
+#define DEREF_BOOL 2
+#define DEREF_NUMBER 3
+/*
+ * dereference macro ... return one of the DEREF_* values
+ * for DEREF_ERROR, error is reported here
+ */
+static int
+varDeref(char *name)
+{
+ Symbol s;
+ Expr *x;
+ LexIn *t;
+
+ /* lookup macro name */
+ if ((s = symLookup(&vars, name)) == NULL) {
+ fprintf(stderr, "undefined macro name $%s\n", name);
+ return DEREF_ERROR;
+ }
+ x = symValue(s);
+
+ /* string macro */
+ if (x->sem == SEM_CHAR) {
+ t = (LexIn *) zalloc(sizeof(LexIn));
+ t->prev = lin;
+ lin = t;
+ lin->name = (char *) alloc(strlen(name) + 1);
+ strcpy(lin->name, name);
+ lin->macro = (char *) x->ring;
+ lin->lno = 1;
+ lin->cno = 0;
+ return DEREF_STRING;
+ }
+
+ /* boolean valued macro */
+ if (x->sem == SEM_BOOLEAN) {
+ yylval.x = x;
+ return DEREF_BOOL;
+ }
+
+ /* constant numeric valued macro */
+ if (x->sem == SEM_NUMCONST) {
+ /*
+ * need to copy the Expr as the one returned here may be freed
+ * later after constant folding, and we need the real macro's
+ * value to be available for use in later rules
+ */
+ yylval.x = newExpr(NOP, NULL, NULL, -1, -1, -1, 1, SEM_NUMCONST);
+ yylval.x->smpls[0].ptr = x->smpls[0].ptr;
+ yylval.x->valid = 1;
+ return DEREF_NUMBER;
+ }
+
+ /* variable numeric valued macro */
+ if (x->sem == SEM_NUMVAR) {
+ yylval.x = x;
+ return DEREF_NUMBER;
+ }
+
+ fprintf(stderr, "varDeref(%s): internal botch sem=%d?\n", name, x->sem);
+ dumpExpr(x);
+ exit(1);
+}
+
+
+/* push character into lookahead queue */
+static void
+prevc(int c)
+{
+ if (lin) {
+ lin->look[lin->lookin++] = c;
+ if (lin->lookin >= LEX_MAX + 2)
+ lin->lookin = 0;
+ }
+}
+
+
+/* push string into lookahead queue */
+static void
+prevs(char *s)
+{
+ while (*s != '\0') {
+ lin->look[lin->lookin++] = *s++;
+ if (lin->lookin >= LEX_MAX + 2)
+ lin->lookin = 0;
+ }
+}
+
+/* get IDENT after '$' ... match rules for IDENT in main scanner */
+static int
+get_ident(char *namebuf)
+{
+ int c;
+ int d = 0;
+ int i;
+ char *namebuf_start = namebuf;
+
+ c = nextc();
+ if (c == '\'') {
+ d = c;
+ c = nextc();
+ }
+ if (!isalpha(c)) {
+ fprintf(stderr, "macro name must begin with an alphabetic (not '%c')\n", c);
+ lexSync();
+ return 0;
+ }
+ i = 0;
+ do {
+ if (c == '\\') c = nextc();
+ *namebuf++ = c;
+ i++;
+ c = nextc();
+ } while (i < LEX_MAX && c != EOF &&
+ (isalpha(c) || isdigit(c) || c == '_' || (d && c != d)));
+
+ if (i == LEX_MAX) {
+ namebuf_start[20] = '\0';
+ fprintf(stderr, "macro name too long: $%s...\n", namebuf_start);
+ lexSync();
+ return 0;
+ }
+ if (c == EOF) {
+ *namebuf = '\0';
+ fprintf(stderr, "unexpected end of file in macro name: $%s\n", namebuf_start);
+ lexSync();
+ return 0;
+ }
+
+ if (!d)
+ prevc(c);
+
+ *namebuf = '\0';
+
+#if PCP_DEBUG
+ if (pmDebug & DBG_TRACE_APPL0)
+ fprintf(stderr, "get_ident() -> macro name \"%s\"\n", namebuf_start);
+#endif
+
+ return 1;
+}
+
+
+/***********************************************************************
+ * exported functions
+ ***********************************************************************/
+
+/* initialize scan of new input stream */
+int
+lexInit(char *fname)
+{
+ lin = NULL;
+ if (! inFile(fname))
+ return 0;
+ token = (char *) alloc(LEX_MAX + 1);
+ return 1;
+}
+
+
+/* finalize scan of input stream */
+void
+lexFinal(void)
+{
+ free(token);
+}
+
+
+/* not end of input stream? */
+int lexMore(void)
+{
+ return (lin != NULL);
+}
+
+
+/* discard input to next ';' or EOF */
+void
+lexSync(void)
+{
+ int c;
+
+ do
+ c = nextc();
+ while (c != ';' && c != EOF)
+ ;
+ prevc(c);
+}
+
+
+/* scanner main function */
+int
+yylex(void)
+{
+ int c, d; /* current character */
+ int esc = 0; /* for escape processing */
+ static int ahead = 0; /* lookahead token */
+ int behind = 0; /* lookbehind token */
+ LexEntry1 *lt1; /* scans through lexbuf1 */
+ LexEntry2 *lt2; /* scans through lexbuf2 */
+ char *p, *q;
+ int i;
+ char nbuf[LEX_MAX+1]; /* for getting macro name */
+
+ /* token from previous invocation */
+ if (ahead) {
+ c = ahead;
+ ahead = 0;
+#if PCP_DEBUG
+ if (pmDebug & DBG_TRACE_APPL0)
+ fprintf(stderr, "yylex() -> TOKEN (ahead) \'%c\'\n", c);
+#endif
+ return c;
+ }
+
+ /* scan token from input */
+ c = nextc();
+ while (c != EOF) {
+
+ /* skip blanks */
+ while (isspace(c))
+ c = nextc();
+
+ /* scan C style comment */
+ if (c == '/') {
+ if ((d = nextc()) != '*')
+ prevc(d);
+ else {
+ c = nextc();
+ while (c != EOF) {
+ d = nextc();
+ if ((c == '*') && (d == '/')) {
+ c = nextc();
+ break;
+ }
+ c = d;
+ }
+ continue;
+ }
+ }
+
+ /* scan C++ style comment */
+ if (c == '/') {
+ if ((d = nextc()) != '/')
+ prevc(d);
+ else {
+ do c = nextc();
+ while (( c != '\n') && (c != EOF));
+ continue;
+ }
+ }
+
+ /* scan alphanumeric */
+ if (isalpha(c) || (c == '\'') || (c == '%')) {
+ d = c;
+ if (d == '\'') c = nextc();
+ i = 0;
+ do {
+ if (c == '$') {
+ /* macro embedded in identifier */
+ int sts;
+ if (!get_ident(nbuf))
+ return EOF;
+ sts = varDeref(nbuf);
+ if (sts == DEREF_ERROR) {
+ /* error reporting in varDeref() */
+ lexSync();
+ return EOF;
+ }
+ else if (sts != DEREF_STRING) {
+ synerr();
+ fprintf(stderr, "macro $%s not string valued as expected\n", nbuf);
+ lexSync();
+ return EOF;
+ }
+ c = nextc();
+ }
+ else {
+ if (c == '\\') c = nextc();
+ token[i++] = c;
+ c = nextc();
+ }
+ } while ((isalpha(c) || isdigit(c) ||
+ c == '.' || c == '_' || c == '\\' || c == '$' ||
+ (d == '\'' && c != d)) &&
+ (i < LEX_MAX));
+ token[i] = '\0';
+ if (d == '\'') c = nextc();
+
+ /*
+ * recognize keywords associated with units of space, time
+ * and count, see unitab[]
+ */
+ if (d != '\'') {
+ lt2 = &unitab[0];
+ if (i > 0 && token[i-1] == 's')
+ i--;
+ do {
+ if (strlen(lt2->key) == i &&
+ strncmp(token, lt2->key, i) == 0) {
+
+ /* if looking ahead after '/', return UNIT_SLASH */
+ if (behind == '/') {
+ prevs(&token[0]);
+ prevc(c);
+#if PCP_DEBUG
+ if (pmDebug & DBG_TRACE_APPL0)
+ fprintf(stderr, "yylex() -> OPERATOR \"/\"\n");
+#endif
+ return UNIT_SLASH;
+ }
+ prevc(c);
+
+ yylval.u = noUnits;
+ switch (lt2->token) {
+ case SPACE_UNIT:
+ yylval.u.dimSpace = 1;
+ yylval.u.scaleSpace = lt2->scale;
+ break;
+ case TIME_UNIT:
+ yylval.u.dimTime = 1;
+ yylval.u.scaleTime = lt2->scale;
+ break;
+ case EVENT_UNIT:
+ yylval.u.dimCount = 1;
+ yylval.u.scaleCount = lt2->scale;
+ break;
+ }
+#if PCP_DEBUG
+ if (pmDebug & DBG_TRACE_APPL0)
+ fprintf(stderr, "yylex() -> UNITS \"%s\"\n", pmUnitsStr(&yylval.u));
+#endif
+ return lt2->token;
+ }
+ lt2++;
+ } while (lt2->key);
+ }
+
+ /* if looking ahead, return previous token */
+ if (behind) {
+ prevs(&token[0]);
+ prevc(c);
+#if PCP_DEBUG
+ if (pmDebug & DBG_TRACE_APPL0)
+ fprintf(stderr, "yylex() -> TOKEN (behind) \'%c\'\n", behind);
+#endif
+ return behind;
+ }
+ prevc(c);
+
+ /* recognize aggregation and quantification */
+ if (d != '\'') {
+ if ((p = strchr(token, '_')) != NULL) {
+ *p = '\0';
+ lt1 = &quantab[0];
+ do {
+ if (strcmp(&token[0], lt1->key) == 0) {
+ c = lt1->token;
+ q = p + 1;
+ lt1 = &domtab[0];
+ do {
+ if (strcmp(q, lt1->key) == 0) {
+ ahead = lt1->token;
+#if PCP_DEBUG
+ if (pmDebug & DBG_TRACE_APPL0)
+ fprintf(stderr, "yylex() -> OPERATOR \"%s\'\n", token);
+#endif
+ return c;
+ }
+ lt1++;
+ } while (lt1->key);
+ break;
+ }
+ lt1++;
+ } while (lt1->key);
+ *p = '_';
+ }
+
+ /* recognize other reserved word */
+ lt1 = &optab[0];
+ do {
+ if (strcmp(&token[0], lt1->key) == 0) {
+#if PCP_DEBUG
+ if (pmDebug & DBG_TRACE_APPL0)
+ fprintf(stderr, "yylex() -> RESERVED-WORD \"%s\"\n", token);
+#endif
+ return lt1->token;
+ }
+ lt1++;
+ } while (lt1->key);
+ }
+
+ /* recognize identifier */
+ yylval.s = (char *) alloc(strlen(&token[0]) + 1);
+ strcpy(yylval.s, &token[0]);
+#if PCP_DEBUG
+ if (pmDebug & DBG_TRACE_APPL0)
+ fprintf(stderr, "yylex() -> IDENT \"%s\"\n", token);
+#endif
+ return IDENT;
+ }
+
+ /* if looking ahead, return preceding token */
+ if (behind) {
+ prevc(c);
+#if PCP_DEBUG
+ if (pmDebug & DBG_TRACE_APPL0)
+ fprintf(stderr, "yylex() -> TOKEN (behind) \'%c\'\n", behind);
+#endif
+ return behind;
+ }
+
+ /* special case for .[0-9]... number without leading [0-9] */
+ if (c == '.') {
+ c = nextc();
+ if (isdigit(c)) {
+ /* note prevc() implements a FIFO, not a stack! */
+ prevc('.'); /* push back period */
+ prevc(c); /* push back digit */
+ c = '0'; /* start with fake leading zero */
+ }
+ else
+ prevc(c); /* not a digit after period, push back */
+ }
+
+ /* scan NUMBER */
+ if (isdigit(c)) {
+ int flote = 0;
+ i = 0;
+ do {
+ token[i++] = c;
+ c = nextc();
+ if ((flote == 0) && (c == '.') && (i < LEX_MAX)) {
+ c = nextc();
+ if (c == '.')
+ prevc(c); /* INTERVAL token */
+ else {
+ flote = 1;
+ token[i++] = '.';
+ }
+ }
+ if ((flote <= 1) && (i < LEX_MAX) && ((c == 'e') || (c == 'E'))) {
+ flote = 2;
+ token[i++] = c;
+ c = nextc();
+ }
+ if ((flote <= 2) && (c == '-') && (i < LEX_MAX)) {
+ flote = 3;
+ token[i++] = c;
+ c = nextc();
+ }
+ } while (isdigit(c) && (i < LEX_MAX));
+ prevc(c);
+ token[i] = '\0';
+ yylval.d = strtod(&token[0], NULL);
+#if PCP_DEBUG
+ if (pmDebug & DBG_TRACE_APPL0)
+ fprintf(stderr, "yylex() -> NUMBER %g\n", yylval.d);
+#endif
+ return NUMBER;
+ }
+
+ /* scan string */
+ if (c == '"') {
+ yylval.s = NULL;
+ i = 0;
+ c = nextc();
+ while ((c != '"') && (c != EOF) && (i < LEX_MAX)) {
+
+ /* escape character */
+ if (c == '\\') {
+ esc = 1;
+ c = nextc();
+ switch (c) {
+ case 'n':
+ c = '\n';
+ break;
+ case 't':
+ c = '\t';
+ break;
+ case 'v':
+ c = '\v';
+ break;
+ case 'b':
+ c = '\b';
+ break;
+ case 'r':
+ c = '\r';
+ break;
+ case 'f':
+ c = '\f';
+ break;
+ case 'a':
+ c = '\a';
+ break;
+ }
+ }
+ else
+ esc = 0;
+
+ /* macro embedded in string */
+ if (c == '$' && !esc) {
+ int sts;
+ if (!get_ident(nbuf))
+ return EOF;
+ sts = varDeref(nbuf);
+ if (sts == DEREF_ERROR) {
+ /* error reporting in varDeref() */
+ lexSync();
+ return EOF;
+ }
+ else if (sts != DEREF_STRING) {
+ synerr();
+ fprintf(stderr, "macro $%s not string valued as expected\n", nbuf);
+ lexSync();
+ return EOF;
+ }
+ c = nextc();
+ }
+
+ /* add character to string */
+ yylval.s = (char *) ralloc(yylval.s, i+2);
+ yylval.s[i++] = c;
+ c = nextc();
+ }
+ if (i == 0) {
+ /* special case for null string */
+ yylval.s = (char *) ralloc(yylval.s, 1);
+ }
+ yylval.s[i++] = '\0';
+#if PCP_DEBUG
+ if (pmDebug & DBG_TRACE_APPL0)
+ fprintf(stderr, "yylex() -> STRING \"%s\"\n", yylval.s);
+#endif
+ return STRING;
+ }
+
+ /* scan operator */
+ switch (c) {
+ case ';':
+ case '}':
+ do
+ d = nextc();
+ while (isspace(d));
+ if (d == '.') {
+ while (lin)
+ unwind();
+ }
+ else
+ prevc(d);
+#if PCP_DEBUG
+ if (pmDebug & DBG_TRACE_APPL0)
+ fprintf(stderr, "yylex() -> END-OF-RULE\n");
+#endif
+ return EOF;
+
+ case '$':
+ if (!get_ident(nbuf))
+ return EOF;
+ switch (varDeref(nbuf)) {
+ case DEREF_ERROR:
+ lexSync();
+ return EOF;
+ case DEREF_STRING:
+ c = nextc();
+ continue;
+ case DEREF_BOOL:
+#if PCP_DEBUG
+ if (pmDebug & DBG_TRACE_APPL0)
+ fprintf(stderr, "yylex() -> (boolean) macro $%s\n", nbuf);
+#endif
+ return VAR;
+ case DEREF_NUMBER:
+#if PCP_DEBUG
+ if (pmDebug & DBG_TRACE_APPL0)
+ fprintf(stderr, "yylex() -> (numeric) macro $%s\n", nbuf);
+#endif
+ return VAR;
+ }
+ /*NOTREACHED*/
+ break;
+
+ case '/':
+ behind = c;
+ c = nextc();
+ continue;
+ case '-':
+ if ((d = nextc()) == '>') {
+#if PCP_DEBUG
+ if (pmDebug & DBG_TRACE_APPL0)
+ fprintf(stderr, "yylex() -> OPERATOR \"->\"\n");
+#endif
+ return ARROW;
+ }
+ prevc(d);
+ break;
+ case '=':
+ if ((d = nextc()) == '=') {
+#if PCP_DEBUG
+ if (pmDebug & DBG_TRACE_APPL0)
+ fprintf(stderr, "yylex() -> OPERATOR \"==\"\n");
+#endif
+ return EQ_REL;
+ }
+ prevc(d);
+ break;
+ case '!':
+ if ((d = nextc()) == '=') {
+#if PCP_DEBUG
+ if (pmDebug & DBG_TRACE_APPL0)
+ fprintf(stderr, "yylex() -> OPERATOR \"!=\"\n");
+#endif
+ return NEQ_REL;
+ }
+ prevc(d);
+#if PCP_DEBUG
+ if (pmDebug & DBG_TRACE_APPL0)
+ fprintf(stderr, "yylex() -> OPERATOR \"!\"\n");
+#endif
+ return NOT;
+ case '<':
+ if ((d = nextc()) == '=') {
+#if PCP_DEBUG
+ if (pmDebug & DBG_TRACE_APPL0)
+ fprintf(stderr, "yylex() -> OPERATOR \"<=\"\n");
+#endif
+ return LEQ_REL;
+ }
+ prevc(d);
+ break;
+ case '>':
+ if ((d = nextc()) == '=') {
+#if PCP_DEBUG
+ if (pmDebug & DBG_TRACE_APPL0)
+ fprintf(stderr, "yylex() -> OPERATOR \">=\"\n");
+#endif
+ return GEQ_REL;
+ }
+ prevc(d);
+ break;
+ case '&':
+ if ((d = nextc()) == '&') {
+#if PCP_DEBUG
+ if (pmDebug & DBG_TRACE_APPL0)
+ fprintf(stderr, "yylex() -> OPERATOR \"&&\"\n");
+#endif
+ return AND;
+ }
+ prevc(d);
+#if PCP_DEBUG
+ if (pmDebug & DBG_TRACE_APPL0)
+ fprintf(stderr, "yylex() -> OPERATOR \"&\"\n");
+#endif
+ return SEQ;
+ case '|':
+ if ((d = nextc()) == '|') {
+#if PCP_DEBUG
+ if (pmDebug & DBG_TRACE_APPL0)
+ fprintf(stderr, "yylex() -> OPERATOR \"||\"\n");
+#endif
+ return OR;
+ }
+ prevc(d);
+#if PCP_DEBUG
+ if (pmDebug & DBG_TRACE_APPL0)
+ fprintf(stderr, "yylex() -> OPERATOR \"|\"\n");
+#endif
+ return ALT;
+ case '.':
+ if ((d = nextc()) == '.') {
+#if PCP_DEBUG
+ if (pmDebug & DBG_TRACE_APPL0)
+ fprintf(stderr, "yylex() -> OPERATOR \"..\"\n");
+#endif
+ return INTERVAL;
+ }
+ prevc(d);
+ break;
+
+ }
+#if PCP_DEBUG
+ if (pmDebug & DBG_TRACE_APPL0) {
+ if (c == EOF)
+ fprintf(stderr, "yylex() -> EOF\n");
+ else
+ fprintf(stderr, "yylex() -> TOKEN \'%c\' (0x%x)\n", c, c & 0xff);
+ }
+#endif
+ return c;
+ }
+
+#if PCP_DEBUG
+ if (pmDebug & DBG_TRACE_APPL0)
+ fprintf(stderr, "yylex() -> EOF\n");
+#endif
+ return EOF;
+}
+
+
+
diff --git a/src/pmie/src/lexicon.h b/src/pmie/src/lexicon.h
new file mode 100644
index 0000000..7c2c16a
--- /dev/null
+++ b/src/pmie/src/lexicon.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 1995 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/***********************************************************************
+ * lexicon.h - lexical scanner
+ ***********************************************************************/
+
+#ifndef LEXICON_H
+#define LEXICON_H
+
+/***********************************************************************
+ * ONLY FOR USE BY: lexicon.c and syntax.c
+ ***********************************************************************/
+
+#define LEX_MAX 254 /* max length of token */
+
+/* scanner input context stack entry */
+typedef struct lexin {
+ struct lexin *prev; /* calling context on stack */
+ FILE *stream; /* rule input stream */
+ char *macro; /* input from macro definition */
+ char *name; /* file/macro name */
+ int lno; /* current line number */
+ int cno; /* current column number */
+ int lookin; /* lookahead buffer input index */
+ int lookout; /* lookahead buffer output index */
+ signed char look[LEX_MAX + 2]; /* lookahead ring buffer */
+} LexIn;
+
+extern LexIn *lin; /* current input context */
+
+
+/***********************************************************************
+ * public
+ ***********************************************************************/
+
+/* initialize scan of new input file */
+int lexInit(char *);
+
+/* finalize scan of input stream */
+void lexFinal(void);
+
+/* not end of input stream? */
+int lexMore(void);
+
+/* discard input until ';' or EOF */
+void lexSync(void);
+
+/* scanner main function */
+int yylex(void);
+
+/* yacc parser */
+int yyparse(void);
+
+#endif /* LEXICON_H */
+
diff --git a/src/pmie/src/logger.h b/src/pmie/src/logger.h
new file mode 100644
index 0000000..534f4dc
--- /dev/null
+++ b/src/pmie/src/logger.h
@@ -0,0 +1,83 @@
+/*
+ * Provided by Alan Hoyt <ahoyt@moser-inc.com> as part of the Solaris port,
+ * this code came from
+ * http://www.mit.edu/afs/sipb/service/logging/arch/sun4x_55/build/zephyr/clients/syslogd/syslogd.c-test1
+ *
+ * Copyright (c) 1983, 1988 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the University of California, Berkeley. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#define NOPRI 0x10 /* the "no priority" priority */
+#define LOG_MAKEPRI(f,p) (((f) << 3) + (p))
+#define LOG_MARK LOG_MAKEPRI(LOG_NFACILITIES, 0) /* mark "facility" */
+
+#ifndef LOG_PRIMASK
+#define LOG_PRIMASK 0x07
+#endif
+#ifndef LOG_FACMASK
+#define LOG_FACMASK 0x03f8
+#endif
+
+typedef struct code {
+ char *c_name;
+ int c_val;
+} CODE;
+
+struct code prioritynames[] = {
+ { "panic", LOG_EMERG },
+ { "alert", LOG_ALERT },
+ { "crit", LOG_CRIT },
+ { "error", LOG_ERR },
+ { "warning", LOG_WARNING },
+ { "notice", LOG_NOTICE },
+ { "info", LOG_INFO },
+ { "debug", LOG_DEBUG },
+ { "none", NOPRI },
+ { "emerg", LOG_EMERG },
+ { "err", LOG_ERR },
+ { "warn", LOG_WARNING },
+ { NULL, -1 }
+};
+
+struct code facilitynames[] = {
+ { "daemon", LOG_DAEMON },
+#ifndef IS_MINGW
+ { "kern", LOG_KERN },
+ { "user", LOG_USER },
+ { "mail", LOG_MAIL },
+ { "auth", LOG_AUTH },
+ { "syslog", LOG_SYSLOG },
+ { "lpr", LOG_LPR },
+ { "news", LOG_NEWS },
+ { "uucp", LOG_UUCP },
+ { "cron", LOG_CRON },
+ { "reserved", -1 },
+ { "reserved", -1 },
+ { "reserved", -1 },
+ { "cron", LOG_CRON },
+ { "local0", LOG_LOCAL0 },
+ { "local1", LOG_LOCAL1 },
+ { "local2", LOG_LOCAL2 },
+ { "local3", LOG_LOCAL3 },
+ { "local4", LOG_LOCAL4 },
+ { "local5", LOG_LOCAL5 },
+ { "local6", LOG_LOCAL6 },
+ { "local7", LOG_LOCAL7 },
+ { "security", LOG_AUTH },
+ { "mark", LOG_MARK },
+#endif
+ { NULL, -1 }
+};
+
diff --git a/src/pmie/src/match_inst.c b/src/pmie/src/match_inst.c
new file mode 100644
index 0000000..26a1d1c
--- /dev/null
+++ b/src/pmie/src/match_inst.c
@@ -0,0 +1,135 @@
+/*
+ * Copyright (c) 1999 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <regex.h>
+#include "pmapi.h" /* _pmCtime only */
+#include "impl.h" /* _pmCtime only */
+#include "dstruct.h"
+#include "fun.h"
+#include "show.h"
+
+/*
+ * x-arg1 is the bexp, x->arg2 is the regex
+ */
+void
+cndMatch_inst(Expr *x)
+{
+ Expr *arg1 = x->arg1;
+ Expr *arg2 = x->arg2;
+ Boolean *ip1;
+ Boolean *op;
+ int n;
+ int i;
+ int sts;
+ int mi;
+ Metric *m;
+
+ EVALARG(arg1)
+ ROTATE(x)
+
+
+#if PCP_DEBUG
+ if (pmDebug & DBG_TRACE_APPL2) {
+ fprintf(stderr, "match_inst(" PRINTF_P_PFX "%p): regex handle=" PRINTF_P_PFX "%p desire %s\n",
+ x, arg2->ring, x->op == CND_MATCH ? "match" : "nomatch");
+ dumpExpr(x);
+ }
+#endif
+
+ if (arg2->sem != SEM_REGEX) {
+ fprintf(stderr, "cndMatch_inst: internal botch arg2 not SEM_REGEX?\n");
+ dumpExpr(arg2);
+ exit(1);
+ }
+
+ if (arg1->tspan > 0) {
+
+ mi = 0;
+ m = &arg1->metrics[mi++];
+ i = 0;
+ ip1 = (Boolean *)(&arg1->smpls[0])->ptr;
+ op = (Boolean *)(&x->smpls[0])->ptr;
+
+ for (n = 0; n < arg1->tspan; n++) {
+
+ if (!arg2->valid || !arg1->valid) {
+ *op++ = B_UNKNOWN;
+ }
+ else if (x->e_idom <= 0) {
+ *op++ = B_FALSE;
+ }
+ else {
+ while (i >= m->m_idom) {
+ /*
+ * no more values, next metric
+ */
+ m = &arg1->metrics[mi++];
+ i = 0;
+ }
+
+ if (m->inames == NULL) {
+ *op++ = B_FALSE;
+ }
+ else {
+ sts = regexec((regex_t *)arg2->ring, m->inames[i], 0, NULL, 0);
+#if PCP_DEBUG
+ if (pmDebug & DBG_TRACE_APPL2) {
+ if (x->op == CND_MATCH && sts != REG_NOMATCH) {
+ fprintf(stderr, "match_inst: inst=\"%s\" match && %s\n",
+ m->inames[i],
+ *ip1 == B_TRUE ? "true" :
+ (*ip1 == B_FALSE ? "false" :
+ (*ip1 == B_UNKNOWN ? "unknown" : "bogus" )));
+
+ }
+ else if (x->op == CND_NOMATCH && sts == REG_NOMATCH) {
+ fprintf(stderr, "match_inst: inst=\"%s\" nomatch && %s\n",
+ m->inames[i],
+ *ip1 == B_TRUE ? "true" :
+ (*ip1 == B_FALSE ? "false" :
+ (*ip1 == B_UNKNOWN ? "unknown" : "bogus" )));
+ }
+ }
+#endif
+ if ((x->op == CND_MATCH && sts != REG_NOMATCH) ||
+ (x->op == CND_NOMATCH && sts == REG_NOMATCH))
+ *op++ = *ip1 && B_TRUE;
+ else
+ *op++ = *ip1 && B_FALSE;
+ }
+ i++;
+ }
+ ip1++;
+ }
+ x->valid++;
+ }
+ else
+ x->valid = 0;
+
+ x->smpls[0].stamp = arg1->smpls[0].stamp;
+
+#if PCP_DEBUG
+ if (pmDebug & DBG_TRACE_APPL2) {
+ fprintf(stderr, "cndMatch_inst(" PRINTF_P_PFX "%p) ...\n", x);
+ dumpExpr(x);
+ }
+#endif
+}
+
diff --git a/src/pmie/src/merge.sk b/src/pmie/src/merge.sk
new file mode 100644
index 0000000..ea3e814
--- /dev/null
+++ b/src/pmie/src/merge.sk
@@ -0,0 +1,103 @@
+/*
+ * Copyright (c) 1995-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.
+ *
+ * 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
+ */
+
+/***********************************************************************
+ * skeleton: merge.sk
+ ***********************************************************************/
+
+/*
+ * operator: @FUN
+ */
+
+void
+@FUN_n(Expr *x)
+{
+ Expr *arg1 = x->arg1;
+ Sample *is1 = &arg1->smpls[0];
+ Sample *is2 = &arg1->smpls[1];
+ Sample *os = &x->smpls[0];
+ @ITYPE *ip1;
+ @ITYPE *ip2;
+ @OTYPE *op;
+ RealTime delta;
+ int n;
+ int i;
+
+ EVALARG(arg1)
+ ROTATE(x)
+
+ if (arg1->valid >= 2 && x->tspan > 0) {
+ ip1 = (@ITYPE *)is1->ptr;
+ ip2 = (@ITYPE *)is2->ptr;
+ op = (@OTYPE *)os->ptr;
+ n = x->tspan;
+ @DELTA
+ for (i = 0; i < n; i++) {
+ *op = *ip1++ @OP *ip2++;
+ @SCALE
+ op++;
+ }
+ os->stamp = is1->stamp;
+ x->valid++;
+ }
+ else x->valid = 0;
+
+#if PCP_DEBUG
+ if (pmDebug & DBG_TRACE_APPL2) {
+ fprintf(stderr, "@FUN_n(" PRINTF_P_PFX "%p) ...\n", x);
+ dumpExpr(x);
+ }
+#endif
+}
+
+void
+@FUN_1(Expr *x)
+{
+ Expr *arg1 = x->arg1;
+ Sample *is1 = &arg1->smpls[0];
+ Sample *is2 = &arg1->smpls[1];
+ Sample *os = &x->smpls[0];
+ @ITYPE *ip1;
+ @ITYPE *ip2;
+ @OTYPE *op;
+ RealTime delta;
+
+ EVALARG(arg1)
+ ROTATE(x)
+
+ if (arg1->valid >= 2) {
+ ip1 = (@ITYPE *)is1->ptr;
+ ip2 = (@ITYPE *)is2->ptr;
+ op = (@OTYPE *)os->ptr;
+ @DELTA
+ *op = *ip1 @OP *ip2;
+ @SCALE
+ os->stamp = is1->stamp;
+ x->valid++;
+ }
+ else x->valid = 0;
+
+#if PCP_DEBUG
+ if (pmDebug & DBG_TRACE_APPL2) {
+ fprintf(stderr, "@FUN_1(" PRINTF_P_PFX "%p) ...\n", x);
+ dumpExpr(x);
+ }
+#endif
+}
+
+
diff --git a/src/pmie/src/meta b/src/pmie/src/meta
new file mode 100755
index 0000000..00074ef
--- /dev/null
+++ b/src/pmie/src/meta
@@ -0,0 +1,299 @@
+#!/bin/sh
+#
+# Copyright (c) 1997 Silicon Graphics, Inc. All Rights Reserved.
+#
+# This program is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by the
+# Free Software Foundation; either version 2 of the License, or (at your
+# option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+# for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+#
+# Generate evaluator functions from skeletons.
+#
+
+##############
+# procedures #
+##############
+
+CULLCOPYRIGHT="/^ \* Copyright.* Silicon Graphics.*$/d"
+
+_fetch()
+{
+fin=fetch.sk
+sed -e "$CULLCOPYRIGHT" $fin >> $fout
+}
+
+_misc()
+{
+fin=misc.sk
+sed -e "$CULLCOPYRIGHT" \
+ -e "s/@ITYPE/$itype/g" \
+ -e "s/@OTYPE/$otype/g" \
+ $fin >> $fout
+}
+
+_aggr()
+{
+fin=aggregate.sk
+sed -e "$CULLCOPYRIGHT" \
+ -e "s/@FUN/$fun/g" \
+ -e "s/@ITYPE/$itype/g" \
+ -e "s/@OTYPE/$otype/g" \
+ -e "s/@TTYPE/$ttype/g" \
+ -e "s/@TOP/$top/g" \
+ -e "s/@LOOP/$loop/g" \
+ -e "s/@BOT/$bot/g" \
+ -e "s/@NOTVALID/$notvalid/g" \
+ $fin >> $fout
+}
+
+_unary()
+{
+fin=unary.sk
+sed -e "$CULLCOPYRIGHT" \
+ -e "s/@FUN/$fun/g" \
+ -e "s/@ITYPE/$itype/g" \
+ -e "s/@OTYPE/$otype/g" \
+ -e "s/@OP/$op/g" \
+ $fin >> $fout
+}
+
+_binary()
+{
+fin=binary.sk
+sed -e "$CULLCOPYRIGHT" \
+ -e "s/@FUN/$fun/g" \
+ -e "s/@ITYPE/$itype/g" \
+ -e "s/@OTYPE/$otype/g" \
+ -e "s/@OP/$op/g" \
+ $fin >> $fout
+}
+
+_merge()
+{
+fin=merge.sk
+if [ -z "$scale" ]
+then
+ sed -e '/RealTime/d' $fin
+else
+ cat $fin
+fi \
+| sed -e "$CULLCOPYRIGHT" \
+ -e "s/@FUN/$fun/g" \
+ -e "s/@ITYPE/$itype/g" \
+ -e "s/@OTYPE/$otype/g" \
+ -e "s/@OP/$op/g" \
+ -e "s/@DELTA/$delta/g" \
+ -e "s/@SCALE/$scale/g" \
+ >> $fout
+}
+
+_act()
+{
+fin=act.sk
+sed -e "$CULLCOPYRIGHT" $fin >> $fout
+}
+
+
+
+########
+# main #
+########
+
+fout=fun.c
+rm -f $fout
+cat hdr.sk > $fout
+
+
+#
+# fetch
+#
+_fetch
+
+#
+# rule and delay
+#
+itype=double
+otype=double
+
+_misc
+
+#
+# aggregation operators
+#
+itype=double
+otype=double
+ttype=double
+notvalid="x->valid = 0;"
+
+fun=cndSum
+top="a = *ip;"
+loop="a += *ip;"
+bot="*op++ = a;"
+_aggr
+
+fun=cndAvg
+top="a = *ip;"
+loop="a += *ip;"
+bot="*op++ = a \/ n;"
+_aggr
+
+fun=cndMax
+top="a = *ip;"
+loop="if (*ip > a) a = *ip;"
+bot="*op++ = a;"
+_aggr
+
+fun=cndMin
+top="a = *ip;"
+loop="if (*ip < a) a = *ip;"
+bot="*op++ = a;"
+_aggr
+
+#
+# arithmetic operators
+#
+itype=double
+otype=double
+ttype=double
+
+fun=cndNeg
+op="OP(x) -(x)"
+_unary
+
+fun=cndAdd
+op="OP(x,y) ((x) + (y))"
+_binary
+
+fun=cndSub
+op="OP(x,y) ((x) - (y))"
+_binary
+
+fun=cndMul
+op="OP(x,y) ((x) * (y))"
+_binary
+
+fun=cndDiv
+op="OP(x,y) ((x) \/ (y))"
+_binary
+
+fun=cndRate
+delta="delta = is1->stamp - is2->stamp;"
+op="-"
+scale="*op = *op \\/ delta;"
+_merge
+
+#
+# relational operators
+#
+itype=double
+otype=Boolean
+ttype=Boolean
+
+fun=cndEq
+op="OP(x,y) ((x) == (y))"
+_binary
+
+fun=cndNeq
+op="OP(x,y) ((x) != (y))"
+_binary
+
+fun=cndLt
+op="OP(x,y) ((x) < (y))"
+_binary
+
+fun=cndLte
+op="OP(x,y) ((x) <= (y))"
+_binary
+
+fun=cndGt
+op="OP(x,y) ((x) > (y))"
+_binary
+
+fun=cndGte
+op="OP(x,y) ((x) >= (y))"
+_binary
+
+#
+# boolean connectives
+#
+itype=Boolean
+otype=Boolean
+ttype=Boolean
+
+fun=cndNot
+op="OP(x) (((x) == B_TRUE || (x) == B_FALSE) ? !(x) : B_UNKNOWN)"
+_unary
+
+fun=cndRise
+delta=""
+op=">"
+scale=""
+_merge
+
+fun=cndFall
+delta=""
+op="<"
+scale=""
+_merge
+
+#
+# quantifiers
+#
+itype=Boolean
+otype=Boolean
+ttype=Boolean
+
+fun=cndAll
+top="a = *ip;"
+loop="if (*ip == B_FALSE) a = B_FALSE;\\
+ else if (*ip == B_UNKNOWN \\&\\& a != B_UNKNOWN) a = B_UNKNOWN;"
+bot="*op++ = a;"
+notvalid="*op++ = B_UNKNOWN; os->stamp = is->stamp; x->valid++;"
+_aggr
+
+fun=cndSome
+top="a = *ip;"
+loop="if (*ip == B_TRUE) a = B_TRUE;\\
+ else if (*ip == B_UNKNOWN \\&\\& a != B_UNKNOWN) a = B_UNKNOWN;"
+bot="*op++ = a;"
+notvalid="*op++ = B_UNKNOWN; os->stamp = is->stamp; x->valid++;"
+_aggr
+
+fun=cndPcnt
+ttype='int '
+top="a = *ip;"
+loop="a += *ip;"
+bot="*op++ = (a >= (int)(0.5 + *(double *)x->arg2->ring * n)) ? B_TRUE : B_FALSE;"
+notvalid="*op++ = B_UNKNOWN; os->stamp = is->stamp; x->valid++;"
+_aggr
+
+#
+# truth counter
+#
+itype=Boolean
+otype=double
+notvalid="x->valid = 0;"
+
+fun=cndCount
+top="a = *ip == B_TRUE ? 1 : 0;"
+loop="if (*ip == B_TRUE) a++;"
+bot="*op++ = a;"
+_aggr
+
+#
+# actions
+#
+_act
+
+# discourage changes to fun.c
+#
+chmod 444 $fout
diff --git a/src/pmie/src/misc.sk b/src/pmie/src/misc.sk
new file mode 100644
index 0000000..30095f4
--- /dev/null
+++ b/src/pmie/src/misc.sk
@@ -0,0 +1,176 @@
+/*
+ * Copyright (c) 1995-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.
+ *
+ * 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
+ */
+
+/***********************************************************************
+ * skeleton: misc.sk
+ ***********************************************************************/
+
+#include <assert.h>
+
+/*
+ * operator RULE
+ */
+
+void
+rule(Expr *x)
+{
+ Expr *arg1 = x->arg1;
+ Expr *arg2 = x->arg2;
+ int sts;
+
+ EVALARG(arg1)
+ if ((x->valid = arg1->valid) > 0) {
+ sts = (*(Boolean *)x->ring = *(Boolean *)arg1->ring);
+ if (sts == B_FALSE)
+ perf->eval_false++;
+ else if (sts == B_TRUE) {
+ perf->eval_true++;
+ EVALARG(arg2)
+ }
+ else
+ perf->eval_unknown++;
+ }
+ else
+ perf->eval_unknown++;
+}
+
+/*
+ * operator CND_RULESET
+ */
+
+void
+ruleset(Expr *x)
+{
+ Expr *op; /* operator nodes */
+ Expr *rp; /* rule or action nodes */
+ Expr *save_curr; /* save-restore curr when calling rule() */
+ Expr *other; /* UNKNOWN/OTHERWISE clauses */
+
+ x->valid = 0;
+ op = x->arg1;
+ while (op != NULL) {
+ if (op->op == RULE)
+ rp = op;
+ else {
+ assert(op->op == CND_RULESET || op->op == CND_OR);
+ rp = op->arg1;
+ }
+ assert(rp->op == RULE);
+ save_curr = curr;
+ curr = rp;
+ rule(rp);
+ curr = save_curr;
+ if (rp->arg1->valid) {
+ x->valid = rp->arg1->valid;
+ *(Boolean *)x->ring = *(Boolean *)rp->arg1->ring;
+ if (x->valid > 0 && *(Boolean *)x->ring == B_TRUE) {
+ /* predicate is true, so stop evaluation */
+ return;
+ }
+ }
+ if (op->op == RULE)
+ break;
+ op = op->arg2;
+ }
+
+ if (x->arg2 == NULL)
+ /* no OTHERWISE or UNKNOWN clauses */
+ return;
+
+ other = x->arg2;
+ assert(other->op == CND_OTHER);
+
+ if (x->valid == 0) {
+ /*
+ * all predicates are B_UNKNOWN, so do the UNKNOWN action if any
+ */
+ if (other->arg1->op != NOP) {
+ rp = other->arg1;
+ save_curr = curr;
+ curr = rp;
+ rule(rp);
+ curr = save_curr;
+ return;
+ }
+ }
+
+ /*
+ * no predicate is B_TRUE and either some predicate is B_FALSE
+ * or they are all B_UNKNOWN and there is no UNKNOWN action ...
+ * so do the OTHERWISE action, if any
+ */
+ if (other->arg2->op != NOP) {
+ rp = other->arg2;
+ save_curr = curr;
+ curr = rp;
+ rule(rp);
+ save_curr = curr;
+ x->valid = rp->arg1->valid;
+ *(Boolean *)x->ring = *(Boolean *)rp->arg1->ring;
+ }
+}
+
+/*
+ * operator: cndDelay
+ */
+
+void
+cndDelay_n(Expr *x)
+{
+ Expr *arg1 = x->arg1;
+ int n = arg1->tdom;
+ Sample *is = &arg1->smpls[n - 1];
+ Sample *os = &x->smpls[0];
+ @ITYPE *ip;
+ @OTYPE *op;
+ int i;
+
+ EVALARG(arg1)
+ ROTATE(x)
+
+ if (arg1->valid >= n && x->tspan > 0) {
+ ip = (@ITYPE *)is->ptr;
+ op = (@OTYPE *)os->ptr;
+ for (i = 0; i < x->tspan; i++)
+ *op++ = *ip++;
+ os->stamp = is->stamp;
+ x->valid++;
+ }
+ else x->valid = 0;
+}
+
+void
+cndDelay_1(Expr *x)
+{
+ Expr *arg1 = x->arg1;
+ int n = arg1->tdom;
+ Sample *is = &arg1->smpls[n - 1];
+ Sample *os = &x->smpls[0];
+
+ EVALARG(arg1)
+ ROTATE(x)
+
+ if (arg1->valid >= n) {
+ *(@OTYPE *)os->ptr = *(@ITYPE *)is->ptr;
+ os->stamp = is->stamp;
+ x->valid++;
+ }
+ else x->valid = 0;
+}
+
+
diff --git a/src/pmie/src/pmie.c b/src/pmie/src/pmie.c
new file mode 100644
index 0000000..b5150fa
--- /dev/null
+++ b/src/pmie/src/pmie.c
@@ -0,0 +1,942 @@
+/***********************************************************************
+ * pmie.c - performance inference engine
+ ***********************************************************************
+ *
+ * Copyright (c) 2013-2014 Red Hat, Inc.
+ * Copyright (c) 1995-2003 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+/*
+ * pmie debug flags:
+ * APPL0 - lexical scanning
+ * APPL1 - parse/expression tree construction
+ * APPL2 - expression execution
+ */
+
+#include <ctype.h>
+#include <limits.h>
+#include <sys/stat.h>
+#include "pmapi.h"
+#include "impl.h"
+
+#include "dstruct.h"
+#include "stomp.h"
+#include "syntax.h"
+#include "pragmatics.h"
+#include "eval.h"
+#include "show.h"
+
+
+/***********************************************************************
+ * constants
+ ***********************************************************************/
+
+#define LINE_LENGTH 255 /* max length of command token */
+#define PROC_FNAMESIZE 20 /* from proc pmda - proc.h */
+
+static char *prompt = "pmie> ";
+static char *intro = "Performance Co-Pilot Inference Engine (pmie), "
+ "Version %s\n\n%s%s";
+char *clientid;
+
+static FILE *logfp;
+static char logfile[MAXPATHLEN+1];
+static char perffile[MAXPATHLEN+1]; /* /var/tmp/<pid> file name */
+static char *username;
+
+static char menu[] =
+"pmie debugger commands\n\n"
+" f [file-name] - load expressions from given file or stdin\n"
+" l [expr-name] - list named expression or all expressions\n"
+" r [interval] - run for given or default interval\n"
+" S time-spec - set start time for run\n"
+" T time-spec - set default interval for run command\n"
+" v [expr-name] - print subexpression used for %h, %i and\n"
+" %v bindings\n"
+" h or ? - print this menu of commands\n"
+" q - quit\n\n";
+
+/***********************************************************************
+ * command line usage
+ ***********************************************************************/
+
+static int
+override(int opt, pmOptions *opts)
+{
+ if (opt == 'a' || opt == 'h' || opt == 'H' || opt == 'V')
+ return 1;
+ return 0;
+}
+
+static pmLongOptions longopts[] = {
+ PMAPI_OPTIONS_HEADER("General options"),
+ PMOPT_ALIGN,
+ PMOPT_ARCHIVE,
+ PMOPT_DEBUG,
+ PMOPT_HOST,
+ PMOPT_NAMESPACE,
+ PMOPT_ORIGIN,
+ PMOPT_START,
+ PMOPT_FINISH,
+ PMOPT_INTERVAL,
+ PMOPT_TIMEZONE,
+ PMOPT_HOSTZONE,
+ PMOPT_HELP,
+ PMAPI_OPTIONS_HEADER("Runtime options"),
+ { "check", 0, 'C', 0, "parse configuration and exit" },
+ { "config", 1, 'c', "FILE", "configuration file" },
+ { "interact", 0, 'd', 0, "interactive debugging mode" },
+ { "foreground", 0, 'f', 0, "run in the foreground, not as a daemon" },
+ { "", 0, 'H', NULL }, /* was: no DNS lookup on the default hostname */
+ { "", 1, 'j', "FILE", "stomp protocol (JMS) file" },
+ { "logfile", 1, 'l', "FILE", "send status and error messages to FILE" },
+ { "username", 1, 'U', "USER", "run as named USER in daemon mode [default pcp]" },
+ PMAPI_OPTIONS_HEADER("Reporting options"),
+ { "buffer", 0, 'b', 0, "one line buffered output stream, stdout on stderr" },
+ { "timestamp", 0, 'e', 0, "force timestamps to be reported with -V, -v or -W" },
+ { "", 0, 'v', 0, "verbose mode, expression values printed" },
+ { "verbose", 0, 'V', 0, "verbose mode, annotated expression values printed" },
+ { "", 0, 'W', 0, "verbose mode, satisfying expression values printed" },
+ { "secret-applet", 0, 'X', 0, "run in secret applet mode (thin client)" },
+ { "secret-agent", 0, 'x', 0, "run in secret agent mode (summary PMDA)" },
+ PMAPI_OPTIONS_END
+};
+
+static pmOptions opts = {
+ .flags = PM_OPTFLAG_STDOUT_TZ,
+ .short_options = "a:A:bc:CdD:efHh:j:l:n:O:S:t:T:U:vVWXxzZ:?",
+ .long_options = longopts,
+ .short_usage = "[options] [filename ...]",
+ .override = override,
+};
+
+
+/***********************************************************************
+ * interactive commands
+ ***********************************************************************/
+
+/* read command input line */
+static int
+readLine(char *bfr, int max)
+{
+ int c, i;
+
+ /* skip blanks */
+ do
+ c = getchar();
+ while (isspace(c));
+
+ /* scan till end of line */
+ i = 0;
+ while ((c != '\n') && (c != EOF) && (i < max)) {
+ bfr[i++] = c;
+ c = getchar();
+ }
+ bfr[i] = '\0';
+ return (c != EOF);
+}
+
+
+/* scan interactive command token */
+static char *
+scanCmd(char **pp)
+{
+ char *p = *pp;
+
+ /* skip blanks */
+ while (isspace((int)*p))
+ p++;
+
+ /* single char token */
+ if (isgraph((int)*p)) {
+ *pp = p + 1;
+ return p;
+ }
+
+ return NULL;
+}
+
+
+/* scan interactive command argument */
+static char *
+scanArg(char *p)
+{
+ char *q;
+
+ /* strip leading blanks */
+ while (isspace((int)*p))
+ p++;
+ if (*p == '\0')
+ return NULL;
+ q = p;
+
+ /* strip trailing blanks */
+ while (*q != '\0')
+ q++;
+ q--;
+ while (isspace((int)*q))
+ q--;
+ *(q + 1) = '\0';
+
+ /* return result */
+ return p;
+}
+
+
+/* load rules from given file or stdin */
+static void
+load(char *fname)
+{
+ Symbol s;
+ Expr *d;
+ int sts = 0;
+ int sep = __pmPathSeparator();
+ char config[MAXPATHLEN+1];
+
+ /* search for configfile on configuration file path */
+ if (fname && access(fname, F_OK) != 0) {
+ sts = oserror(); /* always report the first error */
+ if (__pmAbsolutePath(fname)) {
+ fprintf(stderr, "%s: cannot access config file %s: %s\n", pmProgname,
+ fname, strerror(sts));
+ exit(1);
+ }
+#if PCP_DEBUG
+ else if (pmDebug & DBG_TRACE_APPL0) {
+ fprintf(stderr, "load: cannot access config file %s: %s\n", fname, strerror(sts));
+ }
+#endif
+ snprintf(config, sizeof(config)-1, "%s%c" "pmie" "%c%s",
+ pmGetConfig("PCP_SYSCONF_DIR"), sep, sep, fname);
+ if (access(config, F_OK) != 0) {
+ fprintf(stderr, "%s: cannot access config file as either %s or %s: %s\n",
+ pmProgname, fname, config, strerror(sts));
+ exit(1);
+ }
+#if PCP_DEBUG
+ else if (pmDebug & DBG_TRACE_APPL0) {
+ fprintf(stderr, "load: using standard config file %s\n", config);
+ }
+#endif
+ fname = config;
+ }
+#if PCP_DEBUG
+ else if (pmDebug & DBG_TRACE_APPL0) {
+ fprintf(stderr, "load: using config file %s\n",
+ fname == NULL? "<stdin>":fname);
+ }
+#endif
+
+ if (perf->config[0] == '\0') { /* keep record of first config */
+ if (fname == NULL)
+ strcpy(perf->config, "<stdin>");
+ else if (realpath(fname, perf->config) == NULL) {
+ fprintf(stderr, "%s: failed to resolve realpath for %s: %s\n",
+ pmProgname, fname, osstrerror());
+ exit(1);
+ }
+ }
+
+ if (synInit(fname)) {
+ while ((s = syntax()) != NULL) {
+ d = (Expr *) symValue(symDelta);
+ pragmatics(s, *(RealTime *)d->smpls[0].ptr);
+ }
+ }
+}
+
+
+/* list given expression or all expressions */
+static void
+list(char *name)
+{
+ Task *t;
+ Symbol *r;
+ Symbol s;
+ int i;
+
+ if (name) { /* single named rule */
+ if ( (s = symLookup(&rules, name)) )
+ showSyntax(stdout, s);
+ else
+ printf("%s: error - rule \"%s\" not defined\n", pmProgname, name);
+ }
+ else { /* all rules */
+ t = taskq;
+ while (t) {
+ r = t->rules;
+ for (i = 0; i < t->nrules; i++) {
+ showSyntax(stdout, *r);
+ r++;
+ }
+ t = t->next;
+ }
+ }
+}
+
+
+/* list binding subexpression of given expression or all expressions */
+static void
+sublist(char *name)
+{
+ Task *t;
+ Symbol *r;
+ Symbol s;
+ int i;
+
+ if (name) { /* single named rule */
+ if ( (s = symLookup(&rules, name)) )
+ showSubsyntax(stdout, s);
+ else
+ printf("%s: error - rule '%s' not defined\n", pmProgname, name);
+ }
+ else { /* all rules */
+ t = taskq;
+ while (t) {
+ r = t->rules;
+ for (i = 0; i < t->nrules; i++) {
+ showSubsyntax(stdout, *r);
+ r++;
+ }
+ t = t->next;
+ }
+ }
+}
+
+
+/***********************************************************************
+ * manipulate the performance instrumentation data structure
+ ***********************************************************************/
+
+static void
+stopmonitor(void)
+{
+ if (*perffile)
+ unlink(perffile);
+}
+
+static void
+startmonitor(void)
+{
+ void *ptr;
+ char *path;
+ int fd;
+ char zero = '\0';
+ char pmie_dir[MAXPATHLEN];
+
+ /* try to create the port file directory. OK if it already exists */
+ snprintf(pmie_dir, sizeof(pmie_dir), "%s%c%s",
+ pmGetConfig("PCP_TMP_DIR"), __pmPathSeparator(), PMIE_SUBDIR);
+ if (mkdir2(pmie_dir, S_IRWXU | S_IRWXG | S_IRWXO) < 0) {
+ if (oserror() != EEXIST) {
+ fprintf(stderr, "%s: warning cannot create stats file dir %s: %s\n",
+ pmProgname, pmie_dir, osstrerror());
+ }
+ }
+ atexit(stopmonitor);
+
+ /* create and initialize memory mapped performance data file */
+ sprintf(perffile, "%s%c%" FMT_PID, pmie_dir, __pmPathSeparator(), getpid());
+ unlink(perffile);
+ if ((fd = open(perffile, O_RDWR | O_CREAT | O_EXCL | O_TRUNC,
+ S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)) < 0) {
+ /* cannot create stats file; too bad, so sad, continue on without it */
+ perf = &instrument;
+ return;
+ }
+ /* seek to struct size and write one zero */
+ lseek(fd, sizeof(pmiestats_t)-1, SEEK_SET);
+ if (write(fd, &zero, 1) != 1) {
+ fprintf(stderr, "%s: Warning: write failed for stats file %s: %s\n",
+ pmProgname, perffile, osstrerror());
+ }
+
+ /* map perffile & associate the instrumentation struct with it */
+ if ((ptr = __pmMemoryMap(fd, sizeof(pmiestats_t), 1)) == NULL) {
+ fprintf(stderr, "%s: memory map failed for stats file %s: %s\n",
+ pmProgname, perffile, osstrerror());
+ perf = &instrument;
+ } else {
+ perf = (pmiestats_t *)ptr;
+ }
+ close(fd);
+
+ path = (logfile[0] == '\0') ? "<none>" : logfile;
+ strncpy(perf->logfile, path, sizeof(perf->logfile));
+ perf->logfile[sizeof(perf->logfile)-1] = '\0';
+ strncpy(perf->defaultfqdn, dfltHostName, sizeof(perf->defaultfqdn));
+ perf->defaultfqdn[sizeof(perf->defaultfqdn)-1] = '\0';
+ perf->version = 1;
+}
+
+
+/***********************************************************************
+ * signal handling
+ ***********************************************************************/
+
+static void
+sigintproc(int sig)
+{
+ __pmSetSignalHandler(SIGINT, SIG_IGN);
+ __pmSetSignalHandler(SIGTERM, SIG_IGN);
+ if (pmDebug & DBG_TRACE_DESPERATE)
+ __pmNotifyErr(LOG_INFO, "%s caught SIGINT or SIGTERM\n", pmProgname);
+ doexit = sig;
+}
+
+static void
+remap_stdout_stderr(void)
+{
+ int i, j;
+
+ fflush(stderr);
+ fflush(stdout);
+ setlinebuf(stderr);
+ setlinebuf(stdout);
+ i = fileno(stdout);
+ close(i);
+ if ((j = dup(fileno(stderr))) != i)
+ fprintf(stderr, "%s: Warning: failed to link stdout ... "
+ "dup() returns %d, expected %d (stderr=%d)\n",
+ pmProgname, j, i, fileno(stderr));
+}
+
+void
+logRotate(void)
+{
+ FILE *fp;
+ int sts;
+
+ fp = __pmRotateLog(pmProgname, logfile, logfp, &sts);
+ if (sts != 0) {
+ fprintf(stderr, "pmie: PID = %" FMT_PID ", default host = %s via %s\n\n",
+ getpid(), dfltHostName, dfltHostConn);
+ remap_stdout_stderr();
+ logfp = fp;
+ } else {
+ __pmNotifyErr(LOG_ERR, "pmie: log rotation failed\n");
+ }
+}
+
+static void
+sighupproc(int sig)
+{
+ __pmSetSignalHandler(SIGHUP, sighupproc);
+ dorotate = 1;
+}
+
+static void
+sigbadproc(int sig)
+{
+ if (pmDebug & DBG_TRACE_DESPERATE) {
+ __pmNotifyErr(LOG_ERR, "Unexpected signal %d ...\n", sig);
+ fprintf(stderr, "\nProcedure call traceback ...\n");
+ __pmDumpStack(stderr);
+ fflush(stderr);
+ }
+ stopmonitor();
+ _exit(sig);
+}
+
+
+/***********************************************************************
+ * command line processing - extract command line arguments & initialize
+ ***********************************************************************/
+
+static void
+getargs(int argc, char *argv[])
+{
+ char *configfile = NULL;
+ char *commandlog = NULL;
+ char *subopts;
+ char *subopt;
+ char *msg;
+ int checkFlag = 0;
+ int foreground = 0;
+ int sts;
+ int c;
+ int bflag = 0;
+ int dfltConn = 0; /* default context type */
+ Archive *a;
+ struct timeval tv, tv1, tv2;
+
+ extern int showTimeFlag;
+ extern int errs; /* syntax errors from syntax.c */
+
+ memset(&tv, 0, sizeof(tv));
+ memset(&tv1, 0, sizeof(tv1));
+ memset(&tv2, 0, sizeof(tv2));
+ dstructInit();
+
+ while ((c = pmGetOptions(argc, argv, &opts)) != EOF) {
+ switch (c) {
+
+ case 'a': /* archives */
+ if (dfltConn && dfltConn != PM_CONTEXT_ARCHIVE) {
+ /* (technically, multiple -a's are allowed.) */
+ pmprintf("%s: at most one of -a or -h allowed\n", pmProgname);
+ opts.errors++;
+ break;
+ }
+ dfltConn = opts.context = PM_CONTEXT_ARCHIVE;
+ subopts = opts.optarg;
+ for ( ; ; ) {
+ subopt = subopts;
+ subopts = strchr(subopts, ',');
+ if (subopts != NULL) {
+ *subopts++ = '\0';
+ }
+ a = (Archive *)zalloc(sizeof(Archive));
+ a->fname = subopt;
+ if (!initArchive(a)) {
+ exit(1);
+ }
+ if (subopts == NULL) break;
+ }
+ foreground = 1;
+ break;
+
+ case 'b': /* line buffered, stdout on stderr */
+ bflag++;
+ break;
+
+ case 'c': /* configuration file */
+ if (interactive) {
+ pmprintf("%s: at most one of -c and -d allowed\n", pmProgname);
+ opts.errors++;
+ break;
+ }
+ configfile = opts.optarg;
+ break;
+
+ case 'C': /* check config and exit */
+ checkFlag = 1;
+ break;
+
+ case 'd': /* interactive mode */
+ if (configfile) {
+ pmprintf("%s: at most one of -c and -d allowed\n", pmProgname);
+ opts.errors++;
+ break;
+ }
+ interactive = 1;
+ break;
+
+ case 'e': /* force timestamps */
+ showTimeFlag = 1;
+ break;
+
+ case 'f': /* in foreground, not as daemon */
+ foreground = 1;
+ break;
+
+ case 'H': /* deprecated: no DNS lookups */
+ break;
+
+ case 'h': /* default host name */
+ if (dfltConn) {
+ pmprintf("%s: at most one of -a or -h allowed\n", pmProgname);
+ opts.errors++;
+ break;
+ }
+ dfltConn = opts.context = PM_CONTEXT_HOST;
+ dfltHostConn = opts.optarg;
+ dfltHostName = ""; /* unknown until newContext */
+ break;
+
+ case 'j': /* stomp protocol (JMS) config */
+ stompfile = opts.optarg;
+ break;
+
+ case 'l': /* alternate log file */
+ if (commandlog != NULL) {
+ pmprintf("%s: at most one -l option is allowed\n", pmProgname);
+ opts.errors++;
+ break;
+ }
+ commandlog = opts.optarg;
+ isdaemon = 1;
+ break;
+
+ case 'U': /* run as named user */
+ username = opts.optarg;
+ isdaemon = 1;
+ break;
+
+ case 'v': /* print values */
+ verbose = 1;
+ break;
+
+ case 'V': /* print annotated values */
+ verbose = 2;
+ break;
+
+ case 'W': /* print satisfying values */
+ verbose = 3;
+ break;
+
+ case 'X': /* secret applet flag */
+ applet = 1;
+ verbose = 1;
+ setlinebuf(stdout);
+ break;
+
+ case 'x': /* summary PMDA flag */
+ agent = 1;
+ verbose = 1;
+ isdaemon = 1;
+ break;
+ }
+ }
+
+ if (!opts.errors && configfile && opts.optind != argc) {
+ pmprintf("%s: extra filenames cannot be given after using -c\n",
+ pmProgname);
+ opts.errors++;
+ }
+ if (!opts.errors && bflag && agent) {
+ pmprintf("%s: the -b and -x options are incompatible\n",
+ pmProgname);
+ opts.errors++;
+ }
+ if (opts.errors) {
+ pmUsageMessage(&opts);
+ exit(1);
+ }
+
+ if (foreground)
+ isdaemon = 0;
+
+ hostZone = opts.tzflag;
+ timeZone = opts.timezone;
+ if (opts.interval.tv_sec || opts.interval.tv_usec)
+ dfltDelta = realize(opts.interval);
+
+ if (archives || interactive)
+ perf = &instrument;
+
+ if (isdaemon) { /* daemon mode */
+ /* done before opening log to get permissions right */
+ __pmSetProcessIdentity(username);
+
+#if defined(HAVE_TERMIO_SIGNALS)
+ signal(SIGTTOU, SIG_IGN);
+ signal(SIGTTIN, SIG_IGN);
+ signal(SIGTSTP, SIG_IGN);
+#endif
+ __pmSetSignalHandler(SIGINT, sigintproc);
+ __pmSetSignalHandler(SIGTERM, sigintproc);
+ __pmSetSignalHandler(SIGBUS, sigbadproc);
+ __pmSetSignalHandler(SIGSEGV, sigbadproc);
+ }
+ else {
+ /* need to catch these so the atexit() processing is done */
+ __pmSetSignalHandler(SIGINT, sigintproc);
+ __pmSetSignalHandler(SIGTERM, sigintproc);
+ }
+
+ if (commandlog != NULL) {
+ logfp = __pmOpenLog(pmProgname, commandlog, stderr, &sts);
+ if (realpath(commandlog, logfile) == NULL) {
+ fprintf(stderr, "%s: cannot find realpath for log %s: %s\n",
+ pmProgname, commandlog, osstrerror());
+ exit(1);
+ }
+ __pmSetSignalHandler(SIGHUP, (isdaemon && !agent) ? sighupproc : SIG_IGN);
+ } else {
+ __pmSetSignalHandler(SIGHUP, SIG_IGN);
+ }
+
+ /*
+ * -b ... force line buffering and stdout onto stderr
+ */
+ if ((bflag || isdaemon) && !agent)
+ remap_stdout_stderr();
+
+ /* default host from leftmost archive on command line, or from
+ discovery after a brief connection */
+ if (archives) {
+ a = archives;
+ while (a->next)
+ a = a->next;
+ dfltHostName = a->hname; /* already filled in during initArchive() */
+ } else if (!dfltConn || dfltConn == PM_CONTEXT_HOST) {
+ if (dfltConn == 0) /* default case, no -a or -h */
+ dfltHostConn = "local:";
+ sts = pmNewContext(PM_CONTEXT_HOST, dfltHostConn);
+ /* pmcd down locally, try to extract hostname manually */
+ if (sts < 0 && (!dfltConn ||
+ !strcmp(dfltHostConn, "localhost") ||
+ !strcmp(dfltHostConn, "local:") ||
+ !strcmp(dfltHostConn, "unix:")))
+ sts = pmNewContext(PM_CONTEXT_LOCAL, NULL);
+ if (sts < 0) {
+ fprintf(stderr, "%s: cannot find host name for %s\n"
+ "pmNewContext failed: %s\n",
+ pmProgname, dfltHostConn, pmErrStr(sts));
+ exit(1);
+ } else {
+ const char *tmp = pmGetContextHostName(sts);
+
+ if (strlen(tmp) == 0) {
+ fprintf(stderr, "%s: pmGetContextHostName(%d) failed\n",
+ pmProgname, sts);
+ exit(1);
+ }
+ if ((dfltHostName = strdup(tmp)) == NULL)
+ __pmNoMem("host name copy", strlen(tmp)+1, PM_FATAL_ERR);
+ pmDestroyContext(sts);
+ }
+ }
+
+ if (!archives && !interactive) {
+ if (commandlog != NULL)
+ fprintf(stderr, "pmie: PID = %" FMT_PID ", default host = %s via %s\n\n",
+ getpid(), dfltHostName, dfltHostConn);
+ startmonitor();
+ }
+
+ /* initialize time */
+ now = archives ? first : getReal() + 1.0;
+ zoneInit();
+ reflectTime(dfltDelta);
+
+ /* parse time window - just to check argument syntax */
+ unrealize(now, &tv1);
+ if (archives) {
+ unrealize(last, &tv2);
+ } else {
+ tv2.tv_sec = INT_MAX; /* sizeof(time_t) == sizeof(int) */
+ tv2.tv_usec = 0;
+ }
+ if (pmParseTimeWindow(opts.start_optarg, opts.finish_optarg,
+ opts.align_optarg, opts.origin_optarg,
+ &tv1, &tv2,
+ &tv, &tv2, &tv1,
+ &msg) < 0) {
+ fputs(msg, stderr);
+ exit(1);
+ }
+ start = realize(tv1);
+ stop = realize(tv2);
+ runTime = stop - start;
+
+ /* when not in secret agent mode, register client id with pmcd */
+ if (!agent)
+ clientid = __pmGetClientId(argc, argv);
+
+ if (!interactive && opts.optind == argc) { /* stdin or config file */
+ load(configfile);
+ }
+ else { /* list of 1/more filenames */
+ while (opts.optind < argc) {
+ load(argv[opts.optind]);
+ opts.optind++;
+ }
+ }
+
+#if PCP_DEBUG
+ if (pmDebug & DBG_TRACE_APPL1)
+ dumpRules();
+#endif
+
+ if (checkFlag)
+ exit(errs == 0 ? 0 : 1); /* exit 1 for syntax errors ...
+ * suggestion from
+ * Kevin Wang <kjw@rightsock.com>
+ */
+
+ if (isdaemon) { /* daemon mode */
+ /* Note: we can no longer unilaterally close stdin here, as it
+ * can really confuse remap_stdout_stderr() during log rotation!
+ */
+ if (agent)
+ close(fileno(stdin));
+#ifndef IS_MINGW
+ setsid(); /* not process group leader, lose controlling tty */
+#endif
+ }
+
+ if (stomping)
+ stompInit(); /* connect to our message server */
+
+ if (agent)
+ agentInit(); /* initialize secret agent stuff */
+
+ /* really parse time window */
+ if (!archives) {
+ now = getReal() + 1.0;
+ reflectTime(dfltDelta);
+ }
+ unrealize(now, &tv1);
+ if (archives) {
+ unrealize(last, &tv2);
+ } else {
+ tv2.tv_sec = INT_MAX;
+ tv2.tv_usec = 0;
+ }
+ if (pmParseTimeWindow(opts.start_optarg, opts.finish_optarg,
+ opts.align_optarg, opts.origin_optarg,
+ &tv1, &tv2,
+ &tv, &tv2, &tv1,
+ &msg) < 0) {
+ fputs(msg, stderr);
+ exit(1);
+ }
+
+ /* set run timing window */
+ start = realize(tv1);
+ stop = realize(tv2);
+ runTime = stop - start;
+}
+
+/***********************************************************************
+ * interactive (debugging) mode
+ ***********************************************************************/
+
+static void
+interact(void)
+{
+ int quit = 0;
+ char *line = (char *)zalloc(LINE_LENGTH + 2);
+ char *finger;
+ char *token;
+ char *msg;
+ RealTime rt;
+ struct timeval tv1, tv2;
+
+ printf(intro, PCP_VERSION, menu, prompt);
+ fflush(stdout);
+ while (!quit && readLine(line, LINE_LENGTH)) {
+ finger = line;
+
+ if ( (token = scanCmd(&finger)) ) {
+ switch (*token) {
+
+ case 'f':
+ token = scanArg(finger);
+ load(token);
+#if PCP_DEBUG
+ if (pmDebug & DBG_TRACE_APPL1)
+ dumpRules();
+#endif
+ break;
+
+ case 'l':
+ token = scanArg(finger);
+ list(token);
+ break;
+
+ case 'r':
+ token = scanArg(finger);
+ if (token) {
+ if (pmParseInterval(token, &tv1, &msg) == 0)
+ runTime = realize(tv1);
+ else {
+ fputs(msg, stderr);
+ free(msg);
+ break;
+ }
+ }
+ if (!archives) {
+ invalidate();
+ rt = getReal();
+ if (now < rt)
+ now = rt;
+ start = now;
+ }
+ stop = start + runTime;
+ run();
+ break;
+
+ case 'S':
+ token = scanArg(finger);
+ if (token == NULL) {
+ fprintf(stderr, "%s: error - argument required\n", pmProgname);
+ break;
+ }
+ unrealize(start, &tv1);
+ if (archives) {
+ unrealize(last, &tv2);
+ } else {
+ tv2.tv_sec = INT_MAX;
+ tv2.tv_usec = 0;
+ }
+ if (__pmParseTime(token, &tv1, &tv2, &tv1, &msg) < 0) {
+ fputs(msg, stderr);
+ free(msg);
+ break;
+ }
+ start = realize(tv1);
+ if (archives)
+ invalidate();
+ break;
+
+ case 'T':
+ token = scanArg(finger);
+ if (token == NULL) {
+ fprintf(stderr, "%s: error - argument required\n", pmProgname);
+ break;
+ }
+ if (pmParseInterval(token, &tv1, &msg) < 0) {
+ fputs(msg, stderr);
+ free(msg);
+ break;
+ }
+ runTime = realize(tv1);
+ break;
+ case 'q':
+ quit = 1;
+ break;
+
+ case 'v':
+ token = scanArg(finger);
+ sublist(token);
+ break;
+
+ case '?':
+ default:
+ printf("%s", menu);
+ }
+ }
+ if (!quit) {
+ printf("%s", prompt);
+ fflush(stdout);
+ }
+ }
+ free(line);
+}
+
+
+/***********************************************************************
+ * main
+ ***********************************************************************/
+
+int
+main(int argc, char **argv)
+{
+ __pmGetUsername(&username);
+ setlinebuf(stdout);
+
+ /* PCP_COUNTER_WRAP in environment enables "counter wrap" logic */
+ if (getenv("PCP_COUNTER_WRAP") != NULL)
+ dowrap = 1;
+
+ getargs(argc, argv);
+
+ if (interactive)
+ interact();
+ else
+ run();
+ exit(0);
+}
diff --git a/src/pmie/src/pragmatics.c b/src/pmie/src/pragmatics.c
new file mode 100644
index 0000000..23e3d1e
--- /dev/null
+++ b/src/pmie/src/pragmatics.c
@@ -0,0 +1,1226 @@
+/*
+ * pragmatics.c - inference engine pragmatics analysis
+ *
+ * Copyright (c) 1995-2003 Silicon Graphics, Inc. All Rights Reserved.
+ * Copyright (c) 2013-2014 Red Hat, Inc.
+ *
+ * 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
+ *
+ * The analysis of how to organize the fetching of metrics (pragmatics),
+ * and any other parts of the inference engine sensitive to details of
+ * the PMAPI access are kept in this source file.
+ */
+
+#include <math.h>
+#include <ctype.h>
+#include "pmapi.h"
+#include "impl.h"
+#include "dstruct.h"
+#include "eval.h"
+#include "pragmatics.h"
+#if defined(HAVE_IEEEFP_H)
+#include <ieeefp.h>
+#endif
+
+extern char *clientid;
+
+/* for initialization of pmUnits struct */
+pmUnits noUnits;
+pmUnits countUnits = { .dimCount = 1 };
+
+char *
+findsource(char *host)
+{
+ static char buf[MAXPATHLEN+MAXHOSTNAMELEN+30];
+
+ if (archives) {
+ Archive *a = archives;
+ while (a) { /* find archive for host */
+ if (strcmp(host, a->hname) == 0)
+ break;
+ a = a->next;
+ }
+ if (a)
+ snprintf(buf, sizeof(buf), "archive %s (host %s)", a->fname, host);
+ else
+ snprintf(buf, sizeof(buf), "host %s in unknown archive!", host);
+ }
+ else
+ snprintf(buf, sizeof(buf), "host %s", host);
+
+ return buf;
+}
+
+/***********************************************************************
+ * PMAPI context creation & destruction
+ ***********************************************************************/
+
+int /* > 0: context handle, -1: retry later */
+newContext(char *host)
+{
+ Archive *a;
+ int sts = -1;
+
+ if (archives) {
+ a = archives;
+ while (a) { /* find archive for host */
+ if (strcmp(host, a->hname) == 0)
+ break;
+ a = a->next;
+ }
+ if (a) { /* archive found */
+ if ((sts = pmNewContext(PM_CONTEXT_ARCHIVE, a->fname)) < 0) {
+ fprintf(stderr, "%s: cannot open archive %s\n",
+ pmProgname, a->fname);
+ fprintf(stderr, "pmNewContext: %s\n", pmErrStr(sts));
+ exit(1);
+ }
+ }
+ else { /* no archive for host */
+ fprintf(stderr, "%s: no archive for host %s\n", pmProgname, host);
+ exit(1);
+ }
+ }
+ else if ((sts = pmNewContext(PM_CONTEXT_HOST, host)) < 0) {
+ if (host_state_changed(host, STATE_FAILINIT) == 1) {
+ if (sts == -ECONNREFUSED)
+ fprintf(stderr, "%s: warning - pmcd "
+ "on host %s does not respond\n",
+ pmProgname, host);
+ else if (sts == PM_ERR_PERMISSION)
+ fprintf(stderr, "%s: warning - host %s does not "
+ "permit delivery of metrics to the local host\n",
+ pmProgname, host);
+ else if (sts == PM_ERR_CONNLIMIT)
+ fprintf(stderr, "%s: warning - pmcd "
+ "on host %s has exceeded its connection limit\n",
+ pmProgname, host);
+ else
+ fprintf(stderr, "%s: warning - host %s is unreachable\n",
+ pmProgname, host);
+ }
+ sts = -1;
+ }
+ else if (clientid != NULL)
+ /* register client id with pmcd */
+ __pmSetClientId(clientid);
+ return sts;
+}
+
+
+/***********************************************************************
+ * instance profile
+ ***********************************************************************/
+
+/* equality (not symmetric) of instance names */
+static int
+eqinst(char *i1, char *i2)
+{
+ int n1 = (int) strlen(i1);
+ int n2 = (int) strlen(i2);
+
+ /* test equality of first word */
+ if ((strncmp(i1, i2, n1) == 0) && ((n1 == n2) || isspace((int)i2[n1])))
+ return 1;
+
+ do { /* skip over first word */
+ i2++;
+ n2--;
+ if (n2 < n1)
+ return 0;
+ } while (! isspace((int)*i2));
+
+ do { /* skip over spaces */
+ i2++;
+ n2--;
+ if (n2 < n1)
+ return 0;
+ } while (isspace((int)*i2));
+
+ /* test equality of second word */
+ if ((strncmp(i1, i2, n1) == 0) && ((n1 == n2) || isspace((int)i2[n1])))
+ return 1;
+ return 0;
+}
+
+
+/***********************************************************************
+ * task queue
+ ***********************************************************************/
+
+/* find Task for new rule */
+static Task *
+findTask(RealTime delta)
+{
+ Task *t, *u;
+ int n = 0;
+
+ t = taskq;
+ if (t) {
+ while (t->next) { /* find last task in queue */
+ t = t->next;
+ n++;
+ }
+
+ /* last task in queue has same delta */
+ if (t->delta == delta)
+ return t;
+ }
+
+ u = newTask(delta, n); /* create new Task */
+ if (t) {
+ t->next = u;
+ u->prev = t;
+ }
+ else
+ taskq = u;
+ return u;
+}
+
+
+/***********************************************************************
+ * wait list
+ ***********************************************************************/
+
+/* put Metric onto wait list */
+void
+waitMetric(Metric *m)
+{
+ Host *h = m->host;
+
+ m->next = h->waits;
+ m->prev = NULL;
+ if (h->waits) h->waits->prev = m;
+ h->waits = m;
+}
+
+/* remove Metric from wait list */
+void
+unwaitMetric(Metric *m)
+{
+ if (m->prev) m->prev->next = m->next;
+ else m->host->waits = m->next;
+ if (m->next) m->next->prev = m->prev;
+}
+
+
+/***********************************************************************
+ * fetch list
+ ***********************************************************************/
+
+/* find Host for Metric */
+static Host *
+findHost(Task *t, Metric *m)
+{
+ Host *h;
+
+ h = t->hosts;
+ while (h) { /* look for existing host */
+ if (h->name == m->hname)
+ return h;
+ h = h->next;
+ }
+
+ h = newHost(t, m->hname); /* add new host */
+ if (t->hosts) {
+ h->next = t->hosts;
+ t->hosts->prev = h;
+ }
+ t->hosts = h;
+ return h;
+}
+
+/* helper function for Extended Time Base */
+static void
+getDoubleAsXTB(double *realtime, int *ival, int *mode)
+{
+#define SECS_IN_24_DAYS 2073600.0
+
+ if (*realtime > SECS_IN_24_DAYS) {
+ *ival = (int)*realtime;
+ *mode = (*mode & 0x0000ffff) | PM_XTB_SET(PM_TIME_SEC);
+ }
+ else {
+ *ival = (int)(*realtime * 1000.0);
+ *mode = (*mode & 0x0000ffff) | PM_XTB_SET(PM_TIME_MSEC);
+ }
+}
+
+
+/* find Fetch bundle for Metric */
+static Fetch *
+findFetch(Host *h, Metric *m)
+{
+ Fetch *f;
+ int sts;
+ int i;
+ int n;
+ pmID pmid = m->desc.pmid;
+ pmID *p;
+ struct timeval tv;
+
+ /* find existing Fetch bundle */
+ f = h->fetches;
+
+ /* create new Fetch bundle */
+ if (! f) {
+ f = newFetch(h);
+ if ((f->handle = newContext(symName(h->name))) < 0) {
+ free(f);
+ h->down = 1;
+ return NULL;
+ }
+ if (archives) {
+ int tmp_ival;
+ int tmp_mode = PM_MODE_INTERP;
+ getDoubleAsXTB(&h->task->delta, &tmp_ival, &tmp_mode);
+
+ tv.tv_sec = (time_t)start;
+ tv.tv_usec = (int)((start - tv.tv_sec) * 1000000.0);
+ if ((sts = pmSetMode(tmp_mode, &tv, tmp_ival)) < 0) {
+ fprintf(stderr, "%s: pmSetMode failed: %s\n", pmProgname,
+ pmErrStr(sts));
+ exit(1);
+ }
+#if PCP_DEBUG
+ if (pmDebug & DBG_TRACE_APPL1) {
+ fprintf(stderr, "findFetch: fetch=0x%p host=0x%p delta=%.6f handle=%d\n", f, h, h->task->delta, f->handle);
+ }
+#endif
+ }
+ f->next = NULL;
+ f->prev = NULL;
+ h->fetches = f;
+ }
+
+ /* look for existing pmid */
+ p = f->pmids;
+ n = f->npmids;
+ for (i = 0; i < n; i++) {
+ if (*p == pmid) break;
+ p++;
+ }
+
+ /* add new pmid */
+ if (i == n) {
+ p = f->pmids;
+ p = ralloc(p, (n+1) * sizeof(pmID));
+ p[n] = pmid;
+ f->npmids = n + 1;
+ f->pmids = p;
+ }
+
+ return f;
+}
+
+
+/* find Profile for Metric */
+static Profile *
+findProfile(Fetch *f, Metric *m)
+{
+ Profile *p;
+ int sts;
+
+ /* find existing Profile */
+ p = f->profiles;
+ while (p) {
+ if (p->indom == m->desc.indom) {
+ m->next = p->metrics;
+ if (p->metrics) p->metrics->prev = m;
+ p->metrics = m;
+ break;
+ }
+ p = p->next;
+ }
+
+ /* create new Profile */
+ if (p == NULL) {
+ m->next = NULL;
+ p = newProfile(f, m->desc.indom);
+ p->next = f->profiles;
+ if (f->profiles) f->profiles->prev = p;
+ f->profiles = p;
+ p->metrics = m;
+ }
+
+ /* add instances required by Metric to Profile */
+ if ((sts = pmUseContext(f->handle)) < 0) {
+ fprintf(stderr, "%s: pmUseContext failed: %s\n", pmProgname,
+ pmErrStr(sts));
+ exit(1);
+ }
+
+ /*
+ * If any rule requires all instances, then ignore restricted
+ * instance lists from all other rules
+ */
+ if (m->specinst == 0 && p->need_all == 0) {
+ sts = pmDelProfile(p->indom, 0, (int *)0);
+ if (sts < 0) {
+ fprintf(stderr, "%s: pmDelProfile failed: %s\n", pmProgname,
+ pmErrStr(sts));
+ exit(1);
+ }
+ sts = pmAddProfile(p->indom, 0, (int *)0);
+ p->need_all = 1;
+ }
+ else if (m->specinst > 0 && p->need_all == 0)
+ sts = pmAddProfile(p->indom, m->m_idom, m->iids);
+ else
+ sts = 0;
+
+ if (sts < 0) {
+ fprintf(stderr, "%s: pmAddProfile failed: %s\n", pmProgname,
+ pmErrStr(sts));
+ exit(1);
+ }
+
+ m->profile = p;
+ return p;
+}
+
+
+/* organize fetch bundling for given expression */
+static void
+bundle(Task *t, Expr *x)
+{
+ Metric *m;
+ Host *h;
+ int i;
+
+ if (x->op == CND_FETCH) {
+ m = x->metrics;
+ for (i = 0; i < x->hdom; i++) {
+ h = findHost(t, m);
+ m->host = h;
+ if (m->conv) /* initialized Metric */
+ bundleMetric(h, m);
+ else /* uninitialized Metric */
+ waitMetric(m);
+ m++;
+ }
+#if PCP_DEBUG
+ if (pmDebug & DBG_TRACE_APPL1) {
+ fprintf(stderr, "bundle: task " PRINTF_P_PFX "%p nth=%d prev=" PRINTF_P_PFX "%p next=" PRINTF_P_PFX "%p delta=%.3f nrules=%d\n",
+ t, t->nth, t->prev, t->next, t->delta, t->nrules+1);
+ __dumpExpr(1, x);
+ m = x->metrics;
+ for (i = 0; i < x->hdom; i++) {
+ __dumpMetric(2, m);
+ m++;
+ }
+ }
+#endif
+ }
+ else {
+ if (x->arg1) {
+ bundle(t, x->arg1);
+ if (x->arg2)
+ bundle(t, x->arg2);
+ }
+ }
+}
+
+
+/***********************************************************************
+ * secret agent mode support
+ ***********************************************************************/
+
+/* send pmDescriptor for the given Expr as a binary PDU */
+static void
+sendDesc(Expr *x, pmValueSet *vset)
+{
+ pmDesc d;
+
+ d.pmid = vset->pmid;
+ d.indom = PM_INDOM_NULL;
+ switch (x->sem) {
+ case PM_SEM_COUNTER:
+ case PM_SEM_INSTANT:
+ case PM_SEM_DISCRETE:
+ /* these map directly to PMAPI semantics */
+ d.type = PM_TYPE_DOUBLE;
+ d.sem = x->sem;
+ d.units = x->units;
+ break;
+
+ case SEM_NUMVAR:
+ case SEM_NUMCONST:
+ case SEM_BOOLEAN:
+ /* map to a numeric value */
+ d.type = PM_TYPE_DOUBLE;
+ d.sem = PM_SEM_INSTANT;
+ d.units = x->units;
+ break;
+
+ default:
+ fprintf(stderr, "sendDesc(%s): botch sem=%d?\n", pmIDStr(d.pmid), x->sem);
+ /* FALLTHROUGH */
+ case SEM_UNKNOWN:
+ case SEM_CHAR:
+ case SEM_REGEX:
+ /* no mapping is possible */
+ d.type = PM_TYPE_NOSUPPORT;
+ d.sem = PM_SEM_INSTANT;
+ d.units = noUnits;
+ break;
+ }
+ __pmSendDesc(STDOUT_FILENO, pmWhichContext(), &d);
+}
+
+
+/***********************************************************************
+ * exported functions
+ ***********************************************************************/
+
+/* initialize access to archive */
+int
+initArchive(Archive *a)
+{
+ pmLogLabel label;
+ struct timeval tv;
+ int sts;
+ int handle;
+ Archive *b;
+ const char *tmp;
+
+ /* setup temorary context for the archive */
+ if ((sts = pmNewContext(PM_CONTEXT_ARCHIVE, a->fname)) < 0) {
+ fprintf(stderr, "%s: cannot open archive %s\n"
+ "pmNewContext failed: %s\n",
+ pmProgname, a->fname, pmErrStr(sts));
+ return 0;
+ }
+ handle = sts;
+
+ tmp = pmGetContextHostName(handle);
+ if (strlen(tmp) == 0) {
+ fprintf(stderr, "%s: pmGetContextHostName(%d) failed\n",
+ pmProgname, handle);
+ return 0;
+ }
+ if ((a->hname = strdup(tmp)) == NULL)
+ __pmNoMem("host name copy", strlen(tmp)+1, PM_FATAL_ERR);
+
+ /* get the goodies from archive label */
+ if ((sts = pmGetArchiveLabel(&label)) < 0) {
+ fprintf(stderr, "%s: cannot read label from archive %s\n"
+ "pmGetArchiveLabel failed: %s\n",
+ pmProgname, a->fname, pmErrStr(sts));
+ pmDestroyContext(handle);
+ return 0;
+ }
+ a->first = realize(label.ll_start);
+ if ((sts = pmGetArchiveEnd(&tv)) < 0) {
+ fprintf(stderr, "%s: archive %s is corrupted\n"
+ "pmGetArchiveEnd failed: %s\n",
+ pmProgname, a->fname, pmErrStr(sts));
+ pmDestroyContext(handle);
+ return 0;
+ }
+ a->last = realize(tv);
+
+ /* check for duplicate host */
+ b = archives;
+ while (b) {
+ if (strcmp(a->hname, b->hname) == 0) {
+ fprintf(stderr, "%s: Error: archive %s not legal - archive %s is already open "
+ "for host %s\n", pmProgname, a->fname, b->fname, b->hname);
+ pmDestroyContext(handle);
+ return 0;
+ }
+ b = b->next;
+ }
+
+ /* put archive record on the archives list */
+ a->next = archives;
+ archives = a;
+
+ /* update first and last available data points */
+ if (first == -1 || a->first < first)
+ first = a->first;
+ if (a->last > last)
+ last = a->last;
+
+ pmDestroyContext(handle);
+ return 1;
+}
+
+
+/* initialize timezone */
+void
+zoneInit(void)
+{
+ int sts;
+ int handle = -1;
+ Archive *a;
+
+ if (timeZone) { /* TZ from timezone string */
+ if ((sts = pmNewZone(timeZone)) < 0)
+ fprintf(stderr, "%s: cannot set timezone to %s\n"
+ "pmNewZone failed: %s\n", pmProgname, timeZone,
+ pmErrStr(sts));
+ }
+ else if (! archives && hostZone) { /* TZ from live host */
+ if ((handle = pmNewContext(PM_CONTEXT_HOST, dfltHostConn)) < 0)
+ fprintf(stderr, "%s: cannot set timezone from %s\n"
+ "pmNewContext failed: %s\n", pmProgname,
+ findsource(dfltHostConn), pmErrStr(handle));
+ else if ((sts = pmNewContextZone()) < 0)
+ fprintf(stderr, "%s: cannot set timezone from %s\n"
+ "pmNewContextZone failed: %s\n", pmProgname,
+ findsource(dfltHostConn), pmErrStr(sts));
+ else
+ fprintf(stdout, "%s: timezone set to local timezone of host %s\n",
+ pmProgname, dfltHostConn);
+ if (handle >= 0)
+ pmDestroyContext(handle);
+ }
+ else if (hostZone) { /* TZ from an archive */
+ a = archives;
+ while (a) {
+ if (strcmp(dfltHostName, a->hname) == 0)
+ break;
+ a = a->next;
+ }
+ if (! a)
+ fprintf(stderr, "%s: no archive supplied for host %s\n",
+ pmProgname, dfltHostName);
+ else if ((handle = pmNewContext(PM_CONTEXT_ARCHIVE, a->fname)) < 0)
+ fprintf(stderr, "%s: cannot set timezone from %s\npmNewContext failed: %s\n",
+ pmProgname, findsource(dfltHostName), pmErrStr(handle));
+ else if ((sts = pmNewContextZone()) < 0)
+ fprintf(stderr, "%s: cannot set timezone from %s\n"
+ "pmNewContextZone failed: %s\n",
+ pmProgname, findsource(dfltHostName), pmErrStr(sts));
+ else
+ fprintf(stdout, "%s: timezone set to local timezone of host %s\n",
+ pmProgname, dfltHostName);
+ if (handle >= 0)
+ pmDestroyContext(handle);
+ }
+}
+
+
+/* convert to canonical units */
+pmUnits
+canon(pmUnits in)
+{
+ static pmUnits out;
+
+ out = in;
+ out.scaleSpace = PM_SPACE_BYTE;
+ out.scaleTime = PM_TIME_SEC;
+ out.scaleCount = 0;
+ return out;
+}
+
+/* scale factor to canonical pmUnits */
+double
+scale(pmUnits in)
+{
+ double f;
+
+ /* scale space to Mbyte */
+ f = pow(1024, in.dimSpace * (in.scaleSpace - PM_SPACE_BYTE));
+
+ /* scale time to seconds */
+ if (in.scaleTime > PM_TIME_SEC)
+ f *= pow(60, in.dimTime * (in.scaleTime - PM_TIME_SEC));
+ else
+ f *= pow(1000, in.dimTime * (in.scaleTime - PM_TIME_SEC));
+
+ /* scale events to millions of events */
+ f *= pow(10, in.dimCount * in.scaleCount);
+
+ return f;
+}
+
+
+/* initialize Metric */
+int /* 1: ok, 0: try again later, -1: fail */
+initMetric(Metric *m)
+{
+ char *hname = symName(m->hname);
+ char *mname = symName(m->mname);
+ char **inames;
+ int *iids;
+ int handle;
+ int ret = 1;
+ int sts;
+ int i, j;
+
+ /* set up temporary context */
+ if ((handle = newContext(hname)) < 0)
+ return 0;
+
+ host_state_changed(hname, STATE_RECONN);
+
+ if ((sts = pmLookupName(1, &mname, &m->desc.pmid)) < 0) {
+ fprintf(stderr, "%s: metric %s not in namespace for %s\n"
+ "pmLookupName failed: %s\n",
+ pmProgname, mname, findsource(hname), pmErrStr(sts));
+ ret = 0;
+ goto end;
+ }
+
+ /* fill in performance metric descriptor */
+ if ((sts = pmLookupDesc(m->desc.pmid, &m->desc)) < 0) {
+ fprintf(stderr, "%s: metric %s not currently available from %s\n"
+ "pmLookupDesc failed: %s\n",
+ pmProgname, mname, findsource(hname), pmErrStr(sts));
+ ret = 0;
+ goto end;
+ }
+
+ if (m->desc.type == PM_TYPE_STRING ||
+ m->desc.type == PM_TYPE_AGGREGATE ||
+ m->desc.type == PM_TYPE_AGGREGATE_STATIC ||
+ m->desc.type == PM_TYPE_EVENT ||
+ m->desc.type == PM_TYPE_HIGHRES_EVENT ||
+ m->desc.type == PM_TYPE_UNKNOWN) {
+ fprintf(stderr, "%s: metric %s has non-numeric type\n", pmProgname, mname);
+ ret = -1;
+ }
+ else if (m->desc.indom == PM_INDOM_NULL) {
+ if (m->specinst != 0) {
+ fprintf(stderr, "%s: metric %s has no instances\n", pmProgname, mname);
+ ret = -1;
+ }
+ else
+ m->m_idom = 1;
+ }
+ else {
+ /* metric has instances, get full instance profile */
+ if (archives) {
+ if ((sts = pmGetInDomArchive(m->desc.indom, &iids, &inames)) < 0) {
+ fprintf(stderr, "Metric %s from %s - instance domain not "
+ "available in archive\npmGetInDomArchive failed: %s\n",
+ mname, findsource(hname), pmErrStr(sts));
+ ret = -1;
+ }
+ }
+ else if ((sts = pmGetInDom(m->desc.indom, &iids, &inames)) < 0) {
+ fprintf(stderr, "Instance domain for metric %s from %s not (currently) available\n"
+ "pmGetIndom failed: %s\n", mname, findsource(hname), pmErrStr(sts));
+ ret = 0;
+ }
+
+ if (ret == 1) { /* got instance profile */
+ if (m->specinst == 0) {
+ /* all instances */
+ m->iids = iids;
+ m->m_idom = sts;
+ m->inames = alloc(m->m_idom*sizeof(char *));
+ for (i = 0; i < m->m_idom; i++) {
+ m->inames[i] = sdup(inames[i]);
+ }
+ }
+ else {
+ /* selected instances only */
+ m->m_idom = 0;
+ for (i = 0; i < m->specinst; i++) {
+ /* look for first matching instance name */
+ for (j = 0; j < sts; j++) {
+ if (eqinst(m->inames[i], inames[j])) {
+ m->iids[i] = iids[j];
+ m->m_idom++;
+ break;
+ }
+ }
+ if (j == sts) {
+ __pmNotifyErr(LOG_ERR, "metric %s from %s does not "
+ "(currently) have instance \"%s\"\n",
+ mname, findsource(hname), m->inames[i]);
+ m->iids[i] = PM_IN_NULL;
+ ret = 0;
+ }
+ }
+ if (sts > 0) {
+ /*
+ * pmGetInDom or pmGetInDomArchive returned some
+ * instances above
+ */
+ free(iids);
+ }
+
+ /*
+ * if specinst != m_idom, then some not found ... move these
+ * to the end of the list
+ */
+ for (j = m->specinst-1; j >= 0; j--) {
+ if (m->iids[j] != PM_IN_NULL)
+ break;
+ }
+ for (i = 0; i < j; i++) {
+ if (m->iids[i] == PM_IN_NULL) {
+ /* need to swap */
+ char *tp;
+ tp = m->inames[i];
+ m->inames[i] = m->inames[j];
+ m->iids[i] = m->iids[j];
+ m->inames[j] = tp;
+ m->iids[j] = PM_IN_NULL;
+ j--;
+ }
+ }
+ }
+
+#if PCP_DEBUG
+ if (pmDebug & DBG_TRACE_APPL1) {
+ int numinst;
+ fprintf(stderr, "initMetric: %s from %s: instance domain specinst=%d\n",
+ mname, hname, m->specinst);
+ if (m->m_idom < 1) fprintf(stderr, " %d instances!\n", m->m_idom);
+ numinst = m->specinst == 0 ? m->m_idom : m->specinst;
+ for (i = 0; i < numinst; i++) {
+ fprintf(stderr, " indom[%d]", i);
+ if (m->iids[i] == PM_IN_NULL)
+ fprintf(stderr, " ?missing");
+ else
+ fprintf(stderr, " %d", m->iids[i]);
+ fprintf(stderr, " \"%s\"\n", m->inames[i]);
+ }
+ }
+#endif
+ if (sts > 0) {
+ /*
+ * pmGetInDom or pmGetInDomArchive returned some instances
+ * above
+ */
+ free(inames);
+ }
+ }
+ }
+
+ if (ret == 1) {
+ /* compute conversion factor into canonical units
+ - non-zero conversion factor flags initialized metric */
+ m->conv = scale(m->desc.units);
+
+ /* automatic rate computation */
+ if (m->desc.sem == PM_SEM_COUNTER) {
+ m->vals = (double *) ralloc(m->vals, m->m_idom * sizeof(double));
+ for (j = 0; j < m->m_idom; j++)
+ m->vals[j] = 0;
+ }
+ }
+
+end:
+ /* destroy temporary context */
+ pmDestroyContext(handle);
+
+ /* retry not meaningful for archives */
+ if (archives && (ret == 0))
+ ret = -1;
+
+ return ret;
+}
+
+
+/* reinitialize Metric - only for live host */
+int /* 1: ok, 0: try again later, -1: fail */
+reinitMetric(Metric *m)
+{
+ char *hname = symName(m->hname);
+ char *mname = symName(m->mname);
+ char **inames;
+ int *iids;
+ int handle;
+ int ret = 1;
+ int sts;
+ int i, j;
+
+ /* set up temporary context */
+ if ((handle = newContext(hname)) < 0)
+ return 0;
+
+ host_state_changed(hname, STATE_RECONN);
+
+ if ((sts = pmLookupName(1, &mname, &m->desc.pmid)) < 0) {
+ ret = 0;
+ goto end;
+ }
+
+ /* fill in performance metric descriptor */
+ if ((sts = pmLookupDesc(m->desc.pmid, &m->desc)) < 0) {
+ ret = 0;
+ goto end;
+ }
+
+ if (m->desc.type == PM_TYPE_STRING ||
+ m->desc.type == PM_TYPE_AGGREGATE ||
+ m->desc.type == PM_TYPE_AGGREGATE_STATIC ||
+ m->desc.type == PM_TYPE_EVENT ||
+ m->desc.type == PM_TYPE_HIGHRES_EVENT ||
+ m->desc.type == PM_TYPE_UNKNOWN) {
+ fprintf(stderr, "%s: metric %s has non-numeric type\n", pmProgname, mname);
+ ret = -1;
+ }
+ else if (m->desc.indom == PM_INDOM_NULL) {
+ if (m->specinst != 0) {
+ fprintf(stderr, "%s: metric %s has no instances\n", pmProgname, mname);
+ ret = -1;
+ }
+ else
+ m->m_idom = 1;
+ }
+ else {
+ if ((sts = pmGetInDom(m->desc.indom, &iids, &inames)) < 0) { /* full profile */
+ ret = 0;
+ }
+ else {
+ if (m->specinst == 0) {
+ /* all instances */
+ m->iids = iids;
+ m->m_idom = sts;
+ m->inames = alloc(m->m_idom*sizeof(char *));
+ for (i = 0; i < m->m_idom; i++) {
+ m->inames[i] = sdup(inames[i]);
+ }
+ }
+ else {
+ /* explicit instance profile */
+ m->m_idom = 0;
+ for (i = 0; i < m->specinst; i++) {
+ /* look for first matching instance name */
+ for (j = 0; j < sts; j++) {
+ if (eqinst(m->inames[i], inames[j])) {
+ m->iids[i] = iids[j];
+ m->m_idom++;
+ break;
+ }
+ }
+ if (j == sts) {
+ m->iids[i] = PM_IN_NULL;
+ ret = 0;
+ }
+ }
+ if (sts > 0) {
+ /*
+ * pmGetInDom or pmGetInDomArchive returned some
+ * instances above
+ */
+ free(iids);
+ }
+
+ /*
+ * if specinst != m_idom, then some not found ... move these
+ * to the end of the list
+ */
+ for (j = m->specinst-1; j >= 0; j--) {
+ if (m->iids[j] != PM_IN_NULL)
+ break;
+ }
+ for (i = 0; i < j; i++) {
+ if (m->iids[i] == PM_IN_NULL) {
+ /* need to swap */
+ char *tp;
+ tp = m->inames[i];
+ m->inames[i] = m->inames[j];
+ m->iids[i] = m->iids[j];
+ m->inames[j] = tp;
+ m->iids[j] = PM_IN_NULL;
+ j--;
+ }
+ }
+ }
+
+#if PCP_DEBUG
+ if (pmDebug & DBG_TRACE_APPL1) {
+ int numinst;
+ fprintf(stderr, "reinitMetric: %s from %s: instance domain specinst=%d\n",
+ mname, hname, m->specinst);
+ if (m->m_idom < 1) fprintf(stderr, " %d instances!\n", m->m_idom);
+ if (m->specinst == 0) numinst = m->m_idom;
+ else numinst = m->specinst;
+ for (i = 0; i < numinst; i++) {
+ fprintf(stderr, " indom[%d]", i);
+ if (m->iids[i] == PM_IN_NULL)
+ fprintf(stderr, " ?missing");
+ else
+ fprintf(stderr, " %d", m->iids[i]);
+ fprintf(stderr, " \"%s\"\n", m->inames[i]);
+ }
+ }
+#endif
+ if (sts > 0) {
+ /*
+ * pmGetInDom or pmGetInDomArchive returned some instances
+ * above
+ */
+ free(inames);
+ }
+ }
+ }
+
+ if (ret == 1) {
+ /* compute conversion factor into canonical units
+ - non-zero conversion factor flags initialized metric */
+ m->conv = scale(m->desc.units);
+
+ /* automatic rate computation */
+ if (m->desc.sem == PM_SEM_COUNTER) {
+ m->vals = (double *) ralloc(m->vals, m->m_idom * sizeof(double));
+ for (j = 0; j < m->m_idom; j++)
+ m->vals[j] = 0;
+ }
+ }
+
+ if (ret >= 0) {
+ /*
+ * re-shape, starting here are working up the expression until
+ * we reach the top of the tree or the designated metrics
+ * associated with the node are not the same
+ */
+ Expr *x = m->expr;
+ while (x) {
+ /*
+ * only re-shape expressions that may have set values
+ */
+ if (x->op == CND_FETCH ||
+ x->op == CND_NEG || x->op == CND_ADD || x->op == CND_SUB ||
+ x->op == CND_MUL || x->op == CND_DIV ||
+ x->op == CND_SUM_HOST || x->op == CND_SUM_INST ||
+ x->op == CND_SUM_TIME ||
+ x->op == CND_AVG_HOST || x->op == CND_AVG_INST ||
+ x->op == CND_AVG_TIME ||
+ x->op == CND_MAX_HOST || x->op == CND_MAX_INST ||
+ x->op == CND_MAX_TIME ||
+ x->op == CND_MIN_HOST || x->op == CND_MIN_INST ||
+ x->op == CND_MIN_TIME ||
+ x->op == CND_EQ || x->op == CND_NEQ ||
+ x->op == CND_LT || x->op == CND_LTE ||
+ x->op == CND_GT || x->op == CND_GTE ||
+ x->op == CND_NOT || x->op == CND_AND || x->op == CND_OR ||
+ x->op == CND_RISE || x->op == CND_FALL ||
+ x->op == CND_MATCH || x->op == CND_NOMATCH) {
+ instFetchExpr(x);
+ findEval(x);
+#if PCP_DEBUG
+ if (pmDebug & DBG_TRACE_APPL1) {
+ fprintf(stderr, "reinitMetric: re-shaped ...\n");
+ dumpExpr(x);
+ }
+#endif
+ }
+ if (x->parent) {
+ x = x->parent;
+ if (x->metrics == m)
+ continue;
+ }
+ break;
+ }
+ }
+
+end:
+ /* destroy temporary context */
+ pmDestroyContext(handle);
+
+ return ret;
+}
+
+
+/* put initialised Metric onto fetch list */
+void
+bundleMetric(Host *h, Metric *m)
+{
+ Fetch *f = findFetch(h, m);
+ if (f == NULL) {
+ /*
+ * creating new fetch bundle and pmNewContext failed ...
+ * not much choice here
+ */
+ waitMetric(m);
+ }
+ else
+ /* usual case */
+ findProfile(findFetch(h, m), m);
+}
+
+
+/* reconnect attempt to host */
+int
+reconnect(Host *h)
+{
+ Fetch *f;
+
+ f = h->fetches;
+ while (f) {
+ if (pmReconnectContext(f->handle) < 0)
+ return 0;
+ if (clientid != NULL)
+ /* re-register client id with pmcd */
+ __pmSetClientId(clientid);
+ f = f->next;
+ }
+ return 1;
+}
+
+
+/* pragmatics analysis */
+void
+pragmatics(Symbol rule, RealTime delta)
+{
+ Expr *x = symValue(rule);
+ Task *t;
+
+ if (x->op != NOP) {
+ t = findTask(delta);
+ bundle(t, x);
+ t->nrules++;
+ t->rules = (Symbol *) ralloc(t->rules, t->nrules * sizeof(Symbol));
+ t->rules[t->nrules-1] = symCopy(rule);
+ perf->eval_expected += (float)1/delta;
+ }
+}
+
+/*
+ * find all expressions for a host that has just been marked "down"
+ * and invalidate them
+ */
+static void
+mark_all(Host *hdown)
+{
+ Task *t;
+ Symbol *s;
+ Metric *m;
+ Expr *x;
+ int i;
+
+ for (t = taskq; t != NULL; t = t->next) {
+ s = t->rules;
+ for (i = 0; i < t->nrules; i++, s++) {
+ x = (Expr *)symValue(*s);
+ for (m = x->metrics; m != NULL; m = m->next) {
+ if (m->host == hdown)
+ clobber(x);
+ }
+ }
+ }
+}
+
+/* execute fetches for given Task */
+void
+taskFetch(Task *t)
+{
+ Host *h;
+ Fetch *f;
+ Profile *p;
+ Metric *m;
+ pmResult *r;
+ pmValueSet **v;
+ int i;
+ int sts;
+
+ /* do all fetches, quick as you can */
+ h = t->hosts;
+ while (h) {
+ f = h->fetches;
+ while (f) {
+ if (f->result) pmFreeResult(f->result);
+ if (! h->down) {
+ pmUseContext(f->handle);
+ if ((sts = pmFetch(f->npmids, f->pmids, &f->result)) < 0) {
+ if (! archives) {
+ __pmNotifyErr(LOG_ERR, "pmFetch from %s failed: %s\n",
+ symName(f->host->name), pmErrStr(sts));
+ host_state_changed(symName(f->host->name), STATE_LOSTCONN);
+ h->down = 1;
+ mark_all(h);
+ }
+ f->result = NULL;
+ }
+ }
+ else
+ f->result = NULL;
+ f = f->next;
+ }
+ h = h->next;
+ }
+
+ /* sort and distribute pmValueSets to requesting Metrics */
+ h = t->hosts;
+ while (h) {
+ if (! h->down) {
+ f = h->fetches;
+ while (f && (r = f->result)) {
+ /* sort all vlists in result r */
+ v = r->vset;
+ for (i = 0; i < r->numpmid; i++) {
+ if ((*v)->numval > 0) {
+ qsort((*v)->vlist, (size_t)(*v)->numval,
+ sizeof(pmValue), compair);
+ }
+ v++;
+ }
+
+ /* distribute pmValueSets to Metrics */
+ p = f->profiles;
+ while (p) {
+ m = p->metrics;
+ while (m) {
+ for (i = 0; i < r->numpmid; i++) {
+ if (m->desc.pmid == r->vset[i]->pmid) {
+ if (r->vset[i]->numval > 0) {
+ m->vset = r->vset[i];
+ m->stamp = realize(r->timestamp);
+ }
+ break;
+ }
+ }
+ m = m->next;
+ }
+ p = p->next;
+ }
+ f = f->next;
+ }
+ }
+ h = h->next;
+ }
+}
+
+
+/* send pmDescriptors for all expressions in given task */
+void
+sendDescs(Task *task)
+{
+ Symbol *s;
+ int i;
+
+ s = task->rules;
+ for (i = 0; i < task->nrules; i++) {
+ sendDesc(symValue(*s), task->rslt->vset[i]);
+ s++;
+ }
+}
+
+
+/* convert Expr value to pmValueSet value */
+void
+fillVSet(Expr *x, pmValueSet *vset)
+{
+ if (x->valid > 0) {
+ if (finite(*((double *)x->ring))) { /* copy value */
+ vset->numval = 1;
+ memcpy(&vset->vlist[0].value.pval->vbuf, x->ring, sizeof(double));
+ }
+ else /* value not representable */
+ vset->numval = 0;
+ }
+ else { /* value not available */
+ vset->numval = PM_ERR_VALUE;
+ }
+}
diff --git a/src/pmie/src/pragmatics.h b/src/pmie/src/pragmatics.h
new file mode 100644
index 0000000..bee5f88
--- /dev/null
+++ b/src/pmie/src/pragmatics.h
@@ -0,0 +1,103 @@
+/***********************************************************************
+ * pragmatics.h - inference engine pragmatics analysis
+ *
+ * The analysis of how to organize the fetching of metrics (pragmatics)
+ * and other parts of the inference engine that are particularly
+ * sensitive to details of the performance metrics API are kept here.
+ ***********************************************************************
+ *
+ * Copyright (c) 1995 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#ifndef PRAG_H
+#define PRAG_H
+
+#include "pmapi.h"
+
+/* report where PCP data is coming from */
+char *findsource(char *);
+
+/* initialize performance metrics API */
+void pmcsInit(void);
+
+/* juggle contexts */
+int newContext(char *);
+
+/* initialize access to archive */
+int initArchive(Archive *);
+
+/* initialize timezone */
+void zoneInit(void);
+
+/* convert to canonical units */
+pmUnits canon(pmUnits);
+
+/* scale factor to canonical pmUnits */
+double scale(pmUnits);
+
+/* initialize Metric */
+int initMetric(Metric *);
+
+/* reinitialize Metric */
+int reinitMetric(Metric *);
+
+/* put initialiaed Metric onto fetch list */
+void bundleMetric(Host *, Metric *);
+
+/* reconnect attempt to host */
+int reconnect(Host *);
+
+/* pragmatics analysis */
+void pragmatics(Symbol, RealTime);
+
+/* execute fetches for given Task */
+void taskFetch(Task *);
+
+/* convert Expr value to pmValueSet value */
+void fillVSet(Expr *, pmValueSet *);
+
+/* send pmDescriptors for all expressions in given task */
+void sendDescs(Task *);
+
+/* put Metric onto wait list */
+void waitMetric(Metric *);
+
+/* remove Metric from wait list */
+void unwaitMetric(Metric *);
+
+/* check that pmUnits dimensions are equal */
+#define dimeq(x, y) (((x).dimSpace == (y).dimSpace) && \
+ ((x).dimTime == (y).dimTime) && \
+ ((x).dimCount == (y).dimCount))
+
+/* check equality of two pmUnits */
+#define unieq(x, y) (((x).dimSpace == (y).dimSpace) && \
+ ((x).dimTime == (y).dimTime) && \
+ ((x).dimCount == (y).dimCount) && \
+ ((x).scaleSpace == (y).scaleSpace) && \
+ ((x).scaleTime == (y).scaleTime) && \
+ ((x).scaleCount == (y).scaleCount))
+
+/* for initialization of pmUnits struct */
+extern pmUnits noUnits;
+extern pmUnits countUnits;
+
+/* flag processes spawned */
+extern int need_wait;
+
+#endif /* PRAG_H */
+
+
diff --git a/src/pmie/src/show.c b/src/pmie/src/show.c
new file mode 100644
index 0000000..f879679
--- /dev/null
+++ b/src/pmie/src/show.c
@@ -0,0 +1,1104 @@
+/***********************************************************************
+ * show.c - display expressions and their values
+ ***********************************************************************
+ *
+ * Copyright (c) 1995-2003 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <math.h>
+#include <ctype.h>
+#include <assert.h>
+#include "show.h"
+#include "impl.h"
+#include "dstruct.h"
+#include "lexicon.h"
+#include "pragmatics.h"
+#if defined(HAVE_IEEEFP_H)
+#include <ieeefp.h>
+#endif
+
+/***********************************************************************
+ * local declarations
+ ***********************************************************************/
+
+static struct {
+ int op;
+ char *str;
+} opstr[] = {
+ { RULE, "->" },
+ { CND_FETCH, "<fetch node>" },
+ { CND_DELAY, "<delay node>" },
+ { CND_RATE, "rate" },
+ { CND_NEG, "-" },
+ { CND_ADD, "+" },
+ { CND_SUB, "-" },
+ { CND_MUL, "*" },
+ { CND_DIV, "/" },
+/* aggregation */
+ { CND_SUM_HOST, "sum_host" },
+ { CND_SUM_INST, "sum_inst" },
+ { CND_SUM_TIME, "sum_sample" },
+ { CND_AVG_HOST, "avg_host" },
+ { CND_AVG_INST, "avg_inst" },
+ { CND_AVG_TIME, "avg_sample" },
+ { CND_MAX_HOST, "max_host" },
+ { CND_MAX_INST, "max_inst" },
+ { CND_MAX_TIME, "max_sample" },
+ { CND_MIN_HOST, "min_host" },
+ { CND_MIN_INST, "min_inst" },
+ { CND_MIN_TIME, "min_sample" },
+/* relational */
+ { CND_EQ, "==" },
+ { CND_NEQ, "!=" },
+ { CND_LT, "<" },
+ { CND_LTE, "<=" },
+ { CND_GT, ">" },
+ { CND_GTE, ">=" },
+/* boolean */
+ { CND_NOT, "!" },
+ { CND_RISE, "rising" },
+ { CND_FALL, "falling" },
+ { CND_AND, "&&" },
+ { CND_OR, "||" },
+ { CND_MATCH, "match_inst" },
+ { CND_NOMATCH, "nomatch_inst" },
+ { CND_RULESET, "ruleset" },
+ { CND_OTHER, "other" },
+/* quantification */
+ { CND_ALL_HOST, "all_host" },
+ { CND_ALL_INST, "all_inst" },
+ { CND_ALL_TIME, "all_sample" },
+ { CND_SOME_HOST, "some_host" },
+ { CND_SOME_INST, "some_inst" },
+ { CND_SOME_TIME, "some_sample" },
+ { CND_PCNT_HOST, "pcnt_host" },
+ { CND_PCNT_INST, "pcnt_inst" },
+ { CND_PCNT_TIME, "pcnt_sample" },
+ { CND_COUNT_HOST, "count_host" },
+ { CND_COUNT_INST, "count_inst" },
+ { CND_COUNT_TIME, "count_sample" },
+ { ACT_SEQ, "&" },
+ { ACT_ALT, "|" },
+ { ACT_SHELL, "shell" },
+ { ACT_ALARM, "alarm" },
+ { ACT_SYSLOG, "syslog" },
+ { ACT_PRINT, "print" },
+ { ACT_STOMP, "stomp" },
+ { ACT_ARG, "<action arg node>" },
+ { NOP, "<nop node>" },
+ { OP_VAR, "<op_var node>" },
+};
+
+static int numopstr = sizeof(opstr) / sizeof(opstr[0]);
+
+/***********************************************************************
+ * local utility functions
+ ***********************************************************************/
+
+/* Concatenate string1 to existing string2 whose original length is given. */
+static size_t /* new length of *string2 */
+concat(char *string1, size_t pos, char **string2)
+{
+ size_t slen;
+ size_t tlen;
+ char *cat;
+ char *dog;
+
+ if ((slen = strlen(string1)) == 0)
+ return pos;
+ tlen = pos + slen;
+ cat = (char *) ralloc(*string2, tlen + 1);
+ dog = cat + pos;
+ strcpy(dog, string1);
+ dog += slen;
+ *dog = '\0';
+
+ *string2 = cat;
+ return tlen;
+}
+
+
+/***********************************************************************
+ * host and instance names
+ ***********************************************************************/
+
+/* Return host and instance name for nth value in expression *x */
+static int
+lookupHostInst(Expr *x, int nth, char **host, char **inst)
+{
+ Metric *m = NULL;
+ int mi;
+ int sts = 0;
+ int pick = -1;
+ int matchaggr = 0;
+ int aggrop = NOP;
+ double *aggrval = NULL;
+#if PCP_DEBUG
+ static Expr *lastx = NULL;
+ int dbg_dump = 0;
+#endif
+
+#if PCP_DEBUG
+ if (pmDebug & DBG_TRACE_APPL2) {
+ if (x != lastx) {
+ fprintf(stderr, "lookupHostInst(x=" PRINTF_P_PFX "%p, nth=%d, ...)\n", x, nth);
+ lastx = x;
+ dbg_dump = 1;
+ }
+ }
+#endif
+ if (x->op == CND_MIN_HOST || x->op == CND_MAX_HOST ||
+ x->op == CND_MIN_INST || x->op == CND_MAX_INST ||
+ x->op == CND_MIN_TIME || x->op == CND_MAX_TIME) {
+ /*
+ * extrema operators ... value is here, but the host, instance, sample
+ * context is in the child expression ... go one level deeper and try
+ * to match the value
+ */
+ aggrop = x->op;
+ aggrval = (double *)x->smpls[0].ptr;
+ matchaggr = 1;
+#if PCP_DEBUG
+ if (pmDebug & DBG_TRACE_APPL2) {
+ fprintf(stderr, "lookupHostInst look for extrema val=%f @ " PRINTF_P_PFX "%p\n", *aggrval, x);
+ }
+ x = x->arg1;
+#endif
+ }
+
+ /* check for no host and instance available e.g. constant expression */
+ if ((x->e_idom <= 0 && x->hdom <= 0) || ! x->metrics) {
+ *host = NULL;
+ *inst = NULL;
+#if PCP_DEBUG
+ if (pmDebug & DBG_TRACE_APPL2) {
+ fprintf(stderr, "lookupHostInst(x=" PRINTF_P_PFX "%p, nth=%d, ...) -> %%h and %%i undefined\n", x, nth);
+ }
+#endif
+ return sts;
+ }
+
+ /* find Metric containing the nth instance */
+ if (matchaggr == 0) {
+ pick = nth;
+ mi = 0;
+ for (;;) {
+ m = &x->metrics[mi];
+#if PCP_DEBUG
+ if ((pmDebug & DBG_TRACE_APPL2) && dbg_dump) {
+ fprintf(stderr, "lookupHostInst: metrics[%d]\n", mi);
+ dumpMetric(m);
+ }
+#endif
+ if (pick < m->m_idom)
+ break;
+ if (m->m_idom > 0)
+ pick -= m->m_idom;
+ mi++;
+ }
+ }
+ else {
+ if (aggrop == CND_MIN_HOST || aggrop == CND_MAX_HOST) {
+ int k;
+#if PCP_DEBUG
+ if ((pmDebug & DBG_TRACE_APPL2) && dbg_dump) {
+ fprintf(stderr, "lookupHostInst [extrema_host]:\n");
+ }
+#endif
+ for (k = 0; k < x->tspan; k++) {
+#if DESPERATE
+ fprintf(stderr, "smpls[0][%d]=%g\n", k, *((double *)x->smpls[0].ptr+k));
+#endif
+ if (*aggrval == *((double *)x->smpls[0].ptr+k)) {
+ m = &x->metrics[k];
+ goto done;
+ }
+ }
+ fprintf(stderr, "Internal error: LookupHostInst: %s\n", opStrings(aggrop));
+ }
+ else if (aggrop == CND_MIN_INST || aggrop == CND_MAX_INST) {
+ int k;
+ for (k = 0; k < x->tspan; k++) {
+#if DESPERATE
+ fprintf(stderr, "smpls[0][%d]=%g\n", k, *((double *)x->smpls[0].ptr+k));
+#endif
+ if (*aggrval == *((double *)x->smpls[0].ptr+k)) {
+ pick = k;
+ m = &x->metrics[0];
+#if PCP_DEBUG
+ if ((pmDebug & DBG_TRACE_APPL2) && dbg_dump) {
+ fprintf(stderr, "lookupHostInst [extrema_inst]:\n");
+ dumpMetric(m);
+ }
+#endif
+ goto done;
+ }
+ }
+ fprintf(stderr, "Internal error: LookupHostInst: %s\n", opStrings(aggrop));
+ }
+ else if (aggrop == CND_MIN_TIME || aggrop == CND_MAX_TIME) {
+ int k;
+ for (k = 0; k < x->nsmpls; k++) {
+#if DESPERATE
+ fprintf(stderr, "smpls[%d][0]=%g\n", k, *((double *)x->smpls[k].ptr));
+#endif
+ if (*aggrval == *((double *)x->smpls[k].ptr)) {
+ pick = nth;
+ m = &x->metrics[0];
+#if PCP_DEBUG
+ if ((pmDebug & DBG_TRACE_APPL2) && dbg_dump) {
+ fprintf(stderr, "lookupHostInst [extrema_sample]:\n");
+ dumpMetric(m);
+ }
+#endif
+ goto done;
+ }
+ }
+ fprintf(stderr, "Internal error: LookupHostInst: %s\n", opStrings(aggrop));
+ }
+ }
+
+done:
+ /* host and instance names */
+ if (m == NULL) {
+ *host = NULL;
+ *inst = NULL;
+ }
+ else {
+ *host = symName(m->hname);
+ sts++;
+ if (pick >= 0 && x->e_idom > 0 && m->inames) {
+ *inst = m->inames[pick];
+ sts++;
+ }
+ else
+ *inst = NULL;
+ }
+
+#if PCP_DEBUG
+ if (pmDebug & DBG_TRACE_APPL2) {
+ fprintf(stderr, "lookupHostInst(x=" PRINTF_P_PFX "%p, nth=%d, ...) -> sts=%d %%h=%s %%i=%s\n",
+ x, nth, sts, *host, *inst == NULL ? "undefined" : *inst);
+ }
+#endif
+
+ return sts;
+}
+
+
+/***********************************************************************
+ * expression value
+ ***********************************************************************/
+
+#define BOOLEAN_SPACE 8
+
+static size_t
+showBoolean(Expr *x, int nth, size_t length, char **string)
+{
+ int smpl;
+ size_t tlen;
+ char *cat;
+ char *dog;
+ int val;
+
+ tlen = length + (x->nsmpls * BOOLEAN_SPACE);
+ cat = (char *)ralloc(*string, tlen + 1);
+ dog = cat + length;
+ for (smpl = 0; smpl < x->nsmpls; smpl++) {
+ if (smpl > 0) {
+ strcpy(dog, " ");
+ dog += 1;
+ }
+
+ if (x->valid == 0) {
+ strncpy(dog, "unknown", BOOLEAN_SPACE);
+ dog += strlen("unknown");
+ continue;
+ }
+
+ val = *((char *)x->smpls[smpl].ptr + nth);
+ if (val == B_FALSE) {
+ strncpy(dog, "false", BOOLEAN_SPACE);
+ dog += strlen("false");
+ }
+ else if (val == B_TRUE) {
+ strncpy(dog, "true", BOOLEAN_SPACE);
+ dog += strlen("true");
+ }
+ else if (val == B_UNKNOWN) {
+ strncpy(dog, "unknown", BOOLEAN_SPACE);
+ dog += strlen("unknown");
+ }
+ else {
+ sprintf(dog, "0x%02x?", val & 0xff);
+ dog += 5;
+ }
+ }
+ *dog = '\0';
+
+ *string = cat;
+ return dog - cat;
+}
+
+
+static size_t
+showString(Expr *x, size_t length, char **string)
+{
+ size_t slen;
+ size_t tlen;
+ char *cat;
+ char *dog;
+
+ slen = strlen((char *)x->smpls[0].ptr);
+ tlen = length + slen + 2;
+ cat = (char *)ralloc(*string, tlen + 1);
+ dog = cat + length;
+ *dog++ = '"';
+ strcpy(dog, (char *)x->smpls[0].ptr);
+ dog += slen;
+ *dog++ = '"';
+ *dog = '\0';
+
+ *string = cat;
+ return tlen;
+}
+
+#define DBL_SPACE 24
+
+static size_t
+showNum(Expr *x, int nth, size_t length, char **string)
+{
+ int smpl;
+ size_t tlen;
+ char *cat;
+ char *dog;
+ char *fmt;
+ double v;
+ double abs_v;
+ int sts;
+
+ tlen = length + (x->nsmpls * DBL_SPACE);
+ cat = (char *)ralloc(*string, tlen + 1);
+ dog = cat + length;
+ for (smpl = 0; smpl < x->nsmpls; smpl++) {
+ int noval = 0;
+ if (smpl > 0) {
+ strcpy(dog, " ");
+ dog++;
+ }
+ if (x->valid <= smpl)
+ noval = 1;
+ else {
+#ifdef HAVE_FPCLASSIFY
+ noval = fpclassify(*((double *)x->smpls[smpl].ptr + nth)) == FP_NAN;
+#else
+#ifdef HAVE_ISNAN
+ noval = isnan(*((double *)x->smpls[smpl].ptr + nth));
+#endif
+#endif
+ }
+ if (noval) {
+ if (x->sem == SEM_BOOLEAN) {
+ strcpy(dog, "unknown");
+ dog += strlen("unknown");
+ }
+ else {
+ strcpy(dog, "?");
+ dog++;
+ }
+ }
+ else {
+ v = *((double *)x->smpls[smpl].ptr+nth);
+ if (v == (int)v)
+ sts = sprintf(dog, "%d", (int)v);
+ else {
+ abs_v = v < 0 ? -v : v;
+ if (abs_v < 0.5)
+ fmt = "%g";
+ else if (abs_v < 5)
+ fmt = "%.2f";
+ else if (abs_v < 50)
+ fmt = "%.1f";
+ else
+ fmt = "%.0f";
+ sts = sprintf(dog, fmt, v);
+ }
+ if (sts > 0)
+ dog += sts;
+ else {
+ strcpy(dog, "!");
+ dog += 1;
+ }
+ }
+ }
+ *dog = '\0';
+
+ *string = cat;
+ return dog - cat;
+}
+
+static char *
+showConst(Expr *x)
+{
+ char *string = NULL;
+ size_t length = 0;
+ int i;
+ int first = 1;
+
+ /* construct string representation */
+ if (x->nvals > 0) {
+ for (i = 0; i < x->tspan; i++) {
+ if (first)
+ first = 0;
+ else
+ length = concat(" ", length, &string);
+ if (x->sem == SEM_BOOLEAN)
+ length = showBoolean(x, i, length, &string);
+ else if (x->sem == SEM_REGEX) {
+ /* regex is compiled, cannot recover original string */
+ length = concat("/<regex>/", length, &string);
+ }
+ else if (x->sem == SEM_CHAR) {
+ length = showString(x, length, &string);
+ /* tspan is string length, not an iterator in this case */
+ break;
+ }
+ else
+ length = showNum(x, i, length, &string);
+ }
+ }
+ return string;
+}
+
+
+
+/***********************************************************************
+ * expression syntax
+ ***********************************************************************/
+
+static void
+showSyn(FILE *f, Expr *x)
+{
+ char *s;
+ char *c;
+ Metric *m;
+ char **n;
+ int i;
+ int paren;
+
+ if (x->op == NOP) {
+ /* constant */
+ s = showConst(x);
+ if (s) {
+ c = s;
+ while(isspace((int)*c))
+ c++;
+ fputs(c, f);
+ free(s);
+ }
+ }
+ else if ((x->op == CND_FETCH) || (x->op == CND_DELAY)) {
+ /* fetch expression (perhaps with delay) */
+ m = x->metrics;
+ fprintf(f, "%s", symName(m->mname));
+ for (i = 0; i < x->hdom; i++) {
+ fprintf(f, " :%s", symName(m->hname));
+ m++;
+ }
+ m = x->metrics;
+ if (m->inames) {
+ n = m->inames;
+ for (i = 0; i < m->m_idom; i++) {
+ fprintf(f, " #%s", *n);
+ n++;
+ }
+ }
+ if (x->op == CND_FETCH) {
+ if (x->tdom > 1)
+ fprintf(f, " @0..%d", x->tdom - 1);
+ }
+ else {
+ if (x->tdom == x->arg1->tdom - 1) fprintf(f, " @%d", x->tdom);
+ else fprintf(f, " @%d..%d", x->tdom, x->tdom + x->arg1->tdom - 1);
+ }
+ }
+ else if (x->arg1 && x->arg2) {
+ /* binary operator */
+ if (x->op == ACT_SHELL || x->op == ACT_ALARM || x->op == ACT_PRINT ||
+ x->op == ACT_STOMP) {
+ fputs(opStrings(x->op), f);
+ fputc(' ', f);
+ showSyn(f, x->arg2);
+ fputc(' ', f);
+ showSyn(f, x->arg1);
+ }
+ else if (x->op == ACT_ARG && x->parent->op == ACT_SYSLOG) {
+ int *ip;
+ char *cp;
+ ip = x->arg2->ring;
+ cp = (char *)&ip[1];
+ fprintf(f, "[level=%d tag=\"%s\"]", *ip, cp);
+ fputc(' ', f);
+ showSyn(f, x->arg1);
+ }
+ else if (x->op == CND_PCNT_HOST || x->op == CND_PCNT_INST || x->op == CND_PCNT_TIME) {
+ int pcnt;
+ fputs(opStrings(x->op), f);
+ fputc(' ', f);
+ /*
+ * used to showSyn(f, x->arg2) here, but formatting is a little
+ * better if we punt on there being a single double representation
+ * of the % value at the end of arg2
+ */
+ pcnt = (int)(*((double *)x->arg2->smpls[0].ptr)*100+0.5);
+ fprintf(f, "%d%%", pcnt);
+ fputc(' ', f);
+ if (x->arg1->op == NOP || x->arg1->op == CND_DELAY || x->arg1->op == CND_FETCH)
+ showSyn(f, x->arg1);
+ else {
+ fputc('(', f);
+ showSyn(f, x->arg1);
+ fputc(')', f);
+ }
+ }
+ else if (x->op == CND_MATCH || x->op == CND_NOMATCH) {
+ fputs(opStrings(x->op), f);
+ fputc(' ', f);
+ showSyn(f, x->arg2);
+ fputc(' ', f);
+ fputc('(', f);
+ showSyn(f, x->arg1);
+ fputc(')', f);
+ }
+ else {
+ paren = 1 -
+ (x->arg1->op == NOP || x->arg1->op == CND_DELAY ||
+ x->arg1->op == CND_FETCH || x->arg1->op == CND_RATE ||
+ x->arg1->op == CND_SUM_HOST || x->arg1->op == CND_SUM_INST ||
+ x->arg1->op == CND_SUM_TIME || x->arg1->op == CND_AVG_HOST ||
+ x->arg1->op == CND_AVG_INST || x->arg1->op == CND_AVG_TIME ||
+ x->arg1->op == CND_MAX_HOST || x->arg1->op == CND_MAX_INST ||
+ x->arg1->op == CND_MAX_TIME || x->arg1->op == CND_MIN_HOST ||
+ x->arg1->op == CND_MIN_INST || x->arg1->op == CND_MIN_TIME ||
+ x->arg1->op == CND_COUNT_HOST || x->arg1->op == CND_COUNT_INST ||
+ x->arg1->op == CND_COUNT_TIME || x->arg1->op == CND_RATE ||
+ x->op == RULE);
+ if (paren)
+ fputc('(', f);
+ showSyn(f, x->arg1);
+ if (paren)
+ fputc(')', f);
+ fputc(' ', f);
+ fputs(opStrings(x->op), f);
+ fputc(' ', f);
+ paren = 1 -
+ (x->arg2->op == NOP || x->arg2->op == CND_DELAY ||
+ x->arg2->op == CND_FETCH || x->arg2->op == CND_RATE ||
+ x->arg2->op == CND_SUM_HOST || x->arg2->op == CND_SUM_INST ||
+ x->arg2->op == CND_SUM_TIME || x->arg2->op == CND_AVG_HOST ||
+ x->arg2->op == CND_AVG_INST || x->arg2->op == CND_AVG_TIME ||
+ x->arg2->op == CND_MAX_HOST || x->arg2->op == CND_MAX_INST ||
+ x->arg2->op == CND_MAX_TIME || x->arg2->op == CND_MIN_HOST ||
+ x->arg2->op == CND_MIN_INST || x->arg2->op == CND_MIN_TIME ||
+ x->arg2->op == CND_COUNT_HOST || x->arg2->op == CND_COUNT_INST ||
+ x->arg2->op == CND_COUNT_TIME || x->arg2->op == CND_RATE ||
+ x->op == RULE);
+ if (paren)
+ fputc('(', f);
+ showSyn(f, x->arg2);
+ if (paren)
+ fputc(')', f);
+ }
+ }
+ else {
+ /* unary operator */
+ assert(x->arg1 != NULL);
+ if (x->op == ACT_ARG) {
+ /* parameters for an action */
+ Expr *y = x->arg1;
+ while (y != NULL) {
+ if (y != x->arg1)
+ fputc(' ', f);
+ showSyn(f, y);
+ // fprintf(f, "\"%s\"", (char *)y->smpls[0].ptr);
+ y = y->arg1;
+ }
+ }
+ else {
+ fputs(opStrings(x->op), f);
+ fputc(' ', f);
+ paren = 1 -
+ (x->arg1->op == ACT_SEQ || x->arg1->op == ACT_ALT ||
+ x->op == ACT_SHELL || x->op == ACT_ALARM ||
+ x->op == ACT_SYSLOG || x->op == ACT_PRINT ||
+ x->op == ACT_STOMP || x->op == CND_DELAY);
+ if (paren)
+ fputc('(', f);
+ showSyn(f, x->arg1);
+ if (paren)
+ fputc(')', f);
+ }
+ }
+}
+
+/*
+ * recursive descent to find a conjunct from the root of
+ * the expression that has associated metrics (not constants)
+ */
+static Expr *
+findMetrics(Expr *y)
+{
+ Expr *z;
+
+ if (y == NULL) return NULL;
+ if (y->metrics) return y; /* success */
+
+ /* give up if not a conjunct */
+ if (y->op != CND_AND) return NULL;
+
+ /* recurse left and then right */
+ z = findMetrics(y->arg1);
+ if (z != NULL) return z;
+ return findMetrics(y->arg2);
+}
+
+/***********************************************************************
+ * satisfying bindings and values
+ ***********************************************************************/
+
+/* Find sub-expression that reveals host and instance bindings
+ that satisfy the given expression *x. */
+static Expr *
+findBindings(Expr *x)
+{
+#if PCP_DEBUG
+ if (pmDebug & DBG_TRACE_APPL2) {
+ fprintf(stderr, "call findBindings(x=" PRINTF_P_PFX "%p)\n", x);
+ }
+#endif
+
+ if (x->metrics == NULL) {
+ /*
+ * this Expr node has no metrics (involves only constants)
+ * ... try and find a conjunct at the top level that has
+ * associated metrics
+ */
+ Expr *y = findMetrics(x->arg1);
+ if (y != NULL) x = y;
+ }
+ while (x->metrics && (x->e_idom <= 0 || x->hdom <= 0)) {
+ if (x->op == CND_SUM_HOST || x->op == CND_SUM_INST || x->op == CND_SUM_TIME ||
+ x->op == CND_AVG_HOST || x->op == CND_AVG_INST || x->op == CND_AVG_TIME ||
+ x->op == CND_MAX_HOST || x->op == CND_MAX_INST || x->op == CND_MAX_TIME ||
+ x->op == CND_MIN_HOST || x->op == CND_MIN_INST || x->op == CND_MIN_TIME ||
+ x->op == CND_COUNT_HOST || x->op == CND_COUNT_INST || x->op == CND_COUNT_TIME) {
+ /*
+ * don't descend below an aggregation operator with a singular
+ * value, ... value you seek is right here
+ */
+#if PCP_DEBUG
+ if (pmDebug & DBG_TRACE_APPL2) {
+ fprintf(stderr, "findBindings: found %s @ x=" PRINTF_P_PFX "%p\n", opStrings(x->op), x);
+ }
+#endif
+ break;
+ }
+ if (x->arg1 && x->metrics == x->arg1->metrics) {
+ x = x->arg1;
+#if PCP_DEBUG
+ if (pmDebug & DBG_TRACE_APPL2) {
+ fprintf(stderr, "findBindings: try x->arg1=" PRINTF_P_PFX "%p\n", x);
+ }
+#endif
+ }
+ else if (x->arg2) {
+ x = x->arg2;
+#if PCP_DEBUG
+ if (pmDebug & DBG_TRACE_APPL2) {
+ fprintf(stderr, "findBindings: try x->arg2=" PRINTF_P_PFX "%p\n", x);
+ }
+#endif
+ }
+ else
+ break;
+ }
+#if PCP_DEBUG
+ if (pmDebug & DBG_TRACE_APPL2) {
+ fprintf(stderr, "findBindings finish @ " PRINTF_P_PFX "%p\n", x);
+ dumpTree(x);
+ }
+#endif
+ return x;
+}
+
+
+/* Find sub-expression that reveals the values that satisfy the
+ given expression *x. */
+static Expr *
+findValues(Expr *x)
+{
+#if PCP_DEBUG
+ if (pmDebug & DBG_TRACE_APPL2) {
+ fprintf(stderr, "call findValues(x=" PRINTF_P_PFX "%p)\n", x);
+ }
+#endif
+ while (x->sem == SEM_BOOLEAN && x->metrics) {
+ if (x->metrics == x->arg1->metrics) {
+ x = x->arg1;
+#if PCP_DEBUG
+ if (pmDebug & DBG_TRACE_APPL2) {
+ fprintf(stderr, "findValues: try x->arg1=" PRINTF_P_PFX "%p\n", x);
+ }
+#endif
+ }
+ else {
+ x = x->arg2;
+#if PCP_DEBUG
+ if (pmDebug & DBG_TRACE_APPL2) {
+ fprintf(stderr, "findValues: try x->arg2=" PRINTF_P_PFX "%p\n", x);
+ }
+#endif
+ }
+ }
+#if PCP_DEBUG
+ if (pmDebug & DBG_TRACE_APPL2) {
+ fprintf(stderr, "findValues finish @ " PRINTF_P_PFX "%p\n", x);
+ dumpTree(x);
+ }
+#endif
+ return x;
+}
+
+
+/***********************************************************************
+ * format string
+ ***********************************************************************/
+
+/* Locate next %h, %i or %v in format string. */
+static int /* 0 -> not found, 1 -> host, 2 -> inst, 3 -> value */
+findFormat(char *format, char **pos)
+{
+ for (;;) {
+ if (*format == '\0')
+ return 0;
+ if (*format == '%') {
+ switch (*(format + 1)) {
+ case 'h':
+ *pos = format;
+ return 1;
+ case 'i':
+ *pos = format;
+ return 2;
+ case 'v':
+ *pos = format;
+ return 3;
+ }
+ }
+ format++;
+ }
+
+}
+
+
+/***********************************************************************
+ * exported functions
+ ***********************************************************************/
+
+void
+showSyntax(FILE *f, Symbol s)
+{
+ char *name = symName(s);
+ Expr *x = symValue(s);
+
+ fprintf(f, "%s =\n", name);
+ showSyn(f, x);
+ fprintf(f, ";\n\n");
+}
+
+
+void
+showSubsyntax(FILE *f, Symbol s)
+{
+ char *name = symName(s);
+ Expr *x = symValue(s);
+ Expr *x1;
+ Expr *x2;
+
+ fprintf(f, "%s (subexpression for %s, %s and %s bindings) =\n",
+ name, "%h", "%i", "%v");
+ x1 = findBindings(x);
+ x2 = findValues(x1);
+ showSyn(f, x2);
+ fprintf(f, "\n\n");
+}
+
+
+/* Print value of expression */
+void
+showValue(FILE *f, Expr *x)
+{
+ char *string = NULL;
+
+ string = showConst(x);
+ if (string) {
+ fputs(string, f);
+ free(string);
+ }
+ else
+ fputs("?", f);
+}
+
+
+/* Print value of expression together with any host and instance bindings */
+void
+showAnnotatedValue(FILE *f, Expr *x)
+{
+ char *string = NULL;
+ size_t length = 0;
+ char *host;
+ char *inst;
+ int i;
+
+ /* no annotation possible */
+ if ((x->e_idom <= 0 && x->hdom <= 0) ||
+ x->sem == SEM_CHAR ||
+ x->metrics == NULL ||
+ x->valid == 0) {
+ showValue(f, x);
+ return;
+ }
+
+ /* construct string representation */
+ for (i = 0; i < x->tspan; i++) {
+ length = concat("\n ", length, &string);
+ lookupHostInst(x, i, &host, &inst);
+ length = concat(host, length, &string);
+ if (inst) {
+ length = concat(": [", length, &string);
+ length = concat(inst, length, &string);
+ length = concat("] ", length, &string);
+ }
+ else
+ length = concat(": ", length, &string);
+ if (x->sem == SEM_BOOLEAN)
+ length = showBoolean(x, i, length, &string);
+ else /* numeric value */
+ length = showNum(x, i, length, &string);
+ }
+
+ /* print string representation */
+ if (string) {
+ fputs(string, f);
+ free(string);
+ }
+}
+
+
+void
+showTime(FILE *f, RealTime rt)
+{
+ time_t t = (time_t)rt;
+ char bfr[26];
+
+ pmCtime(&t, bfr);
+ bfr[24] = '\0';
+ fprintf(f, "%s", bfr);
+}
+
+
+void
+showFullTime(FILE *f, RealTime rt)
+{
+ time_t t = (time_t)rt;
+ char bfr[26];
+
+ pmCtime(&t, bfr);
+ bfr[24] = '\0';
+ fprintf(f, "%s.%06d", bfr, (int)((rt-t)*1000000));
+}
+
+
+void
+showSatisfyingValue(FILE *f, Expr *x)
+{
+ char *string = NULL;
+ size_t length = 0;
+ char *host;
+ char *inst;
+ int i;
+ Expr *x1;
+ Expr *x2;
+
+ /* no satisfying values possible */
+ if (x->metrics == NULL || x->valid == 0) {
+ showValue(f, x);
+ return;
+ }
+
+ if (x->sem != SEM_BOOLEAN) {
+ showAnnotatedValue(f, x);
+ return;
+ }
+
+ x1 = findBindings(x);
+ x2 = findValues(x1);
+ if (!x1->valid) {
+ /*
+ * subexpression for %h, %i and %v is not valid but rule is
+ * true, return string without substitution ... rare case
+ * for <bad-or-not evaluated expr> || <true expr> rule
+ */
+ concat(" <no bindings available>", length, &string);
+ goto done;
+ }
+
+ /* construct string representation */
+ for (i = 0; i < x1->tspan; i++) {
+ if ((x1->sem == SEM_BOOLEAN && *((char *)x1->smpls[0].ptr + i) == B_TRUE)
+ || (x1->sem != SEM_BOOLEAN && x1->sem != SEM_UNKNOWN)) {
+ length = concat("\n ", length, &string);
+ lookupHostInst(x1, i, &host, &inst);
+ length = concat(host, length, &string);
+ if (inst) {
+ length = concat(": [", length, &string);
+ length = concat(inst, length, &string);
+ length = concat("] ", length, &string);
+ }
+ else
+ length = concat(": ", length, &string);
+ if (x2->sem == SEM_BOOLEAN)
+ length = showBoolean(x2, i, length, &string);
+ else /* numeric value */
+ length = showNum(x2, i, length, &string);
+ }
+ }
+
+done:
+ /* print string representation */
+ if (string) {
+ fputs(string, f);
+ free(string);
+ }
+}
+
+
+/*
+ * Instantiate format string for each satisfying binding and value
+ * of the current rule ... enumerate and insert %h, %v and %v values
+ *
+ * WARNING: This is not thread safe, it dinks with the format string.
+ */
+size_t /* new length of string */
+formatSatisfyingValue(char *format, size_t length, char **string)
+{
+ char *host;
+ char *inst;
+ char *first;
+ char *prev;
+ char *next;
+ int i;
+ Expr *x1;
+ Expr *x2;
+ int sts1;
+ int sts2;
+
+ /* no formatting present? */
+ if ((sts1 = findFormat(format, &first)) == 0)
+ return concat(format, length, string);
+
+#if PCP_DEBUG
+ if (pmDebug & DBG_TRACE_APPL2) {
+ fprintf(stderr, "formatSatisfyingValue: curr=" PRINTF_P_PFX "%p\n", curr);
+ dumpExpr(curr);
+ }
+#endif
+ x1 = findBindings(curr);
+ x2 = findValues(x1);
+ if (!x1->valid)
+ /*
+ * subexpression for %h, %i and %v is not valid but rule is
+ * true, return string without substitution ... rare case
+ * for <bad-or-not evaluated expr> || <true expr> rule
+ */
+ return concat(format, length, string);
+
+ for (i = 0; i < x1->tspan; i++) {
+ if ((x1->sem == SEM_BOOLEAN && *((char *)x1->smpls[0].ptr + i) == B_TRUE)
+ || (x1->sem != SEM_BOOLEAN && x1->sem != SEM_UNKNOWN)) {
+ prev = format;
+ next = first;
+ sts2 = sts1;
+ lookupHostInst(x1, i, &host, &inst);
+ do {
+ *next = '\0';
+ length = concat(prev, length, string);
+ *next = '%';
+
+ switch (sts2) {
+ case 1:
+ if (host)
+ length = concat(host, length, string);
+ else
+ length = concat("<%h undefined>", length, string);
+ break;
+ case 2:
+ if (inst)
+ length = concat(inst, length, string);
+ else
+ length = concat("<%i undefined>", length, string);
+ break;
+ case 3:
+ if (x2->sem == SEM_BOOLEAN)
+ length = showBoolean(x2, i, length, string);
+ else /* numeric value */
+ length = showNum(x2, i, length, string);
+ break;
+ }
+ prev = next + 2;
+ } while ((sts2 = findFormat(prev, &next)));
+ length = concat(prev, length, string);
+ }
+ }
+
+ return length;
+}
+
+char *
+opStrings(int op)
+{
+ int i;
+ /*
+ * sizing of "eh" is a bit tricky ...
+ * XXXXXXXXX is the number of digits in the largest possible value
+ * for "op", to handle the default "<unknown op %d>" case, but also
+ * "eh" must be long enough to accommodate the longest string from
+ * opstr[i].str ... currently "<action arg node>"
+ */
+ static char *eh = "<unknown op XXXXXXXXX>";
+
+ for (i = 0; i < numopstr; i++) {
+ if (opstr[i].op == op)
+ break;
+ }
+
+ if (i < numopstr)
+ return opstr[i].str;
+ else {
+ sprintf(eh, "<unknown op %d>", op);
+ return eh;
+ }
+}
diff --git a/src/pmie/src/show.h b/src/pmie/src/show.h
new file mode 100644
index 0000000..83341ac
--- /dev/null
+++ b/src/pmie/src/show.h
@@ -0,0 +1,32 @@
+/***********************************************************************
+ * show.h - output syntax and values
+ ***********************************************************************
+ *
+ * Copyright (c) 1995 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#include "dstruct.h"
+
+char *opStrings(int);
+
+void showSyntax(FILE *,Symbol);
+void showSubsyntax(FILE *, Symbol);
+void showValue(FILE *, Expr *);
+void showAnnotatedValue(FILE *, Expr *);
+void showSatisfyingValue(FILE *, Expr *);
+void showTime(FILE *, RealTime);
+void showFullTime(FILE *, RealTime);
+size_t formatSatisfyingValue(char *, size_t, char **);
diff --git a/src/pmie/src/stats.h b/src/pmie/src/stats.h
new file mode 100644
index 0000000..d3d819f
--- /dev/null
+++ b/src/pmie/src/stats.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 1999 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 STATS_H
+#define STATS_H
+
+#include <sys/types.h>
+#include <sys/param.h>
+
+/* subdir nested under PCP_TMP_DIR */
+#define PMIE_SUBDIR "pmie"
+
+/* pmie performance instrumentation */
+typedef struct {
+ char config[MAXPATHLEN+1];
+ char logfile[MAXPATHLEN+1];
+ char defaultfqdn[MAXHOSTNAMELEN+1];
+ float eval_expected; /* pmcd.pmie.eval.expected */
+ unsigned int numrules; /* pmcd.pmie.numrules */
+ unsigned int actions; /* pmcd.pmie.actions */
+ unsigned int eval_true; /* pmcd.pmie.eval.true */
+ unsigned int eval_false; /* pmcd.pmie.eval.false */
+ unsigned int eval_unknown; /* pmcd.pmie.eval.unknown */
+ unsigned int eval_actual; /* pmcd.pmie.eval.actual */
+ unsigned int version;
+} pmiestats_t;
+
+#endif /* STATS_H */
diff --git a/src/pmie/src/stomp.c b/src/pmie/src/stomp.c
new file mode 100644
index 0000000..78a21b8
--- /dev/null
+++ b/src/pmie/src/stomp.c
@@ -0,0 +1,395 @@
+/*
+ * Copyright (c) 2006 Aconex. 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 "pmapi.h"
+#include "impl.h"
+
+static int stomp_connect(const char *hostname, int port);
+static void stomp_disconnect(void);
+
+int stomping;
+char *stompfile;
+extern int verbose;
+
+static int fd = -1;
+static int port = -1;
+static int timeout = 2; /* default 2 sec to timeout JMS server ACKs */
+static char *hostname;
+static char *username;
+static char *passcode;
+static char *topic; /* JMS "topic" for pmie messages */
+static char pmietopic[] = "PMIE"; /* default JMS "topic" for pmie */
+
+static char buffer[4096];
+
+static int stomp_connect(const char *hostname, int port)
+{
+ __pmSockAddr *myaddr;
+ __pmHostEnt *servinfo;
+ void *enumIx;
+ struct timeval tv;
+ struct timeval *ptv;
+ __pmFdSet wfds;
+ int ret;
+ int flags = 0;
+
+ if ((servinfo = __pmGetAddrInfo(hostname)) == NULL)
+ return -1;
+
+ fd = -1;
+ enumIx = NULL;
+ for (myaddr = __pmHostEntGetSockAddr(servinfo, &enumIx);
+ myaddr != NULL;
+ myaddr = __pmHostEntGetSockAddr(servinfo, &enumIx)) {
+ /* Create a socket */
+ if (__pmSockAddrIsInet(myaddr))
+ fd = __pmCreateSocket();
+ else if (__pmSockAddrIsIPv6(myaddr))
+ fd = __pmCreateIPv6Socket();
+ else
+ continue;
+ if (fd < 0) {
+ __pmSockAddrFree(myaddr);
+ continue; /* Try the next address */
+ }
+
+ /* Attempt to connect */
+ flags = __pmConnectTo(fd, myaddr, port);
+ __pmSockAddrFree(myaddr);
+
+ if (flags < 0) {
+ /*
+ * Mark failure in case we fall out the end of the loop
+ * and try next address. fd has been closed in __pmConnectTo().
+ */
+ fd = -1;
+ continue;
+ }
+
+ /* FNDELAY and we're in progress - wait on select */
+ tv.tv_sec = timeout;
+ tv.tv_usec = 0;
+ ptv = (tv.tv_sec || tv.tv_usec) ? &tv : NULL;
+ __pmFD_ZERO(&wfds);
+ __pmFD_SET(fd, &wfds);
+ ret = __pmSelectWrite(fd+1, &wfds, ptv);
+
+ /* Was the connection successful? */
+ if (ret <= 0) {
+ if (oserror() == EINTR)
+ return -2;
+ continue;
+ }
+ ret = __pmConnectCheckError(fd);
+ if (ret == 0)
+ break;
+
+ /* Unsuccessful connection. */
+ __pmCloseSocket(fd);
+ fd = -1;
+ } /* loop over addresses */
+
+ __pmHostEntFree(servinfo);
+
+ if(fd == -1)
+ return -4;
+
+ fd = __pmConnectRestoreFlags(fd, flags);
+ if(fd < 0)
+ return -5;
+
+ return fd;
+}
+
+static int stomp_read_ack(void)
+{
+ struct timeval tv;
+ fd_set fds, readyfds;
+ int nready, sts;
+
+ FD_ZERO(&fds);
+ FD_SET(fd, &fds);
+ tv.tv_sec = timeout;
+ tv.tv_usec = 0;
+ memcpy(&readyfds, &fds, sizeof(readyfds));
+ nready = select(fd + 1, &readyfds, NULL, NULL, &tv);
+ if (nready <= 0) {
+ if (nready == 0)
+ __pmNotifyErr(LOG_ERR, "Timed out waiting for server %s:%d - %s",
+ hostname, port, netstrerror());
+ else
+ __pmNotifyErr(LOG_ERR, "Error waiting for server %s:%d - %s",
+ hostname, port, netstrerror());
+ stomp_disconnect();
+ return -1;
+ }
+
+ do {
+ sts = recv(fd, buffer, sizeof(buffer), 0);
+ if (sts < 0) {
+ __pmNotifyErr(LOG_ERR, "Error recving from server %s:%d - %s",
+ hostname, port, netstrerror());
+ stomp_disconnect();
+ return -1;
+ }
+ /* check for anything else we need to read to clear this ACK */
+ memset(&tv, 0, sizeof(tv));
+ memcpy(&readyfds, &fds, sizeof(readyfds));
+ } while (select(fd + 1, &readyfds, NULL, NULL, &tv) > 0);
+
+ return 0;
+}
+
+static int stomp_write(const char *buffer, int length)
+{
+ int sts;
+
+ do {
+ sts = send(fd, buffer, length, 0);
+ if (sts < 0) {
+ __pmNotifyErr(LOG_ERR, "Write error to JMS server %s:%d - %s",
+ hostname, port, netstrerror());
+ stomp_disconnect();
+ return -1;
+ }
+ else if (sts == 0)
+ break;
+ length -= sts;
+ } while (length > 0);
+
+ return 0;
+}
+
+static int stomp_authenticate(void)
+{
+ int len;
+
+ if (fd < 0)
+ return -1;
+ len = snprintf(buffer, sizeof(buffer),
+ "CONNECT\nlogin:%s\npasscode:%s\n\n", username, passcode);
+ if (stomp_write(buffer, len) < 0)
+ return -1;
+ if (stomp_write("\0\n", 2) < 0)
+ return -1;
+ return 0;
+}
+
+static int stomp_destination(void)
+{
+ int len;
+
+ if (fd < 0)
+ return -1;
+ len = snprintf(buffer, sizeof(buffer),
+ "SUB\ndestination:/topic/%s\n\n", topic);
+ if (stomp_write(buffer, len) < 0)
+ return -1;
+ if (stomp_write("\0\n", 2) < 0)
+ return -1;
+ return 0;
+}
+
+static int stomp_hello(void)
+{
+ int len;
+
+ if (fd < 0)
+ return -1;
+ len = snprintf(buffer, sizeof(buffer), "SEND\ndestination:/topic/%s\n\n"
+ "INFO: PMIE: Established initial connection", topic);
+ if (stomp_write(buffer, len) < 0)
+ return -1;
+ if (stomp_write("\0\n", 2) < 0)
+ return -1;
+ return 0;
+}
+
+static void stomp_disconnect(void)
+{
+ if (fd >= 0)
+ close(fd);
+ fd = -1;
+}
+
+static char *isspace_terminate(char *string)
+{
+ int i = 0;
+
+ while (!isspace((int)string[i++])) /* do nothing */ ;
+ if (i)
+ string[i-1] = '\0';
+ return string;
+}
+
+/*
+ * Parse our stomp configuration file, simple format:
+ * host=<hostname> # JMS server machine
+ * port=<port#> # server port number
+ * username=<user> | user=<user>
+ * passcode=<password> | password=<password>
+ * timeout=<seconds> # optional
+ * topic=<JMStopic> # optional
+ */
+static void stomp_parse(void)
+{
+ char config[MAXPATHLEN+1];
+ FILE *f;
+ int sep = __pmPathSeparator();
+
+ if (stompfile)
+ strncat(config, stompfile, sizeof(config)-1);
+ else
+ snprintf(config, sizeof(config),
+ "%s%c" "config" "%c" "pmie" "%c" "stomp",
+ pmGetConfig("PCP_VAR_DIR"), sep, sep, sep);
+ if ((f = fopen(config, "r")) == NULL) {
+ __pmNotifyErr(LOG_ERR, "Cannot open STOMP configuration file %s: %s",
+ config, osstrerror());
+ exit(1);
+ }
+ while (fgets(buffer, sizeof(buffer), f)) {
+ if (strncmp(buffer, "port=", 5) == 0)
+ port = atoi(isspace_terminate(&buffer[5]));
+ else if (strncmp(buffer, "host=", 5) == 0)
+ hostname = strdup(isspace_terminate(&buffer[5]));
+ else if (strncmp(buffer, "hostname=", 9) == 0)
+ hostname = strdup(isspace_terminate(&buffer[9]));
+ else if (strncmp(buffer, "user=", 5) == 0)
+ username = strdup(isspace_terminate(&buffer[5]));
+ else if (strncmp(buffer, "username=", 9) == 0)
+ username = strdup(isspace_terminate(&buffer[9]));
+ else if (strncmp(buffer, "password=", 9) == 0)
+ passcode = strdup(isspace_terminate(&buffer[9]));
+ else if (strncmp(buffer, "passcode=", 9) == 0)
+ passcode = strdup(isspace_terminate(&buffer[9]));
+ else if (strncmp(buffer, "timeout=", 8) == 0) /* optional */
+ timeout = atoi(isspace_terminate(&buffer[8]));
+ else if (strncmp(buffer, "topic=", 6) == 0) /* optional */
+ topic = strdup(isspace_terminate(&buffer[6]));
+ }
+ fclose(f);
+
+ if (!hostname)
+ __pmNotifyErr(LOG_ERR, "No host in STOMP config file %s", config);
+ if (port == -1)
+ __pmNotifyErr(LOG_ERR, "No port in STOMP config file %s", config);
+ if (!username)
+ __pmNotifyErr(LOG_ERR, "No username in STOMP config file %s", config);
+ if (!passcode)
+ __pmNotifyErr(LOG_ERR, "No passcode in STOMP config file %s", config);
+ if (port == -1 || !hostname || !username || !passcode)
+ exit(1);
+}
+
+/*
+ * Setup the connection to the stomp server, and handle initial protocol
+ * negotiations (sending user/passcode over to the server in particular).
+ * Stomp protocol is clear text... (we don't need no stinkin' security!).
+ * Note: this routine is used for both the initial connection and also for
+ * any subsequent reconnect attempts.
+ */
+void stompInit(void)
+{
+ time_t thistime;
+ static time_t lasttime;
+ static int firsttime = 1;
+
+ if (firsttime) { /* initial connection attempt */
+ stomp_parse();
+ if (!topic)
+ topic = pmietopic;
+ atexit(stomp_disconnect);
+ } else { /* reconnect attempt, if not too soon */
+ time(&thistime);
+ if (thistime < lasttime + 60)
+ goto disconnect;
+ }
+
+ if (verbose)
+ __pmNotifyErr(LOG_INFO, "Connecting to %s, port %d", hostname, port);
+ if (stomp_connect(hostname, port) < 0) {
+ __pmNotifyErr(LOG_ERR, "Could not connect to the message server");
+ goto disconnect;
+ }
+
+ if (verbose)
+ __pmNotifyErr(LOG_INFO, "Connected; sending stomp connect message");
+ if (stomp_authenticate() < 0) {
+ __pmNotifyErr(LOG_ERR, "Could not sent STOMP CONNECT frame to server");
+ goto disconnect;
+ }
+
+ if (verbose)
+ __pmNotifyErr(LOG_INFO, "Sent; waiting for server ACK");
+ if (stomp_read_ack() < 0) {
+ __pmNotifyErr(LOG_ERR, "Could not read STOMP ACK frame.");
+ goto disconnect;
+ }
+
+ if (verbose)
+ __pmNotifyErr(LOG_INFO, "ACK; sending initial PMIE topic and hello");
+ if (stomp_destination() < 0) {
+ __pmNotifyErr(LOG_ERR, "Could not read TOPIC frame.");
+ goto disconnect;
+ }
+ if (stomp_hello() < 0) {
+ __pmNotifyErr(LOG_ERR, "Could not send HELLO frame.");
+ goto disconnect;
+ }
+
+ if (verbose)
+ __pmNotifyErr(LOG_INFO, "Sent; waiting for server ACK");
+ if (stomp_read_ack() < 0) {
+ __pmNotifyErr(LOG_ERR, "Could not read STOMP ACK frame");
+ goto disconnect;
+ }
+
+ if (!firsttime)
+ __pmNotifyErr(LOG_INFO, "Reconnected to STOMP protocol server");
+ else if (verbose)
+ __pmNotifyErr(LOG_INFO, "Initial STOMP protocol setup complete");
+ firsttime = 0;
+ goto finished;
+
+disconnect:
+ stomp_disconnect();
+ if (firsttime)
+ exit(1);
+ /* otherwise, we attempt reconnect on next message firing (>1min) */
+finished:
+ lasttime = thistime;
+}
+
+/*
+ * Send a message to the stomp server.
+ */
+int stompSend(const char *msg)
+{
+ int len;
+
+ if (fd < 0) stompInit(); /* reconnect */
+ if (fd < -1) return -1;
+
+ len = snprintf(buffer, sizeof(buffer),
+ "SEND\ndestination:/topic/%s\n\n", topic);
+ if (stomp_write(buffer, len) < 0)
+ return -1;
+ if (stomp_write(msg, strlen(msg)) < 0)
+ return -1;
+ if (stomp_write("\0\n", 2) < 0)
+ return -1;
+ return 0;
+}
diff --git a/src/pmie/src/stomp.h b/src/pmie/src/stomp.h
new file mode 100644
index 0000000..b015efe
--- /dev/null
+++ b/src/pmie/src/stomp.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2006 Aconex. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/*
+ * Streaming Text Orientated Messaging Protocol implementation
+ * http://stomp.codehaus.org/
+ */
+extern int stomping; /* true if stomp actions present */
+extern char *stompfile; /* stomp config file */
+extern int stompInit(void); /* connect to stomp server */
+extern int stompSend(const char *); /* send to JMS server, via stomp */
diff --git a/src/pmie/src/symbol.c b/src/pmie/src/symbol.c
new file mode 100644
index 0000000..a27bcc4
--- /dev/null
+++ b/src/pmie/src/symbol.c
@@ -0,0 +1,264 @@
+/***********************************************************************
+ * symbol.c - a symbol is an object with a name, a value and a
+ * reference count
+ ***********************************************************************
+ *
+ * Copyright (c) 1995-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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include "symbol.h"
+#include "dstruct.h"
+#include "pmapi.h"
+
+
+/***********************************************************************
+ * constants
+ ***********************************************************************/
+
+/* bucket memory alignment, address mask and size */
+#undef ALIGN
+#define ALIGN 1024
+#undef MASK
+#define MASK (ALIGN - 1)
+#undef BSIZE
+#define BSIZE (ALIGN / sizeof(SymUnion))
+
+
+
+/***********************************************************************
+ * types
+ ***********************************************************************/
+
+typedef SymUnion Bucket[BSIZE];
+
+
+
+/***********************************************************************
+ * local functions
+ ***********************************************************************/
+
+static void
+addBucket(SymUnion *st)
+{
+ SymUnion *bckt = (SymUnion *) aalloc(ALIGN, sizeof(Bucket));
+ SymUnion *scoop = bckt + 1;
+
+ bckt->hdr.prev = st;
+ bckt->hdr.next = st->hdr.next;
+ bckt->hdr.free = scoop;
+ st->hdr.next->hdr.prev = bckt;
+ st->hdr.next = bckt;
+ scoop->entry.refs = 0;
+ scoop->entry.stat.free.ptr = NULL;
+ scoop->entry.stat.free.count = BSIZE - 1;
+}
+
+static void
+remBucket(SymUnion *bckt)
+{
+ SymUnion *prev = bckt->hdr.prev;
+ SymUnion *next = bckt->hdr.next;
+ SymUnion *scan;
+ int i = 1;
+
+ prev->hdr.next = next;
+ next->hdr.prev = prev;
+ while (i < BSIZE) {
+ scan = bckt + i;
+ if (scan->entry.refs == 0)
+ i += scan->entry.stat.free.count;
+ else {
+ free(scan->entry.stat.used.name);
+ i++;
+ }
+ }
+ free(bckt);
+}
+
+
+
+/***********************************************************************
+ * exported functions
+ ***********************************************************************/
+
+/* initialize symbol table */
+void
+symSetTable(SymbolTable *st)
+{
+ /* start with one clean bucket */
+ st->hdr.next = st;
+ st->hdr.prev = st;
+ addBucket(st);
+}
+
+
+/* reset symbol table */
+void
+symClearTable(SymbolTable *st)
+{
+ /* unchain all buckets and free storage */
+ while (st->hdr.next != st)
+ remBucket(st->hdr.next);
+ /* start with one clean bucket */
+ addBucket(st);
+}
+
+
+/* Convert string to symbol. A copy of the name string is made
+ on the heap for use by the symbol. */
+Symbol
+symIntern(SymbolTable *st, char *name)
+{
+ SymUnion *bckt;
+ SymUnion *scoop = NULL;
+ char *copy;
+ int i;
+
+ /* pick up existing symbol */
+ bckt = st->hdr.next;
+ while (bckt != st) {
+ i = 1;
+ while (i < BSIZE) {
+ scoop = bckt + i;
+ if (scoop->entry.refs) {
+ if (strcmp(name, scoop->entry.stat.used.name) == 0) {
+ scoop->entry.refs++;
+ return scoop;
+ }
+ i++;
+ }
+ else
+ i += scoop->entry.stat.free.count;
+ }
+ bckt = bckt->hdr.next;
+ }
+
+ /* pick up free entry */
+ bckt = st->hdr.next;
+ while (bckt != st) {
+ if ((scoop = bckt->hdr.free)) {
+ if (scoop->entry.stat.free.count > 1)
+ scoop += --scoop->entry.stat.free.count;
+ else
+ bckt->hdr.free = scoop->entry.stat.free.ptr;
+ break;
+ }
+ bckt = bckt->hdr.next;
+ }
+
+ /* no free entry - allocate new bucket */
+ if (scoop == NULL) {
+ addBucket(st);
+ scoop = st->hdr.next + 1;
+ scoop += --scoop->entry.stat.free.count;
+ }
+
+ /* initialize symbol */
+ scoop->entry.refs = 1;
+ copy = (char *) alloc(strlen(name) + 1);
+ strcpy(copy, name);
+ scoop->entry.stat.used.name = copy;
+ scoop->entry.stat.used.value = NULL;
+ return scoop;
+}
+
+
+/* lookup symbol by name */
+Symbol
+symLookup(SymbolTable *st, char *name)
+{
+ SymUnion *bckt;
+ SymUnion *scoop;
+ int i;
+
+ bckt = st->hdr.next;
+ while (bckt != st) {
+ i = 1;
+ while (i < BSIZE) {
+ scoop = bckt + i;
+ if (scoop->entry.refs) {
+ if (strcmp(name, scoop->entry.stat.used.name) == 0) {
+ scoop->entry.refs++;
+ return scoop;
+ }
+ i++;
+ }
+ else
+ i += scoop->entry.stat.free.count;
+ }
+ bckt = bckt->hdr.next;
+ }
+ return NULL;
+}
+
+
+/* copy symbol */
+Symbol
+symCopy(Symbol sym)
+{
+ sym->entry.refs++;
+ return sym;
+}
+
+
+/* remove reference to symbol */
+void
+symFree(Symbol sym)
+{
+ SymUnion *bckt;
+ SymUnion *lead, *lag;
+
+ if ((sym != SYM_NULL) && (--sym->entry.refs <= 0)) {
+
+ /* free up name string BUT NOT value */
+ free(sym->entry.stat.used.name);
+
+ /* find correct place in ordered free list */
+ bckt = (SymUnion *) ((char *) sym - ((long) sym & MASK));
+ lead = bckt->hdr.free;
+ lag = NULL;
+ while ((lead != NULL) && (lead < sym)) {
+ lag = lead;
+ lead = lead->entry.stat.free.ptr;
+ }
+
+ if (lag != NULL && (lag + lag->entry.stat.free.count) == sym) {
+ /* coalesce with preceding free block */
+ lag->entry.stat.free.count++;
+ sym = lag;
+ }
+ else {
+ /* link up as single free entry */
+ if (lag)
+ lag->entry.stat.free.ptr = sym;
+ else
+ bckt->hdr.free = sym;
+ sym->entry.stat.free.count = 1;
+ sym->entry.stat.free.ptr = lead;
+ }
+
+ if (sym + sym->entry.stat.free.count == lead) {
+ /* coalesce with following free block */
+ sym->entry.stat.free.count += lead->entry.stat.free.count;
+ sym->entry.stat.free.ptr = lead->entry.stat.free.ptr;
+ }
+ }
+}
+
+
diff --git a/src/pmie/src/symbol.h b/src/pmie/src/symbol.h
new file mode 100644
index 0000000..07e222a
--- /dev/null
+++ b/src/pmie/src/symbol.h
@@ -0,0 +1,93 @@
+/***********************************************************************
+ * symbol.h - a symbol is an object with a name, a value and a
+ * reference count
+ ***********************************************************************
+ *
+ * Copyright (c) 1995 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#ifndef SYMBOL_H
+#define SYMBOL_H
+
+/***********************************************************************
+ * private
+ ***********************************************************************/
+
+union symunion; /* for forward reference */
+
+typedef struct {
+ union symunion *next; /* forward pointer */
+ union symunion *prev; /* backward pointer */
+ union symunion *free; /* free list head */
+} SymHdr;
+
+typedef struct {
+ union symunion *ptr; /* free list pointer */
+ int count; /* number of free entries */
+} SymFree;
+
+typedef struct {
+ char *name; /* name string */
+ void *value; /* arbitrary value */
+} SymUsed;
+
+typedef struct {
+ union {
+ SymFree free; /* free symbol table entry */
+ SymUsed used; /* occupied symbol table entry */
+ } stat;
+ int refs; /* refernce count */
+} SymEntry;
+
+typedef union symunion {
+ SymHdr hdr; /* symbol table or bucket header */
+ SymEntry entry; /* symbol or free slot */
+} SymUnion;
+
+
+/***********************************************************************
+ * public
+ ***********************************************************************/
+
+#define SYM_NULL NULL
+typedef SymUnion *Symbol;
+typedef SymUnion SymbolTable;
+
+/* access to name string, value and reference count */
+#define symName(sym) ((sym)->entry.stat.used.name)
+#define symValue(sym) ((sym)->entry.stat.used.value)
+#define symRefs(sym) ((sym)->entry.refs)
+
+/* initialize symbol table */
+void symSetTable(SymbolTable *);
+
+/* reset symbol table */
+void symClearTable(SymbolTable *);
+
+/* convert string to symbol */
+Symbol symIntern(SymbolTable *, char *);
+
+/* lookup symbol by name */
+Symbol symLookup(SymbolTable *, char *);
+
+/* copy symbol */
+Symbol symCopy(Symbol);
+
+/* remove reference to symbol */
+void symFree(Symbol);
+
+#endif /* SYMBOL_H */
+
diff --git a/src/pmie/src/syntax.c b/src/pmie/src/syntax.c
new file mode 100644
index 0000000..46edc27
--- /dev/null
+++ b/src/pmie/src/syntax.c
@@ -0,0 +1,781 @@
+/***********************************************************************
+ * syntax.c - inference rule language parser
+ ***********************************************************************
+ *
+ * Copyright (c) 1995 Silicon Graphics, Inc. All Rights Reserved.
+ * Copyright (c) 2013 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#include "pmapi.h"
+#include "impl.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#ifdef HAVE_REGEX_H
+#include <regex.h>
+#endif
+#include "dstruct.h"
+#include "symbol.h"
+#include "syntax.h"
+#include "lexicon.h"
+#include "grammar.h"
+#include "pragmatics.h"
+#include "eval.h"
+#include "show.h"
+
+
+
+/***********************************************************************
+ * constants
+ ***********************************************************************/
+
+#define NAMEGEN_MAX 12
+
+
+
+/***********************************************************************
+ * variables
+ ***********************************************************************/
+
+Symbol parse; /* result of parse */
+int errs; /* error count */
+
+
+/***********************************************************************
+ * miscellaneous local functions
+ ***********************************************************************/
+
+/* generate unique name, only good till next call */
+static char *
+nameGen(void)
+{
+ static int state = 0;
+ static char bfr[NAMEGEN_MAX];
+
+ state++;
+ snprintf(bfr, sizeof(bfr), "expr_%1d", state);
+
+ return bfr;
+}
+
+
+/* check domains cardinality agreement */
+static int
+checkDoms(Expr *x1, Expr *x2)
+{
+ if ((x1->nvals != 1) && (x2->nvals != 1)) {
+ if (x1->hdom != x2->hdom) {
+ synerr();
+ fprintf(stderr, "host domains have different size (%d and %d)\n",
+ x1->hdom, x2->hdom);
+ return 0;
+ }
+ if (x1->e_idom != x2->e_idom) {
+ synerr();
+ fprintf(stderr, "instance domains have different size (%d and %d)\n",
+ x1->e_idom, x2->e_idom);
+ return 0;
+ }
+ if (x1->tdom != x2->tdom) {
+ synerr();
+ fprintf(stderr, "time domains have different size (%d and %d)\n",
+ x1->tdom, x2->tdom);
+ return 0;
+ }
+ }
+ return 1;
+}
+
+
+/* evaluate constant expression */
+static void
+evalConst(Expr *x)
+{
+ if ((x->op < ACT_SHELL) &&
+ (x->arg1->op == NOP) &&
+ ((x->arg2 == NULL) || (x->arg2->op == NOP))) {
+ (x->eval)(x);
+ x->op = NOP;
+ x->eval = NULL;
+ freeExpr(x->arg1); x->arg1 = NULL;
+ freeExpr(x->arg2); x->arg2 = NULL;
+ }
+}
+
+
+
+/***********************************************************************
+ * error reporting
+ ***********************************************************************/
+
+static void
+report(char *msg)
+{
+ LexIn *x = lin;
+
+ fprintf(stderr, "%s: %s - ", pmProgname, msg);
+ if (x) {
+ fprintf(stderr, "near line %d of ", x->lno);
+ if (x->stream) {
+ if (x->name) fprintf(stderr, "file %s", x->name);
+ else fprintf(stderr, "standard input");
+ }
+ else {
+ fprintf(stderr, "macro $%s", x->name);
+ do x = x->prev;
+ while (x && x->stream == NULL);
+ if (x) {
+ fprintf(stderr, " called from ");
+ if (x->name) fprintf(stderr, "file %s", x->name);
+ else fprintf(stderr, "standard input");
+ }
+ }
+ }
+ else
+ fprintf(stderr, "at end of file");
+ putc('\n', stderr);
+}
+
+void
+synwarn(void)
+{
+ report("warning");
+}
+
+void
+synerr(void)
+{
+ report("syntax error");
+ errs++;
+}
+
+void
+yyerror(char *s)
+{
+ synerr();
+}
+
+
+/***********************************************************************
+ * post processing
+ * - propagate instance information from fetch expressions towards
+ * the root of the expression tree, allocating ring buffers etc.
+ * along the way.
+ ***********************************************************************/
+
+static void
+postExpr(Expr *x)
+{
+ if (x->op == CND_FETCH) {
+ instFetchExpr(x);
+ }
+ else {
+ if (x->arg1) {
+ postExpr(x->arg1);
+ if (x->arg2)
+ postExpr(x->arg2);
+ }
+ }
+}
+
+
+
+/***********************************************************************
+ * parser actions
+ ***********************************************************************/
+
+/* statement */
+Symbol
+statement(char *name, Expr *x)
+{
+ Symbol s;
+
+ /* error guard */
+ if (x == NULL) return NULL;
+
+ /* if name not given, make one up */
+ if (name == NULL) name = nameGen();
+
+ /* the parsed object is a rule (expression to evaluate) */
+ if (x->op != NOP) {
+ if (symLookup(&rules, name)) {
+ synerr();
+ fprintf(stderr, "rule \"%s\" multiply defined\n", name);
+ freeExpr(x);
+ return NULL;
+ }
+ else {
+ if (errs == 0) {
+ postExpr(x);
+ s = symIntern(&rules, name);
+ }
+ else return NULL;
+ }
+ }
+
+ /* the parsed object is a non-rule */
+ else {
+ if ( (s = symLookup(&vars, name)) )
+ freeExpr(symValue(s));
+ else
+ s = symIntern(&vars, name);
+ }
+
+ symValue(s) = x;
+ return s;
+}
+
+
+/* construct rule Expr */
+Expr *
+ruleExpr(Expr *cond, Expr *act)
+{
+ Expr *x;
+
+ if (cond == NULL) return act;
+ if (act == NULL) return cond;
+
+ if (cond->nvals != 1) {
+ synerr();
+ fprintf(stderr, "rule condition must have a single boolean value\n");
+ }
+
+ perf->numrules++;
+
+ x = newExpr(RULE, cond, act, -1, -1, -1, 1, SEM_BOOLEAN);
+ newRingBfr(x);
+ findEval(x);
+ return x;
+}
+
+
+/* action expression */
+Expr *
+actExpr(int op, Expr *arg1, Expr *arg2)
+{
+ Expr *x;
+
+ /* error guard */
+ if (arg1 == NULL) return NULL;
+
+ /* construct expression node */
+ x = newExpr(op, arg1, arg2, -1, -1, -1, 1, SEM_BOOLEAN);
+ newRingBfr(x);
+ findEval(x);
+
+ return x;
+}
+
+
+/* action argument expression */
+Expr *
+actArgExpr(Expr *arg1, Expr *arg2)
+{
+ Expr *x;
+
+ /* error guard */
+ if (arg1 == NULL) return NULL;
+
+ /* construct expression node */
+ x = newExpr(ACT_ARG, arg1, arg2, -1, 1, -1, 1, SEM_CHAR);
+ newRingBfr(x);
+ findEval(x);
+
+ return x;
+}
+
+/* action argument */
+Expr *
+actArgList(Expr *arg1, char *str)
+{
+ Expr *x;
+
+ /* construct expression node for an action argument string */
+ x = (Expr *) zalloc(sizeof(Expr));
+ x->op = NOP;
+ x->smpls[0].ptr = x->ring = sdup(str);
+ x->valid = x->nsmpls = x->nvals = 1;
+ x->tspan = strlen(str);
+ x->sem = SEM_CHAR;
+ if (arg1) {
+ x->arg1 = arg1;
+ arg1->parent = x;
+ }
+
+ return x;
+}
+
+/* relational operator expression */
+Expr *
+relExpr(int op, Expr *arg1, Expr *arg2)
+{
+ Expr *x;
+ Expr *arg;
+ int i;
+ int sts;
+
+ /* error guard */
+ if (arg1 == NULL || arg2 == NULL) return NULL;
+
+ /* check domains */
+ sts = checkDoms(arg1, arg2);
+
+ /* decide primary argument for inheritance of Expr attributes */
+ arg = primary(arg1, arg2);
+
+ /* construct expression node */
+ x = newExpr(op, arg1, arg2, arg->hdom, arg->e_idom, arg->tdom, abs(arg->tdom), SEM_BOOLEAN);
+#if PCP_DEBUG
+ if (sts == 0 && (pmDebug & DBG_TRACE_APPL1)) {
+ fprintf(stderr, "relExpr: checkDoms(" PRINTF_P_PFX "%p, " PRINTF_P_PFX "%p) failed ...\n", arg1, arg2);
+ __dumpTree(1, x);
+ }
+#endif
+ newRingBfr(x);
+ if (x->tspan > 0) {
+ for (i = 0; i < x->nsmpls; i++)
+ *((char *)x->ring + i) = B_UNKNOWN;
+ }
+ findEval(x);
+
+ /* evaluate constant expression now */
+ evalConst(x);
+
+ return x;
+}
+
+
+/* binary operator expression */
+Expr *
+binaryExpr(int op, Expr *arg1, Expr *arg2)
+{
+ Expr *x;
+ Expr *arg = arg1;
+ int sts = 0;
+
+ /* error guard */
+ if (arg1 == NULL) return NULL;
+
+ if (arg1 != NULL && arg2 != NULL) {
+ if (op != CND_MATCH && op != CND_NOMATCH) {
+ /* check domains */
+ sts = checkDoms(arg1, arg2);
+
+ /* decide primary argument for inheritance of Expr attributes */
+ arg = primary(arg1, arg2);
+ }
+ else {
+ regex_t *pat;
+
+ pat = alloc(sizeof(*pat));
+ if (regcomp(pat, (char *)arg2->ring, REG_EXTENDED|REG_NOSUB) != 0) {
+ /* bad pattern */
+ fprintf(stderr, "illegal regular expression \"%s\"\n", (char *)arg2->ring);
+ free(pat);
+ return NULL;
+ }
+#if PCP_DEBUG
+ if (pmDebug & DBG_TRACE_APPL1) {
+ fprintf(stderr, "binaryExpr: regex=\"%s\" handle=" PRINTF_P_PFX "%p\n", (char *)arg2->ring, pat);
+ }
+#endif
+ /*
+ * change operand from the string form of the pattern to the
+ * compiled regex
+ */
+ free(arg2->ring);
+ arg2->tspan = 1;
+ arg2->ring = pat;
+ arg2->sem = SEM_REGEX;
+ sts = 1;
+ }
+ }
+
+ /* construct expression node */
+ x = newExpr(op, arg1, arg2, arg->hdom, arg->e_idom, arg->tdom, abs(arg->tdom), arg->sem);
+#if PCP_DEBUG
+ if (sts == 0 && (pmDebug & DBG_TRACE_APPL1)) {
+ fprintf(stderr, "binaryExpr: checkDoms(" PRINTF_P_PFX "%p, " PRINTF_P_PFX "%p) failed ...\n", arg1, arg2);
+ __dumpTree(1, x);
+ }
+#endif
+ newRingBfr(x);
+ findEval(x);
+
+ /* evaluate constant expression now */
+ evalConst(x);
+
+ return x;
+}
+
+
+/* unary operator expression */
+Expr *
+unaryExpr(int op, Expr *arg)
+{
+ Expr *x;
+
+ /* error guard */
+ if (arg == NULL) return NULL;
+
+ /* construct expression node */
+ x = newExpr(op, arg, NULL, arg->hdom, arg->e_idom, arg->tdom, abs(arg->tdom), arg->sem);
+ newRingBfr(x);
+ findEval(x);
+
+ /* evaluate constant expression now */
+ evalConst(x);
+
+ return x;
+}
+
+
+/* aggregation/quantification operator expression */
+Expr *
+domainExpr(int op, int dom, Expr *arg)
+{
+ Expr *x;
+ int hdom;
+ int idom;
+ int tdom;
+
+ /* error guard */
+ if (arg == NULL) return NULL;
+
+ hdom = arg->hdom;
+ idom = arg->e_idom;
+ tdom = arg->tdom;
+
+ switch (dom) {
+ case HOST_DOM:
+ if (hdom == -1) {
+ synerr();
+ fprintf(stderr, "no host domain\n");
+ freeExpr(arg);
+ return NULL;
+ }
+ hdom = -1;
+ idom = -1;
+ dom = 0;
+ break;
+ case INST_DOM:
+#if 0
+ /*
+ * I believe this test is no longer correct ... the instance
+ * domain may be unobtainable at this point, or may change
+ * later so checking at the syntactic level is not helpful
+ */
+ if (idom == -1) {
+ synerr();
+ fprintf(stderr, "no instance domain\n");
+ freeExpr(arg);
+ return NULL;
+ }
+#endif
+ idom = -1;
+ dom = 1;
+ break;
+ case TIME_DOM:
+ if (tdom == -1) {
+ synerr();
+ fprintf(stderr, "no time domain\n");
+ freeExpr(arg);
+ return NULL;
+ }
+ tdom = -1;
+ dom = 2;
+ }
+
+ if (op == CND_COUNT_HOST) {
+ x = newExpr(op + dom, arg, NULL, hdom, idom, tdom, abs(tdom), PM_SEM_INSTANT);
+ newRingBfr(x);
+ x->units = countUnits;
+ }
+ else {
+ x = newExpr(op + dom, arg, NULL, hdom, idom, tdom, abs(tdom), arg->sem);
+ newRingBfr(x);
+ }
+
+ findEval(x);
+ return x;
+}
+
+
+/* percentage quantifier */
+Expr *
+percentExpr(double pcnt, int dom, Expr *arg)
+{
+ Expr *x;
+
+ /* error guard */
+ if (arg == NULL) return NULL;
+
+ x = domainExpr(CND_PCNT_HOST, dom, arg);
+ x->arg2 = newExpr(NOP, NULL, NULL, -1, -1, -1, 1, SEM_NUMCONST);
+ newRingBfr(x->arg2);
+ *(double *)x->arg2->ring = pcnt / 100.0;
+ x->arg2->valid = 1;
+ return x;
+}
+
+
+/* merge expression */
+static Expr *
+mergeExpr(int op, Expr *arg)
+{
+ Expr *x;
+
+ /* force argument to buffer at least two samples */
+ if (arg->nsmpls < 2)
+ changeSmpls(&arg, 2);
+
+ /* construct expression node */
+ x = newExpr(op, arg, NULL, arg->hdom, arg->e_idom, arg->tdom, abs(arg->tdom), arg->sem);
+ newRingBfr(x);
+ findEval(x);
+ return x;
+}
+
+
+/* numeric merge expression */
+Expr *
+numMergeExpr(int op, Expr *arg)
+{
+ /* error guard */
+ if (arg == NULL) return NULL;
+
+ /* check argument semantics */
+ if ((arg->sem == SEM_BOOLEAN) || (arg->sem == SEM_CHAR)) {
+ synerr();
+ fprintf(stderr, "operator \"%s\" requires numeric valued argument\n", opStrings(op));
+ return NULL;
+ }
+
+ return mergeExpr(op, arg);
+}
+
+
+/* boolean merge expression */
+Expr *
+boolMergeExpr(int op, Expr *arg)
+{
+ /* error guard */
+ if (arg == NULL) return NULL;
+
+ /* check argument semantics */
+ if (arg->sem != SEM_BOOLEAN) {
+ synerr();
+ fprintf(stderr, "operator \"%s\" requires boolean valued argument\n", opStrings(op));
+ return NULL;
+ }
+
+ return mergeExpr(op, arg);
+}
+
+
+/* fetch expression */
+Expr *
+fetchExpr(char *mname,
+ StringArray hnames,
+ StringArray inames,
+ Interval times)
+{
+ Expr *x;
+ Metric *marr, *m;
+ int fsz, dsz;
+ int sum;
+ int i;
+
+ /* calculate samplecounts for fetch and delay */
+ if (times.t1 == 0) {
+ fsz = times.t2 - times.t1 + 1;
+ dsz = 0;
+ }
+ else {
+ fsz = times.t1 + 1;
+ dsz = times.t2 - times.t1 + 1;
+ }
+
+ /* default host */
+ if (hnames.n == 0) {
+ hnames.n = 1;
+ }
+
+ /* construct Metrics array */
+ marr = m = (Metric *) zalloc(hnames.n * sizeof(Metric));
+ sum = 0;
+ for (i = 0; i < hnames.n; i++) {
+ m->mname = symIntern(&metrics, mname);
+ if (hnames.ss) m->hname = symIntern(&hosts, hnames.ss[i]);
+ else m->hname = symIntern(&hosts, dfltHostName);
+ m->desc.sem = SEM_UNKNOWN;
+ m->m_idom = -1;
+ if (inames.n > 0) {
+ m->specinst = inames.n;
+ m->iids = alloc(inames.n * sizeof(int));
+ m->inames = inames.ss;
+ }
+ else {
+ m->specinst = 0;
+ m->iids = NULL;
+ m->inames = NULL;
+ }
+ if (errs == 0) {
+ int sts = initMetric(m);
+ if (sts < 0) errs++;
+ if (m->m_idom > 0)
+ sum += m->m_idom;
+ }
+ m++;
+ }
+ if (sum == 0)
+ sum = -1;
+
+ /* error exit */
+ if (errs) {
+ m = marr;
+ for (i = 0; i < hnames.n; i++) {
+ if (m->iids) free(m->iids);
+ m++;
+ }
+ free(marr);
+ return NULL;
+ }
+
+ /* construct fetch node */
+ x = newExpr(CND_FETCH, NULL, NULL, hnames.n, sum, fsz, fsz, SEM_UNKNOWN);
+ newRingBfr(x);
+ x->metrics = marr;
+ findEval(x);
+ instFetchExpr(x);
+
+ /* patch in fetch node reference in each Metric */
+ m = marr;
+ for (i = 0; i < hnames.n; i++) {
+ m->expr = x;
+ m++;
+ }
+
+ /* construct delay node */
+ if (dsz) {
+ x = newExpr(CND_DELAY, x, NULL, x->hdom, x->e_idom, dsz, dsz, SEM_UNKNOWN);
+ newRingBfr(x);
+ findEval(x);
+ }
+ return x;
+}
+
+
+/* numeric constant */
+Expr *
+numConst(double v, pmUnits u)
+{
+ Expr *x;
+
+ x = newExpr(NOP, NULL, NULL, -1, -1, -1, 1, SEM_NUMCONST);
+ newRingBfr(x);
+ x->units = canon(u);
+ x->valid = 1;
+ *(double *) x->ring = scale(u) * v;
+ return x;
+}
+
+
+/* string constant */
+Expr *
+strConst(char *s)
+{
+ Expr *x;
+ int n = (int) strlen(s) + 1;
+
+ x = newExpr(NOP, NULL, NULL, -1, -1, -1, 1, SEM_CHAR);
+ x->valid = 1;
+ x->tspan = n;
+ newRingBfr(x);
+ strcpy((char *)x->ring, s);
+ return x;
+}
+
+
+/* boolean constant */
+Expr *
+boolConst(Boolean v)
+{
+ Expr *x;
+
+ x = newExpr(NOP, NULL, NULL, -1, -1, -1, 1, SEM_BOOLEAN);
+ newRingBfr(x);
+ x->valid = 1;
+ *(Boolean *) x->ring = v;
+ return x;
+}
+
+
+/* numeric valued variable */
+Expr *
+numVar(Expr *x)
+{
+ if (x->sem == SEM_BOOLEAN || x->sem == SEM_CHAR) {
+ synerr();
+ fprintf(stderr, "numeric valued variable expected\n");
+ return NULL;
+ }
+ return x;
+}
+
+
+/* truth valued variable */
+Expr *
+boolVar(Expr *x)
+{
+ if (x->sem == SEM_CHAR) {
+ synerr();
+ fprintf(stderr, "truth valued variable expected\n");
+ return NULL;
+ }
+ return x;
+}
+
+
+/***********************************************************************
+ * parser
+ ***********************************************************************/
+
+/* Initialization to be called at the start of new input file. */
+int
+synInit(char *fname)
+{
+ return lexInit(fname);
+}
+
+
+/* parse single statement */
+Symbol
+syntax(void)
+{
+ while (lexMore()) {
+ errs = 0;
+ parse = NULL;
+ yyparse();
+ if (parse)
+ return parse;
+ }
+ lexFinal();
+ return NULL;
+}
+
+
+
diff --git a/src/pmie/src/syntax.h b/src/pmie/src/syntax.h
new file mode 100644
index 0000000..f01228f
--- /dev/null
+++ b/src/pmie/src/syntax.h
@@ -0,0 +1,97 @@
+/***********************************************************************
+ * syntax.h - inference rule language parser
+ ***********************************************************************
+ *
+ * Copyright (c) 1995 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#ifndef SYNTAX_H
+#define SYNTAX_H
+
+
+/***********************************************************************
+ * ONLY FOR USE BY: lexicon.c grammar.y
+ ***********************************************************************/
+
+typedef struct {
+ int n; /* number of elements */
+ char **ss; /* dynamically allocated array */
+} StringArray;
+
+typedef struct {
+ int t1; /* start of interval */
+ int t2; /* end of interval */
+} Interval;
+
+/* parser stack entry */
+typedef union {
+ int i;
+ char *s;
+ double d;
+ StringArray sa;
+ Interval t;
+ pmUnits u;
+ Metric *m;
+ Expr *x;
+ Symbol *z;
+} YYSTYPE;
+#define YYSTYPE_IS_DECLARED 1
+
+extern YYSTYPE yylval;
+
+/* error reporting */
+extern int errs; /* error count */
+void yyerror(char *);
+void synerr(void);
+void synwarn(void);
+
+/* parser actions */
+Symbol statement(char *, Expr *);
+Expr *ruleExpr(Expr *, Expr *);
+Expr *relExpr(int, Expr *, Expr *);
+Expr *binaryExpr(int, Expr *, Expr *);
+Expr *unaryExpr(int, Expr *);
+Expr *domainExpr(int, int, Expr *);
+Expr *percentExpr(double, int, Expr *);
+Expr *numMergeExpr(int, Expr *);
+Expr *boolMergeExpr(int, Expr *);
+Expr *fetchExpr(char *, StringArray, StringArray, Interval);
+Expr *numConst(double, pmUnits);
+Expr *strConst(char *);
+Expr *boolConst(Boolean);
+Expr *numVar(Expr *);
+Expr *boolVar(Expr *);
+Expr *actExpr(int, Expr *, Expr *);
+Expr *actArgExpr(Expr *, Expr *);
+Expr *actArgList(Expr *, char *);
+
+/* parse tree */
+extern Symbol parse;
+
+
+
+/***********************************************************************
+ * public
+ ***********************************************************************/
+
+/* Initialization to be called at the start of new input file. */
+int synInit(char *);
+
+/* parse single statement */
+Symbol syntax(void);
+
+#endif /* SYNTAX_H */
+
diff --git a/src/pmie/src/systemlog.c b/src/pmie/src/systemlog.c
new file mode 100644
index 0000000..a493841
--- /dev/null
+++ b/src/pmie/src/systemlog.c
@@ -0,0 +1,198 @@
+/*
+ * Copyright (c) 1999-2004 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#include <ctype.h>
+#define SYSLOG_NAMES
+#include "dstruct.h"
+#include "eval.h"
+#include "syntax.h"
+#include "systemlog.h"
+#include "pmapi.h"
+
+#if defined(IS_SOLARIS) || defined(IS_AIX) || defined(IS_MINGW)
+#include "logger.h"
+#endif
+
+/*
+ * based on source for logger(1)
+ */
+static int
+decode(char *name, CODE *codetab)
+{
+ CODE *c;
+
+ if (isdigit((int)*name))
+ return (atoi(name));
+
+ for (c = codetab; c->c_name; c++)
+ if (!strcasecmp(name, c->c_name))
+ return (c->c_val);
+
+ return (-1);
+}
+
+/*
+ * Decode a symbolic name to a numeric value
+ * ... based on source for logger(1)
+ */
+static int
+pencode(char *s)
+{
+ char *save;
+ int fac, lev;
+
+ save = s;
+ while (*s && *s != '.') ++s;
+ if (*s) {
+ *s = '\0';
+ fac = decode(save, facilitynames);
+ if (fac < 0) {
+ synwarn();
+ fprintf(stderr, "Ignoring unknown facility (%s) for -p in syslog action\n", save);
+ fac = 0;
+ }
+ s++;
+ }
+ else {
+ fac = 0;
+ s = save;
+ }
+ lev = decode(s, prioritynames);
+ if (lev < 0) {
+ synwarn();
+ fprintf(stderr, "Ignoring unknown priority (%s) for -p in syslog action\n", s);
+ lev = LOG_NOTICE;
+ }
+ return ((lev & LOG_PRIMASK) | (fac & LOG_FACMASK));
+}
+
+/*
+ * handle splitting of -t tag and -p prio across one or two
+ * arguments to the syslog action in the rule
+ */
+static void
+nextch(char **p, Expr **x)
+{
+ static char end = '\0';
+ (*p)++;
+ while (**p == '\0') {
+ if ((*x)->arg1 == NULL) {
+ *p = &end;
+ return;
+ }
+ *x = (*x)->arg1;
+ *p = (*x)->ring;
+ }
+}
+
+/*
+ * post-process the expression tree for a syslog action to gather
+ * any -t tag or -p pri options (as for logger(1)) and build the
+ * encoded equivalent as a new expression accessed via arg2 from
+ * the head of the arguments list
+ */
+void
+do_syslog_args(Expr *act)
+{
+ int pri = -1;
+ char *tag = NULL;
+ char *p;
+ char *q;
+ Expr *others;
+ Expr *tmp;
+ Expr *new;
+
+ /*
+ * scan for -p pri and -t tag
+ */
+ for (others = act->arg1; others != NULL; ) {
+ if (others->ring == NULL) break;
+ p = others->ring;
+ if (*p != '-') break;
+ nextch(&p, &others);
+ if (*p == 'p' && pri == -1) {
+ nextch(&p, &others);
+ while (*p && isspace((int)*p)) nextch(&p, &others);
+ if (*p == '\0') {
+ synwarn();
+ fprintf(stderr, "Missing [facility.]priority after -p in syslog action\n");
+ }
+ else {
+ q = p+1;
+ while (*q && !isspace((int)*q)) q++;
+ if (*q) {
+ synwarn();
+ fprintf(stderr, "Ignoring extra text (%s) after -p pri in syslog action\n", q);
+ *q = '\0';
+ }
+ pri = pencode(p);
+ }
+ }
+ else if (*p == 't' && tag == NULL) {
+ nextch(&p, &others);
+ while (*p && isspace((int)*p)) nextch(&p, &others);
+ if (*p == '\0') {
+ synwarn();
+ fprintf(stderr, "Missing tag after -t in syslog action\n");
+ }
+ else {
+ q = p+1;
+ while (*q && !isspace((int)*q)) q++;
+ if (*q) {
+ synwarn();
+ fprintf(stderr, "Ignoring extra text (%s) after -t tag in syslog action\n", q);
+ *q = '\0';
+ }
+ tag = p;
+ }
+ }
+ else
+ break;
+ others = others->arg1;
+ }
+
+ /* defaults if -t and/or -p not seen */
+ if (pri < 0) pri = LOG_NOTICE;
+ if (tag == NULL) tag = "pcp-pmie";
+
+ /*
+ * construct new arg2 argument node, with
+ * ring -> pri (int) and tag (char *) concatenated
+ */
+ new = (Expr *) zalloc(sizeof(Expr));
+ new->op = NOP;
+ new->ring = (char *)alloc(sizeof(int)+strlen(tag)+1);
+ *((int *)new->ring) = pri;
+ strcpy(&((char *)new->ring)[sizeof(int)], tag);
+ act->arg2 = new;
+ new->parent = act;
+
+ /* free old argument nodes used for -p and/or -t specifications */
+ for (tmp = act->arg1; tmp != others; ) {
+ if (tmp->ring) {
+ free(tmp->ring);
+ }
+ new = tmp->arg1;
+ free(tmp);
+ tmp = new;
+ }
+
+ /* re-link remaining argument nodes */
+ if (others != act->arg1) {
+ act->arg1 = others;
+ if (others != NULL)
+ others->parent = act;
+ }
+
+}
diff --git a/src/pmie/src/systemlog.h b/src/pmie/src/systemlog.h
new file mode 100644
index 0000000..fba5151
--- /dev/null
+++ b/src/pmie/src/systemlog.h
@@ -0,0 +1 @@
+void do_syslog_args(Expr *);
diff --git a/src/pmie/src/unary.sk b/src/pmie/src/unary.sk
new file mode 100644
index 0000000..cbf3839
--- /dev/null
+++ b/src/pmie/src/unary.sk
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 1995-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.
+ *
+ * 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
+ */
+
+/***********************************************************************
+ * skeleton: unary.sk - unary operator
+ ***********************************************************************/
+
+/*
+ * operator: @FUN
+ */
+
+#define @OP
+
+void
+@FUN_n(Expr *x)
+{
+ Expr *arg1 = x->arg1;
+ Sample *is = &arg1->smpls[0];
+ Sample *os = &x->smpls[0];
+ @ITYPE *ip;
+ @OTYPE *op;
+ int n;
+ int i;
+
+ EVALARG(arg1)
+ ROTATE(x)
+
+ if (arg1->valid && x->tspan > 0) {
+ ip = (@ITYPE *) is->ptr;
+ op = (@OTYPE *) os->ptr;
+ n = x->tspan;
+ for (i = 0; i < n; i++) {
+ *op = OP(*ip);
+ op++;
+ ip++;
+ }
+ os->stamp = is->stamp;
+ x->valid++;
+ }
+ else x->valid = 0;
+
+#if PCP_DEBUG
+ if (pmDebug & DBG_TRACE_APPL2) {
+ fprintf(stderr, "@FUN_n(" PRINTF_P_PFX "%p) ...\n", x);
+ dumpExpr(x);
+ }
+#endif
+}
+
+void
+@FUN_1(Expr *x)
+{
+ Expr *arg1 = x->arg1;
+ Sample *is = &arg1->smpls[0];
+ Sample *os = &x->smpls[0];
+
+ EVALARG(arg1)
+ ROTATE(x)
+
+ if (arg1->valid) {
+ *(@OTYPE *)os->ptr = OP(*(@ITYPE *)is->ptr);
+ os->stamp = is->stamp;
+ x->valid++;
+ }
+ else x->valid = 0;
+
+#if PCP_DEBUG
+ if (pmDebug & DBG_TRACE_APPL2) {
+ fprintf(stderr, "@FUN_1(" PRINTF_P_PFX "%p) ...\n", x);
+ dumpExpr(x);
+ }
+#endif
+}
+
+#undef OP
+
diff --git a/src/pmie/stomp b/src/pmie/stomp
new file mode 100644
index 0000000..7c93fec
--- /dev/null
+++ b/src/pmie/stomp
@@ -0,0 +1,11 @@
+#
+# Sample STOMP configuration file, parameters affecting connection
+# between pmie and a JMS server for the "stomp" rule action.
+#
+
+host=foo.bar.com # this is the JMS server (required)
+port=61616 # and its listening here (required)
+timeout=2 # seconds to wait for server (optional)
+topic=PMIE # JMS topic for pmie messages (optional)
+username=joe # required
+password=j03ST0MP # required