summaryrefslogtreecommitdiff
path: root/src/pmns
diff options
context:
space:
mode:
Diffstat (limited to 'src/pmns')
-rw-r--r--src/pmns/GNUmakefile79
-rw-r--r--src/pmns/GNUmakefile.install35
-rwxr-xr-xsrc/pmns/Make.stdpmid169
-rwxr-xr-xsrc/pmns/Rebuild395
-rw-r--r--src/pmns/ReplacePmnsSubtree184
-rw-r--r--src/pmns/lockpmns46
-rwxr-xr-xsrc/pmns/pmnsadd125
-rw-r--r--src/pmns/pmnsdel.c216
-rw-r--r--src/pmns/pmnsmerge.c322
-rw-r--r--src/pmns/pmnsutil.c99
-rw-r--r--src/pmns/pmnsutil.h21
-rw-r--r--src/pmns/stdpmid.local5
-rw-r--r--src/pmns/stdpmid.pcp128
-rw-r--r--src/pmns/unlockpmns30
14 files changed, 1854 insertions, 0 deletions
diff --git a/src/pmns/GNUmakefile b/src/pmns/GNUmakefile
new file mode 100644
index 0000000..32ebe34
--- /dev/null
+++ b/src/pmns/GNUmakefile
@@ -0,0 +1,79 @@
+#
+# 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
+-include ./GNUlocaldefs
+
+PMNS_VAR_DIR = $(PCP_VAR_DIR)/pmns
+PMNS_BIN_DIR = $(PCP_BINADM_DIR)
+PMNS_LIB_DIR = $(PCP_SHARE_DIR)/lib
+
+# Take control here ... do not need to search in libpcp_pmda directory
+# for libpcp_pmda DSO, and it is not even built yet for a virgin make.
+#
+PCPLIB_LDFLAGS = -L$(TOPDIR)/src/libpcp/src
+
+CFILES = pmnsmerge.c pmnsutil.c pmnsdel.c
+HFILES = pmnsutil.h
+TARGETS = pmnsmerge$(EXECSUFFIX) pmnsdel$(EXECSUFFIX)
+SCRIPTS = pmnsadd
+LOCKERS = lockpmns unlockpmns
+STDPMID = stdpmid.pcp stdpmid.local
+
+LSRCFILES = Make.stdpmid GNUmakefile.install Rebuild ReplacePmnsSubtree \
+ $(STDPMID) $(SCRIPTS) $(LOCKERS)
+
+LLDLIBS = $(PCPLIB)
+LDIRT = *.log *.pmns stdpmid .NeedRebuild build.script $(TARGETS)
+
+default: $(SCRIPTS) $(LOCKERS) $(TARGETS) \
+ GNUmakefile.install .NeedRebuild Rebuild ReplacePmnsSubtree stdpmid
+
+include $(BUILDRULES)
+
+pmnsmerge$(EXECSUFFIX): pmnsmerge.o pmnsutil.o
+ $(CCF) -o $@ $(LDFLAGS) pmnsmerge.o pmnsutil.o $(LDLIBS)
+
+pmnsdel$(EXECSUFFIX): pmnsdel.o pmnsutil.o
+ $(CCF) -o $@ $(LDFLAGS) pmnsdel.o pmnsutil.o $(LDLIBS)
+
+.NeedRebuild:
+ echo "This file flags the rc scripts to rebuild the PMNS" > .NeedRebuild
+
+# All PMNS config stuff goes in $PCP_VAR_DIR/pmns
+# For platforms that want it, the .NeedRebuild hook is added there,
+# else a manual touch(1) here is as close as it gets unfortunately.
+#
+install: default
+ $(INSTALL) -m 755 $(TARGETS) $(SCRIPTS) $(PMNS_BIN_DIR)
+ $(INSTALL) -m 755 $(LOCKERS) ReplacePmnsSubtree $(PMNS_LIB_DIR)
+ $(INSTALL) -m 644 GNUmakefile.install $(PMNS_VAR_DIR)/Makefile
+ $(INSTALL) -m 755 Rebuild $(PMNS_VAR_DIR)/Rebuild
+ $(INSTALL) -m 755 Make.stdpmid $(PMNS_VAR_DIR)/Make.stdpmid
+ $(INSTALL) -m 644 $(STDPMID) $(PMNS_VAR_DIR)
+ifeq (, $(filter redhat debian, $(PACKAGE_DISTRIBUTION)))
+ $(INSTALL) -m 644 .NeedRebuild $(PMNS_VAR_DIR)/.NeedRebuild
+endif
+
+stdpmid: $(STDPMID)
+ rm -f build.script
+ $(AWK) <Make.stdpmid >build.script '\
+/^. \$$PCP_DIR/ { print "PCP_CONF=../include/pcp.conf"; print ". ../include/pcp.env"; next }\
+ { print }'
+ sh ./build.script
+
+default_pcp: default
+
+install_pcp: install
diff --git a/src/pmns/GNUmakefile.install b/src/pmns/GNUmakefile.install
new file mode 100644
index 0000000..772c80a
--- /dev/null
+++ b/src/pmns/GNUmakefile.install
@@ -0,0 +1,35 @@
+#
+# Copyright (c) 2000,2004 Silicon Graphics, Inc. All Rights Reserved.
+#
+# This program is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by the
+# Free Software Foundation; either version 2 of the License, or (at your
+# option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+# for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+#
+# Makefile to rebuild the Performance Metrics Names Space (PMNS) (root)
+# and the standard Performance Metric Domain numbers (stdpmid)
+#
+
+TARGETS = root
+PMNS != echo root_*
+STDPMID != echo stdpmid.*
+
+default: root stdpmid
+
+root: $(PMNS)
+ ./Rebuild
+
+stdpmid: $(STDPMID)
+ ./Make.stdpmid
+
+clobber:
+ rm -f stdpmid
diff --git a/src/pmns/Make.stdpmid b/src/pmns/Make.stdpmid
new file mode 100755
index 0000000..c50423e
--- /dev/null
+++ b/src/pmns/Make.stdpmid
@@ -0,0 +1,169 @@
+#!/bin/sh
+#
+# 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.
+#
+
+# source the PCP configuration environment variables
+. $PCP_DIR/etc/pcp.env
+
+if [ -d "$PCP_TMPFILE_DIR" ]
+then
+ tmp=`mktemp -d "$PCP_TMPFILE_DIR/pcp.XXXXXXXXX"` || exit 1
+else
+ # if configure --prefix is used in a the build, then $PCP_TMPFILE_DIR
+ # may not yet exist ... /tmp is a safe bet
+ #
+ tmp=`mktemp -d /tmp/pcp.XXXXXXXXX` || exit 1
+fi
+status=1
+trap "rm -rf $tmp; exit \$status" 0 1 2 3 15
+
+prog=`basename $0`
+OLD=stdpmid
+NEW=$tmp/new
+SOURCE=""
+for file in `echo stdpmid.*`
+do
+ case $file
+ in
+ stdpmid.'*'|stdpmid.O|stdpmid.O.*|stdpmid.N|stdpmid.N.*|stdpmid.rpmorig|stdpmid.*.rpmorig)
+ ;;
+ *)
+ SOURCE="$SOURCE $file"
+ ;;
+ esac
+done
+
+[ -z "$SOURCE" ] && exit # nothing to do - the norm nowadays
+
+# post-processing with sed ...
+# - removes comments
+# - maps white space to a single space
+# - performs domain re-numbering that occured for LAB and ASH
+# between PCP 2.0 and PCP 2.1 to avoid duplicates in the interim
+#
+# Note on sort. Used to be "sort -n +1 -2", but changed to "sort -n -k2,3"
+# to avoid problems with more recent Linux coreutils versions.
+#
+for file in $SOURCE
+do
+ sed <$file \
+ -e '/^#/d' \
+ -e 's/[ ][ ]*/ /' \
+ -e '/^LAB /s/254/246/' \
+ -e '/^ASH /s/7/11/'
+done \
+| sort -n -k2,3 \
+| uniq >$tmp/tmp
+
+if [ -s "$tmp/tmp" ]
+then
+ error=false
+else
+ echo "$prog: Error: failed to create temporary file"
+ exit
+fi
+
+# scan for duplicate domain name, but different domain number
+#
+$PCP_AWK_PROG '{ print $1 }' <$tmp/tmp \
+| sort \
+| uniq -c \
+| while read cnt domain
+do
+ [ $cnt -eq 1 ] && continue
+ echo "$prog: Error: duplicate for domain name \"$domain\" ..."
+ grep "^$domain[ ]" $SOURCE | sed -e 's/^/ /'
+ error=true
+done
+
+# scan for duplicate domain number, but different domain name
+#
+$PCP_AWK_PROG '{ print $2 }' <$tmp/tmp \
+| sort \
+| uniq -c \
+| while read cnt number
+do
+ [ $cnt -eq 1 ] && continue
+ echo "$prog: Error: duplicate for domain number \"$number\" ..."
+ grep "[ ]$number\$" $SOURCE | sed -e 's/^/ /'
+ error=true
+done
+
+$error && exit
+
+# preamble
+#
+cat <<'End-of-File' >$NEW
+/*
+ * NOTE:
+ * Do not edit this file (it is re-created by Make.stdpmid).
+ * To make changes, edit one of the stdpmid.* files, most probably
+ * stdpmid.local, and as root
+ * # make stdpmid
+ *
+ * The following domain number assignments are assumed to apply
+ *
+ * Domain Number Range Use
+ * 0 reserved -- DO NOT USE
+ * 1-31 production PMDAs from PCP packages (#1)
+ * 32-39 ORACLE DBMS PMDAs
+ * 40-47 Sybase DBMS PMDAs
+ * 48-55 Informix DBMS PMDAs
+ * 56-58 SNMP Gateway PMDA
+ * 59-63 Linux PMDAs
+ * 64-69 ISV PMDAs
+ * 70-128 production PMDAs from PCP packages (#2)
+ * 129-510 End-User PMDAs and demo PMDAs
+ * 511 reserved for dynamic PMNS entries -- DO NOT USE
+ *
+ * A Performance Metrics Identifier (PMID) is internally encoded as
+ * 1 bits - flag reserved for internal use
+ * 9 bits - the Performance Metric Domain Agent (PMDA) domain number
+ * from the list below
+ * 12 bits - cluster within domain
+ * 10 bits - serial within cluster
+ */
+
+#ifndef __STDPMID
+#define __STDPMID
+
+End-of-File
+
+cat $tmp/tmp \
+| while read domain number
+do
+ echo "#define $domain $number" >>$NEW
+done
+
+echo '
+#endif' >>$NEW
+
+# only update if it has changed
+#
+if [ -f $OLD ]
+then
+ if cmp -s $OLD $NEW >/dev/null 2>&1
+ then
+ rm -f $NEW
+ fi
+fi
+
+if [ -f $NEW ]
+then
+ cp $NEW $OLD
+ chmod 444 $NEW
+fi
+
+status=0
+exit
diff --git a/src/pmns/Rebuild b/src/pmns/Rebuild
new file mode 100755
index 0000000..1d69e04
--- /dev/null
+++ b/src/pmns/Rebuild
@@ -0,0 +1,395 @@
+#!/bin/sh
+#
+# Copyright (c) 2000-2001,2003 Silicon Graphics, Inc. All Rights Reserved.
+#
+# This program is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by the
+# Free Software Foundation; either version 2 of the License, or (at your
+# option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+# for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+#
+# Rebuild the PMNS, handling assorted errors
+#
+
+# Note. has to be run from where the PMNS files are installed as local
+# file names (not full paths) are used
+#
+
+# source the PCP configuration environment variables
+. $PCP_DIR/etc/pcp.env
+
+tmp=`mktemp -d ./pcp.XXXXXXXXX` || exit 1
+status=1
+
+$PCP_SHARE_DIR/lib/lockpmns root
+trap "$PCP_SHARE_DIR/lib/unlockpmns root; rm -rf $tmp; exit \$status" 0 1 2 3 15
+
+_trace()
+{
+ if $silent
+ then
+ :
+ else
+ if $nochanges
+ then
+ echo "$*"
+ else
+ echo "$*" >>$tmp/trace
+ fi
+ fi
+}
+
+_trace_file()
+{
+ if $silent
+ then
+ :
+ else
+ if $nochanges
+ then
+ cat $1
+ else
+ cat $1 >>$tmp/trace
+ fi
+ fi
+}
+
+_syslog()
+{
+ $PCP_SYSLOG_PROG -p user.alert -t PCP "$*"
+ _trace "$*"
+}
+
+_die()
+{
+ [ -f $tmp/trace ] && cat $tmp/trace
+ rm -f root.new
+ exit
+}
+
+prog=`basename $0`
+debug=false
+nochanges=false
+root=root
+root_updated=false
+update=false
+verbose=""
+dupok=false
+silent=false
+
+_usage()
+{
+ _trace "Usage: Rebuild [-dnsuv]"
+ _trace "Options:"
+ _trace " -d allow duplicate PMIDs in the PMNS"
+ _trace " -n dry run, show me what would be done"
+ _trace " -s silent, exit status says it all"
+ _trace " -u once only upgrade processing for a new PCP version"
+ _trace " -v verbose, for the paranoid"
+}
+
+# Fixup "root" after PCP upgrade
+#
+_upgrade_root()
+{
+ [ ! -f root ] && return
+ _trace "Rebuild: PCP upgrade processing for \"root\" PMNS changes ..."
+ $nochanges && _trace "+ cull root_* names from PMNS ... <root >$tmp/root"
+
+ # Cull root to remove all metrics from root_* files, so only metrics
+ # for optional PMDAs remain
+
+ # If there are deprecated top-level names (below "root") that are
+ # no longer in a root_* file, add them here ...
+ EXCLUDE="pagebuf origin"
+ if [ "$PCP_PLATFORM" = linux ]
+ then
+ # If we're on Linux and the proc PMDA is _not_ included in
+ # the pmcd configuration file, add the top-level metrics
+ # that migrated from the linux PMDA to the proc PMDA
+ #
+ if [ -f $PCP_PMCDCONF_PATH ]
+ then
+ if grep '^proc[ ]' $PCP_PMCDCONF_PATH >/dev/null
+ then
+ # proc PMDA is installed
+ #
+ :
+ else
+ EXCLUDE="$EXCLUDE proc cgroup"
+ fi
+ else
+ EXCLUDE="$EXCLUDE proc cgroup"
+ fi
+ else
+ EXCLUDE="$EXCLUDE proc cgroup"
+ fi
+
+ # now gather top-level names from root_* files
+ #
+ if [ "`echo root_*`" != "root_*" ]
+ then
+ EXCLUDE_TMP=`for file in root_*
+ do
+ $PCP_AWK_PROG <$file '
+$1 == "}" { exit }
+in_root==1 { printf "%s ",$1 }
+$1 == "root" && $2 == "{" { in_root = 1 }'
+done`
+ EXCLUDE="$EXCLUDE $EXCLUDE_TMP"
+ fi
+
+ if [ ! -z "$verbose" -a ! -z "$EXCLUDE" ]
+ then
+ _trace "Exclude these top-level names ..."
+ _trace "`echo $EXCLUDE | fmt | sed -e 's/^/ /'`"
+ fi
+
+ $PCP_AWK_PROG <root >$tmp/root '
+BEGIN { # exclude these top-level names and all their descendents
+ #
+ n = split("'"$EXCLUDE"'", e)
+ for (i=1; i <= n; i++) {
+ not_in_root[e[i]] = 1
+ }
+
+ in_root = 0
+ skip = 0
+ }
+$1 == "root" && $2 == "{" { in_root = 1 }
+$1 == "}" { in_root = 0 }
+in_root { if (!($1 in not_in_root)) print
+ next
+ }
+$2 == "{" { n = split($1, name, ".")
+ if (n > 0 && name[1] in not_in_root)
+ skip = 1
+ }
+skip && $1 == "}" { skip = 0; next }
+skip { next }
+ { print }'
+ if cmp -s root $tmp/root >/dev/null 2>&1
+ then
+ # no changes ... already been here?
+ :
+ else
+ # we will usually end up here
+ root=$tmp/root
+ root_updated=true
+ fi
+}
+
+while getopts dnusv\? c
+do
+ case $c
+ in
+ d) dupok=true
+ ;;
+ n) nochanges=true
+ echo "$prog: Warning: dry run, no changes will be made"
+ ;;
+ u) update=true
+ ;;
+ s) silent=true
+ ;;
+ v) verbose="-v"
+ ;;
+ \?) _usage
+ status=0
+ _die
+ ;;
+ esac
+done
+shift `expr $OPTIND - 1`
+
+# some preliminary checks
+#
+for file in $PCP_BINADM_DIR/pmnsmerge
+do
+ if [ ! -x $file ]
+ then
+ _syslog "$prog: $file is missing. Cannot proceed."
+ _die
+ fi
+done
+
+# remove all trace of old binary pmns (not used in PCP 3.6 or later)
+#
+rm -f root.bin
+
+here=`pwd`
+_trace "Rebuilding the Performance Metrics Name Space (PMNS) in $here ..."
+
+if [ $# -ne 0 ]
+then
+ _usage
+ _die
+fi
+
+if $nochanges
+then
+ CP="_trace + cp"
+ MV="_trace + mv"
+ LN="_trace + ln"
+ RM="_trace + rm"
+ PMNSMERGE="_trace + pmnsmerge ..."
+else
+ CP=cp
+ MV=mv
+ LN=ln
+ RM=rm
+ PMNSMERGE=
+
+ if [ ! -w `pwd` ]
+ then
+ _syslog "$prog: cannot write in directory `pwd`, script should be run as \"root\"?"
+ _die
+ fi
+
+ if [ ! -f root ]
+ then
+ echo "root {" >root
+ echo "}" >>root
+ chmod 644 root
+ fi
+
+ if [ ! -w root ]
+ then
+ _syslog "$prog: cannot write file \"root\" in directory `pwd`, script should be run as \"root\"?"
+ _die
+ fi
+fi
+
+if $update
+then
+ # PCP upgrade fix ups
+ #
+ _upgrade_root
+fi
+
+if [ -f $root ]
+then
+ haveroot=true
+else
+ haveroot=false
+ if $nochanges
+ then
+ _trace "+ create empty root PMNS ..."
+ else
+ root=$tmp/root
+ cat <<EOFEOF >$root
+root {
+}
+EOFEOF
+ fi
+fi
+
+# Merge $root and root_* to produce the new root.
+# Each root_* file should be a complete namespace,
+# i.e. it should include an entry for root.
+#
+mergelist=""
+if [ "`echo root_*`" != "root_*" ]
+then
+ mergelist=`ls -1 root_* | $PCP_AWK_PROG '
+ /root_web/ {next}
+ {print}'`
+fi
+
+_trace "$prog: merging the following PMNS files: "
+_trace $root $mergelist | fmt | sed -e 's/^/ /'
+
+rm -f root.new
+eval $PMNSMERGE
+if $dupok
+then
+ pmnsmerge $verbose -d $root $mergelist root.new >$tmp/out 2>&1
+else
+ pmnsmerge $verbose $root $mergelist root.new >$tmp/out 2>&1
+fi
+
+if [ $? != 0 ]
+then
+ cat $tmp/out
+ _syslog "$prog: pmnsmerge failed"
+ _trace " \"root\" has not been changed."
+ _die
+fi
+
+# Multiple Rebuilds in succession should be a no-op.
+#
+if [ -f root ]
+then
+ if $dupok
+ then
+ pminfo -m -N root 2>/dev/null | sort >$tmp/list.old
+ else
+ pminfo -m -n root 2>/dev/null | sort >$tmp/list.old
+ fi
+fi
+if [ ! -s $tmp/list.old ]
+then
+ if $dupok
+ then
+ pminfo -m -N $root 2>/dev/null | sort >$tmp/list.old
+ else
+ pminfo -m -n $root 2>/dev/null | sort >$tmp/list.old
+ fi
+fi
+if $dupok
+then
+ pminfo -m -N root.new | sort >$tmp/list.new
+else
+ pminfo -m -n root.new | sort >$tmp/list.new
+fi
+if cmp -s $tmp/list.old $tmp/list.new > /dev/null 2>&1
+then
+ [ ! -f root ] && eval $MV root.new root
+ _trace "$prog: PMNS is unchanged."
+else
+ # Install the new root
+ #
+ [ ! -z "$verbose" ] && _trace_file $tmp/out
+ if $haveroot
+ then
+ _trace "$prog: PMNS \"$here/root\" updated."
+ _trace "... previous version saved as \"$here/root.prev\""
+ eval $MV root root.prev
+ else
+ _trace "$prog: new PMNS \"$here/root\" created."
+ fi
+ eval $MV root.new root
+
+ # signal pmcd if it is running
+ #
+ pminfo -v pmcd.version >/dev/null 2>&1 && pmsignal -a -s HUP pmcd
+
+ if [ ! -z "$verbose" ] && $haveroot
+ then
+ _trace "+ PMNS differences ..."
+ diff -c $tmp/list.old $tmp/list.new >$tmp/diff
+ _trace_file $tmp/diff
+ _trace
+ _trace "+ root differences ..."
+ diff -c root.prev root >$tmp/diff
+ _trace_file $tmp/diff
+ fi
+fi
+rm -f root.new
+
+# remake stdpmid
+#
+[ -f Make.stdpmid ] && ./Make.stdpmid
+
+[ X"$verbose" = X-v -a -f $tmp/trace ] && cat $tmp/trace
+
+status=0
+exit
diff --git a/src/pmns/ReplacePmnsSubtree b/src/pmns/ReplacePmnsSubtree
new file mode 100644
index 0000000..8b5a05c
--- /dev/null
+++ b/src/pmns/ReplacePmnsSubtree
@@ -0,0 +1,184 @@
+#!/bin/sh
+#
+# Copyright (c) 2008 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.
+#
+# Replace a subtree in a performance metrics namespace (PMNS) with a new
+# subtree read from a file. The file is moved (note: not copied) into the
+# PMNS and given the same name as the subtree.
+#
+# Usage: ReplacePmnsSubtree [-n namespace] pmns-subtree replacement-file
+#
+# Refer to the NOTE at the bottom of the file for important locking details.
+#
+
+. $PCP_DIR/etc/pcp.env
+
+namespace=$PCP_VAR_DIR/pmns/root
+
+# Print usage message to stderr and exit with status provided (default 1)
+
+usageExit()
+{
+ echo "Usage: $prog [-n namespace] pmns-subtree replacement-file" 1>&2
+ exit ${1:-1}
+}
+
+# Sanity check options, parameters, namespace, etc.
+
+prog=`basename $0`
+while getopts n:\? c
+do
+ case "$c" in
+ n)
+ if [ "x$OPTARG" = x ]
+ then
+ echo "$prog: -n requires a namespace" 1>&2
+ exit 1
+ else
+ namespace="$OPTARG"
+ fi
+ ;;
+ \?)
+ usageExit 0
+ ;;
+ esac
+done
+
+if [ ! -f $namespace ]
+then
+ echo "$prog: namespace doesn't exist: $namespace" 1>&2
+ exit 1
+fi
+
+[ $# != 2 ] && usageExit
+
+subtree=$1
+newSubtreeFile=$2
+namespaceDir=`dirname $namespace`
+
+if [ ! -w $namespaceDir ]
+then
+ echo "$prog: can't write namespace directory $namespaceDir" 1>&2
+ exit 1
+fi
+
+if [ ! -f $newSubtreeFile ]
+then
+ echo "$prog: can't read replacement namespace subtree file for $subtree:" \
+ "$newSubtreeFile" 1>&2
+ exit 1
+fi
+
+# variables for back-up/restore of namespace files affected by this script
+
+backups="root"
+[ -f "$namespaceDir/$subtree" ] && backups="$backups $subtree"
+backSuffix="$prog-$$-backup"
+restore=false # restore backup namespace files in cleanup()
+
+# Signal and exit handler to clean/restore namespace (lock and backups).
+
+haveLock=false
+
+# signals we need to be careful of ... HUP, INT, QUIT, PIPE, ALRM, TERM
+#
+STD_SIGNALS="1 2 3 13 14 15"
+
+cleanup()
+{
+ # Release namespace lock as early as possible. Ignore signals to avoid
+ # releasing a lock already released.
+ trap "" $STD_SIGNALS
+
+ if $restore
+ then
+ for f in $backups
+ do
+ [ -f "$namespaceDir/.$f-$backSuffix" ] && \
+ mv "$namespaceDir/.$f-$backSuffix" "$namespaceDir/$f"
+ done
+ $haveLock && unlockpmns $namespace
+ haveLock=false
+ else
+ $haveLock && unlockpmns $namespace
+ haveLock=false
+ for f in $backups
+ do
+ rm -f "$namespaceDir/.$f-$backSuffix"
+ done
+ fi
+}
+
+trap "cleanup; exit" 0 $STD_SIGNALS
+
+# "haveLock=true" is duplicated in both "if" and "else" to minimise the window
+# for a signal leaving an orphaned lock if the "else" condition fires (we
+# stole an existing lock). See note at bottom of script for details.
+
+if lockpmns $namespace
+then
+ haveLock=true # duplicate: minimise race condition (see note above)
+else
+ haveLock=true # duplicate: minimise race condition (see note above)
+ $PCP_BINADM_DIR/pmpost "PCP: PMNS lock stolen by: $*"
+fi
+
+# Namespace is locked, back up affected files. Once backup completes, enable
+# namespace restore during error handling.
+
+backupErr=false
+for f in $backups
+do
+ dest="$namespaceDir/.$f-$backSuffix"
+ if cp "$namespaceDir/$f" "$dest"
+ then
+ :
+ else
+ echo "error creating namespace backup for $f" 1>&2
+ backupErr=true
+ fi
+done
+$backupErr && exit 1
+restore=true
+
+# pmnsdel leaves any file corresponding to the deleted subtree in place after
+# it runs, regardless of whether it succeeds or fails. Allow pmnsdel to fail
+# (there may be no existing subtree). Return 0 only if the entire replacement
+# succeeds.
+
+sts=0
+pmnsdel -n $namespace $subtree >/dev/null 2>&1
+if pmnsadd -n $namespace $newSubtreeFile >/dev/null 2>&1
+then
+ mv -f $newSubtreeFile $namespaceDir/$subtree
+ sts=$?
+else
+ sts=1
+fi
+[ $sts = 0 ] && restore=false
+exit $sts
+
+##############################################################################
+
+# NOTE
+#
+# If a signal occurs in the very short window between pmnslock returning and
+# setting haveLock, the lock is not released by cleanup(). Any subsequent
+# process calling pmnslock will block until its pmnslock steals the lock
+# (currently after 120 secs). That window is carefully minimised.
+#
+# Past versions of similar scripts (e.g. Rebuild) unconditionally unlocked the
+# namespace when a signal was caught. This could potentially happen while
+# still waiting to acquire the lock, breaking a lock held by another process!
+#
+# Consider locking and backup/restore implications carefully if making changes.
diff --git a/src/pmns/lockpmns b/src/pmns/lockpmns
new file mode 100644
index 0000000..9e670cc
--- /dev/null
+++ b/src/pmns/lockpmns
@@ -0,0 +1,46 @@
+#!/bin/sh
+#
+# 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
+#
+# Lock the PMNS against concurrent transactional updates
+#
+
+. $PCP_DIR/etc/pcp.env
+
+lock=${1-$PCP_VAR_DIR/pmns/root}.lock
+
+i=0
+while true
+do
+ if pmlock $lock
+ then
+ # lock acquired
+ #
+ #DEBUG# echo "pmnslock: `date; ls -li $lock`"
+ break
+ fi
+ if [ $i -eq 20 ]
+ then
+ echo "lockpmns: Warning: Unable to acquire lock ($lock)"
+ echo " after 120 seconds ... continuing anyway"
+ exit 1
+ fi
+ sleep 5
+ i=`expr $i + 1`
+done
+
+exit 0
diff --git a/src/pmns/pmnsadd b/src/pmns/pmnsadd
new file mode 100755
index 0000000..a14e4ad
--- /dev/null
+++ b/src/pmns/pmnsadd
@@ -0,0 +1,125 @@
+#!/bin/sh
+#
+# Copyright (c) 1997-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.
+#
+# Add a subtree of new names into the namespace in the current directory
+#
+
+# source the PCP configuration environment variables
+. $PCP_DIR/etc/pcp.env
+
+umask 22 # anything else is pretty silly
+exitsts=1
+tmp=`mktemp -d $PCP_TMPFILE_DIR/pcp.XXXXXXXXX` || exit 1
+prog=`basename $0`
+trap "rm -rf $tmp; exit \$exitsts" 0 1 2 3 15
+
+_usage()
+{
+ echo "Usage: pmnsadd [-d] [-n namespace] file"
+}
+
+namespace=${PMNS_DEFAULT-$PCP_VAR_DIR/pmns/root}
+dupok=""
+
+while getopts dn:\? c
+do
+ case $c
+ in
+ d) dupok="-d"
+ ;;
+ n) namespace=$OPTARG
+ ;;
+ \?) _usage
+ exitsts=0
+ exit
+ ;;
+ esac
+done
+shift `expr $OPTIND - 1`
+
+if [ $# -ne 1 ]
+then
+ _usage
+ exit
+fi
+
+if [ ! -f $namespace ]
+then
+ echo "$prog: cannot find PMNS file \"$root\""
+ exit
+fi
+
+if [ ! -w $namespace ]
+then
+ echo "$prog: cannot open PMNS file \"$root\" for writing"
+ exit
+fi
+
+if [ ! -f "$1" ]
+then
+ echo "$prog: cannot find input file \"$1\""
+ exit
+fi
+
+if grep '^root {' "$1" >/dev/null
+then
+ # Special case ... if the PMDA only supplies metrics at the root of
+ # the PMNS, e.g. the MMV PMDA with all dynamic metrics, the input
+ # PMNS contains 'root {' already, so just use that without any need
+ # to construct the upper levels of the PMNS
+ #
+ cp "$1" $tmp/tmp
+else
+ # Normal case ... find PMNS pathname for base of new subtree
+ # (subroot), construct upper levels of PMNS as required and hand-off
+ # to pmnsmerge
+ #
+ subroot=`$PCP_AWK_PROG <"$1" 'NF >= 2 && $2 == "{" { print $1 ; exit }'`
+ echo 'root {' >$tmp/tmp
+ path=""
+ for name in `echo "$subroot" | tr '.' ' '`
+ do
+ [ ! -z "$path" ] && echo "$path {" >>$tmp/tmp
+ echo " $name" >>$tmp/tmp
+ echo "}" >>$tmp/tmp
+ if [ -z "$path" ]
+ then
+ path="$name"
+ else
+ path="$path.$name"
+ fi
+ done
+ cat "$1" >>$tmp/tmp
+fi
+
+# try to preserve mode, owner and group for the new output files
+#
+rm -f $namespace.new
+[ -f $namespace ] && cp -p $namespace $namespace.new
+
+$PCP_BINADM_DIR/pmnsmerge -f $dupok $namespace $tmp/tmp $namespace.new
+exitsts=$?
+
+# from here on, ignore SIGINT, SIGHUP and SIGTERM to protect
+# the integrity of the new ouput files
+#
+trap "" 1 2 15
+
+if [ $exitsts = 0 ]
+then
+ mv $namespace.new $namespace
+else
+ echo "$prog: No changes have been made to the PMNS file \"$namespace\""
+ rm -f $namespace.new
+fi
diff --git a/src/pmns/pmnsdel.c b/src/pmns/pmnsdel.c
new file mode 100644
index 0000000..01a0150
--- /dev/null
+++ b/src/pmns/pmnsdel.c
@@ -0,0 +1,216 @@
+/*
+ * Cull subtree(s) from a PCP PMNS
+ *
+ * Copyright (c) 2014 Red Hat.
+ * Copyright (c) 1995-2001 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#include <ctype.h>
+#include <sys/stat.h>
+#include "pmapi.h"
+#include "impl.h"
+#include "pmnsutil.h"
+
+static FILE *outf; /* output */
+static __pmnsNode *root; /* result so far */
+static char *fullname; /* full PMNS pathname for newbie */
+
+static pmLongOptions longopts[] = {
+ PMAPI_OPTIONS_HEADER("Options"),
+ PMOPT_DEBUG,
+ { "duplicates", 0, 'd', 0, "duplicate PMIDs are allowed" },
+ PMOPT_NAMESPACE,
+ PMOPT_HELP,
+ PMAPI_OPTIONS_END
+};
+
+static pmOptions opts = {
+ .short_options = "dD:n:?",
+ .long_options = longopts,
+ .short_usage = "[options] metricpath [...]",
+};
+
+static void
+delpmns(__pmnsNode *base, char *name)
+{
+ char *tail;
+ ptrdiff_t nch;
+ __pmnsNode *np;
+ __pmnsNode *lastp = NULL;
+
+ for (tail = name; *tail && *tail != '.'; tail++)
+ ;
+ nch = tail - name;
+
+ for (np = base->first; np != NULL; np = np->next) {
+ if (strlen(np->name) == nch && strncmp(name, np->name, (int)nch) == 0)
+ break;
+ lastp = np;
+ }
+
+ if (np == NULL) {
+ /* no match ... */
+ fprintf(stderr, "%s: Error: metricpath \"%s\" not defined in the PMNS\n",
+ pmProgname, fullname);
+ exit(1);
+ }
+ else if (*tail == '\0') {
+ /* complete match */
+ if (np == base->first) {
+ /* deleted node is first at this level */
+ base->first = np->next;
+ /*
+ * remove predecessors that only exist to connect
+ * deleted node to the rest of the PMNS
+ */
+ np = base;
+ while (np->first == NULL && np->parent != NULL) {
+ if (np->parent->first == np) {
+ /* victim is the only one at this level ... */
+ np->parent->first = np->next;
+ np = np->parent;
+ }
+ else {
+ /* victim has at least one sibling at this level */
+ lastp = np->parent->first;
+ while (lastp->next != np)
+ lastp = lastp->next;
+ lastp->next = np->next;
+ break;
+ }
+ }
+ }
+ else
+ /* link around deleted node */
+ lastp->next = np->next;
+ return;
+ }
+
+ /* descend */
+ delpmns(np, tail+1);
+}
+
+int
+main(int argc, char **argv)
+{
+ int sep = __pmPathSeparator();
+ int sts;
+ int c;
+ int dupok = 0;
+ char *p;
+ char pmnsfile[MAXPATHLEN];
+ char outfname[MAXPATHLEN];
+ struct stat sbuf;
+
+ if ((p = getenv("PMNS_DEFAULT")) != NULL)
+ strcpy(pmnsfile, p);
+ else
+ snprintf(pmnsfile, sizeof(pmnsfile), "%s%c" "pmns" "%c" "root",
+ pmGetConfig("PCP_VAR_DIR"), sep, sep);
+
+ while ((c = pmgetopt_r(argc, argv, &opts)) != EOF) {
+ switch (c) {
+
+ case 'd': /* duplicate PMIDs are OK */
+ dupok = 1;
+ break;
+
+ case 'D': /* debug flag */
+ if ((sts = __pmParseDebug(opts.optarg)) < 0) {
+ pmprintf("%s: unrecognized debug flag specification (%s)\n",
+ pmProgname, opts.optarg);
+ opts.errors++;
+ } else {
+ pmDebug |= sts;
+ }
+ break;
+
+ case 'n': /* alternative name space file */
+ strcpy(pmnsfile, opts.optarg);
+ break;
+
+ case '?':
+ default:
+ opts.errors++;
+ break;
+ }
+ }
+
+ if (opts.errors || opts.optind > argc - 1) {
+ pmUsageMessage(&opts);
+ exit(1);
+ }
+
+ if ((sts = pmLoadASCIINameSpace(pmnsfile, dupok)) < 0) {
+ fprintf(stderr, "%s: Error: pmLoadNameSpace(%s): %s\n",
+ pmProgname, pmnsfile, pmErrStr(sts));
+ exit(1);
+ }
+
+ {
+ __pmnsTree *t;
+ t = __pmExportPMNS();
+ if (t == NULL) {
+ /* sanity check - shouldn't ever happen */
+ fprintf(stderr, "Exported PMNS is NULL !");
+ exit(1);
+ }
+ root = t->root;
+ }
+
+
+ while (opts.optind < argc) {
+ delpmns(root, fullname = argv[opts.optind]);
+ opts.optind++;
+ }
+
+ /*
+ * from here on, ignore SIGHUP, SIGINT and SIGTERM to protect
+ * the integrity of the new ouput file
+ */
+ __pmSetSignalHandler(SIGHUP, SIG_IGN);
+ __pmSetSignalHandler(SIGINT, SIG_IGN);
+ __pmSetSignalHandler(SIGTERM, SIG_IGN);
+
+ snprintf(outfname, sizeof(outfname), "%s.new", pmnsfile);
+ if ((outf = fopen(outfname, "w")) == NULL) {
+ fprintf(stderr, "%s: Error: cannot open PMNS file \"%s\" for writing: %s\n",
+ pmProgname, outfname, osstrerror());
+ exit(1);
+ }
+ if (stat(pmnsfile, &sbuf) == 0) {
+ /*
+ * preserve the mode and ownership of any existing PMNS file
+ */
+ chmod(outfname, sbuf.st_mode & ~S_IFMT);
+#if defined(HAVE_CHOWN)
+ if (chown(outfname, sbuf.st_uid, sbuf.st_gid) < 0)
+ fprintf(stderr, "%s: chown(%s, ...) failed: %s\n",
+ pmProgname, outfname, osstrerror());
+#endif
+ }
+
+ pmns_output(root, outf);
+ fclose(outf);
+
+ /* rename the PMNS */
+ if (rename2(outfname, pmnsfile) == -1) {
+ fprintf(stderr, "%s: cannot rename \"%s\" to \"%s\": %s\n",
+ pmProgname, outfname, pmnsfile, osstrerror());
+ /* remove the new PMNS */
+ unlink(outfname);
+ exit(1);
+ }
+
+ exit(0);
+}
diff --git a/src/pmns/pmnsmerge.c b/src/pmns/pmnsmerge.c
new file mode 100644
index 0000000..39316a1
--- /dev/null
+++ b/src/pmns/pmnsmerge.c
@@ -0,0 +1,322 @@
+/*
+ * pmnsmerge [-adfv] infile [...] outfile
+ *
+ * Merge PCP PMNS files
+ *
+ * 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.
+ */
+
+#include <ctype.h>
+#include <sys/stat.h>
+#include "pmapi.h"
+#include "impl.h"
+#include "pmnsutil.h"
+
+static FILE *outf; /* output */
+static __pmnsNode *root; /* result so far */
+static char *fullname; /* full PMNS pathname for newbie */
+static int verbose;
+
+static pmLongOptions longopts[] = {
+ PMAPI_OPTIONS_HEADER("Options"),
+ PMOPT_DEBUG,
+ { "", 0, 'a', 0, "process files in order, ignoring embedded _DATESTAMP control lines" },
+ { "duplicates", 0, 'd', 0, "duplicate PMIDs are allowed" },
+ { "force", 0, 'f', 0, "force overwriting of the output file if it exists" },
+ { "verbose", 0, 'v', 0, "verbose, echo input file names as processed" },
+ PMOPT_HELP,
+ PMAPI_OPTIONS_END
+};
+
+static pmOptions opts = {
+ .short_options = "aD:dfv?",
+ .long_options = longopts,
+ .short_usage = "[options] infile [...] outfile",
+};
+
+typedef struct {
+ char *fname;
+ char *date;
+} datestamp_t;
+
+#define STAMP "_DATESTAMP"
+
+static int
+sortcmp(const void *a, const void *b)
+{
+ datestamp_t *pa = (datestamp_t *)a;
+ datestamp_t *pb = (datestamp_t *)b;
+
+ if (pa->date == NULL) return -1;
+ if (pb->date == NULL) return 1;
+ return strcmp(pa->date, pb->date);
+}
+
+/*
+ * scan for #define _DATESTAMP and re-order args accordingly
+ */
+static void
+sortargs(char **argv, int argc)
+{
+ FILE *f;
+ datestamp_t *tab;
+ char *p;
+ char *q;
+ int i;
+ char lbuf[40];
+
+ tab = (datestamp_t *)malloc(argc * sizeof(datestamp_t));
+
+ for (i = 0; i <argc; i++) {
+ if ((f = fopen(argv[i], "r")) == NULL) {
+ fprintf(stderr, "%s: Error: cannot open input PMNS file \"%s\"\n",
+ pmProgname, argv[i]);
+ exit(1);
+ }
+ tab[i].fname = strdup(argv[i]);
+ tab[i].date = NULL;
+ while (fgets(lbuf, sizeof(lbuf), f) != NULL) {
+ if (strncmp(lbuf, "#define", 7) != 0)
+ continue;
+ p = &lbuf[7];
+ while (*p && isspace((int)*p))
+ p++;
+ if (*p == '\0' || strncmp(p, STAMP, strlen(STAMP)) != 0)
+ continue;
+ p += strlen(STAMP);
+ while (*p && isspace((int)*p))
+ p++;
+ q = p;
+ while (*p && !isspace((int)*p))
+ p++;
+ *p = '\0';
+ tab[i].date = strdup(q);
+ break;
+ }
+ fclose(f);
+ }
+
+ qsort(tab, argc, sizeof(tab[0]), sortcmp);
+ for (i = 0; i <argc; i++) {
+ argv[i] = tab[i].fname;
+ if (verbose > 1)
+ printf("arg[%d] %s _DATESTAMP=%s\n", i, tab[i].fname, tab[i].date);
+ }
+
+ free(tab);
+}
+
+static void
+addpmns(__pmnsNode *base, char *name, __pmnsNode *p)
+{
+ char *tail;
+ ptrdiff_t nch;
+ __pmnsNode *np;
+ __pmnsNode *lastp = NULL;
+
+ for (tail = name; *tail && *tail != '.'; tail++)
+ ;
+ nch = tail - name;
+
+ for (np = base->first; np != NULL; np = np->next) {
+ if (strlen(np->name) == nch && strncmp(name, np->name, (int)nch) == 0)
+ break;
+ lastp = np;
+ }
+
+ if (np == NULL) {
+ /* no match ... add here */
+ np = (__pmnsNode *)malloc(sizeof(__pmnsNode));
+ if (base->first) {
+ lastp->next = np;
+ np->parent = lastp->parent;
+ }
+ else {
+ base->first = np;
+ np->parent = base;
+ }
+ np->first = np->next = NULL;
+ np->hash = NULL; /* we do not need this here */
+ np->name = (char *)malloc(nch+1);
+ strncpy(np->name, name, nch);
+ np->name[nch] = '\0';
+ if (*tail == '\0') {
+ np->pmid = p->pmid;
+ return;
+ }
+ np->pmid = PM_ID_NULL;
+ }
+ else if (*tail == '\0') {
+ /* complete match */
+ if (np->pmid != p->pmid) {
+ fprintf(stderr, "%s: Warning: performance metric \"%s\" has multiple PMIDs.\n... using PMID %s and ignoring PMID",
+ pmProgname, fullname, pmIDStr(np->pmid));
+ fprintf(stderr, " %s\n",
+ pmIDStr(p->pmid));
+ }
+ return;
+ }
+
+ /* descend */
+ addpmns(np, tail+1, p);
+}
+
+
+/*
+ * merge, adding new nodes if required
+ */
+static void
+merge(__pmnsNode *p, int depth, char *path)
+{
+ char *name;
+
+ if (depth < 1 || p->pmid == PM_ID_NULL || p->first != NULL)
+ return;
+ name = (char *)malloc(strlen(path)+strlen(p->name)+2);
+ if (*path == '\0')
+ strcpy(name, p->name);
+ else {
+ strcpy(name, path);
+ strcat(name, ".");
+ strcat(name, p->name);
+ }
+ fullname = name;
+ addpmns(root, name, p);
+ free(name);
+}
+
+int
+main(int argc, char **argv)
+{
+ int sts;
+ int first = 1;
+ int c;
+ int j;
+ int force = 0;
+ int asis = 0;
+ int dupok = 0;
+ __pmnsNode *tmp;
+
+ umask((mode_t)022); /* anything else is pretty silly */
+
+ while ((c = pmgetopt_r(argc, argv, &opts)) != EOF) {
+ switch (c) {
+
+ case 'a':
+ asis = 1;
+ break;
+
+ case 'd': /* duplicate PMIDs are OK */
+ dupok = 1;
+ break;
+
+ case 'D': /* debug flag */
+ if ((sts = __pmParseDebug(opts.optarg)) < 0) {
+ pmprintf("%s: unrecognized debug flag specification (%s)\n",
+ pmProgname, opts.optarg);
+ opts.errors++;
+ } else {
+ pmDebug |= sts;
+ }
+ break;
+
+ case 'f': /* force ... unlink file first */
+ force = 1;
+ break;
+
+ case 'v':
+ verbose++;
+ break;
+
+ case '?':
+ default:
+ opts.errors++;
+ break;
+ }
+ }
+
+ if (opts.errors || opts.optind > argc - 2) {
+ pmUsageMessage(&opts);
+ exit(1);
+ }
+
+ if (force)
+ unlink(argv[argc-1]);
+
+ if (access(argv[argc-1], F_OK) == 0) {
+ fprintf(stderr, "%s: Error: output PMNS file \"%s\" already exists!\nYou must either remove it first, or use -f\n",
+ pmProgname, argv[argc-1]);
+ exit(1);
+ }
+
+ /*
+ * from here on, ignore SIGHUP, SIGINT and SIGTERM to protect
+ * the integrity of the new ouput file
+ */
+ __pmSetSignalHandler(SIGHUP, SIG_IGN);
+ __pmSetSignalHandler(SIGINT, SIG_IGN);
+ __pmSetSignalHandler(SIGTERM, SIG_IGN);
+
+ if ((outf = fopen(argv[argc-1], "w+")) == NULL) {
+ fprintf(stderr, "%s: Error: cannot create output PMNS file \"%s\": %s\n", pmProgname, argv[argc-1], osstrerror());
+ exit(1);
+ }
+
+ if (!asis)
+ sortargs(&argv[opts.optind], argc - opts.optind - 1);
+
+ j = opts.optind;
+ while (j < argc-1) {
+ if (verbose)
+ printf("%s:\n", argv[j]);
+
+ if ((sts = pmLoadASCIINameSpace(argv[j], dupok)) < 0) {
+ fprintf(stderr, "%s: Error: pmLoadNameSpace(%s): %s\n",
+ pmProgname, argv[j], pmErrStr(sts));
+ exit(1);
+ }
+ {
+ __pmnsTree *t;
+ t = __pmExportPMNS();
+ if (t == NULL) {
+ /* sanity check - shouldn't ever happen */
+ fprintf(stderr, "Exported PMNS is NULL !");
+ exit(1);
+ }
+ tmp = t->root;
+ }
+
+ if (first) {
+ root = tmp;
+ first = 0;
+ }
+ else {
+ pmns_traverse(tmp, 0, "", merge);
+ }
+ j++;
+ }
+
+ pmns_output(root, outf);
+ fclose(outf);
+
+ /*
+ * now load the merged PMNS to check for errors ...
+ */
+ if ((sts = pmLoadASCIINameSpace(argv[argc-1], dupok)) < 0) {
+ fprintf(stderr, "%s: Error: pmLoadNameSpace(%s): %s\n",
+ pmProgname, argv[argc-1], pmErrStr(sts));
+ exit(1);
+ }
+
+ exit(0);
+}
diff --git a/src/pmns/pmnsutil.c b/src/pmns/pmnsutil.c
new file mode 100644
index 0000000..1e0acf9
--- /dev/null
+++ b/src/pmns/pmnsutil.c
@@ -0,0 +1,99 @@
+/*
+ * 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 "pmapi.h"
+#include "impl.h"
+#include "pmnsutil.h"
+
+static FILE *outf;
+
+/*
+ * breadth-first traversal
+ */
+void
+pmns_traverse(__pmnsNode *p, int depth, char *path, void(*func)(__pmnsNode *, int, char *))
+{
+ char *newpath;
+ __pmnsNode *q;
+
+ if (p != NULL) {
+ /* breadth */
+ for (q = p; q != NULL; q = q->next)
+ (*func)(q, depth, path);
+ if (depth > 0)
+ (*func)(NULL, -1, NULL); /* end of level */
+ /* descend */
+ for (q = p; q != NULL; q = q->next) {
+ if (q->first != NULL) {
+ newpath = (char *)malloc(strlen(path)+strlen(q->name)+2);
+ if (depth == 0)
+ *newpath = '\0';
+ else if (depth == 1)
+ strcpy(newpath, q->name);
+ else {
+ strcpy(newpath, path);
+ strcat(newpath, ".");
+ strcat(newpath, q->name);
+ }
+ pmns_traverse(q->first, depth+1, newpath, func);
+ free(newpath);
+ }
+ }
+ }
+}
+
+/*
+ * generate an ASCII PMNS from the internal format produced by
+ * pmLoadNameSpace and friends
+ */
+static void
+output(__pmnsNode *p, int depth, char *path)
+{
+ static int lastdepth = -1;
+
+ if (depth == 0) {
+ fprintf(outf, "root {\n");
+ lastdepth = 1;
+ return;
+ }
+ else if (depth < 0) {
+ if (lastdepth > 0)
+ fprintf(outf, "}\n");
+ lastdepth = -1;
+ return;
+ }
+ else if (depth != lastdepth)
+ fprintf(outf, "\n%s {\n", path);
+ lastdepth = depth;
+ if (p->first != NULL)
+ fprintf(outf, "\t%s\n", p->name);
+ else {
+ if (pmid_domain(p->pmid) == DYNAMIC_PMID && pmid_item(p->pmid) == 0)
+ fprintf(outf, "\t%s\t%d:*:*\n", p->name, pmid_cluster(p->pmid));
+ else
+ fprintf(outf, "\t%s\t%d:%d:%d\n", p->name, pmid_domain(p->pmid), pmid_cluster(p->pmid), pmid_item(p->pmid));
+ }
+}
+
+void
+pmns_output(__pmnsNode *root, FILE *f)
+{
+ outf = f;
+ pmns_traverse(root, 0, "", output);
+ output(NULL, -2, NULL); /* special hack for null PMNS */
+}
diff --git a/src/pmns/pmnsutil.h b/src/pmns/pmnsutil.h
new file mode 100644
index 0000000..dd6c536
--- /dev/null
+++ b/src/pmns/pmnsutil.h
@@ -0,0 +1,21 @@
+/*
+ * 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
+ */
+
+extern void pmns_traverse(__pmnsNode *, int , char *, void(*)(__pmnsNode *, int, char *));
+extern void pmns_output(__pmnsNode *, FILE *);
+
diff --git a/src/pmns/stdpmid.local b/src/pmns/stdpmid.local
new file mode 100644
index 0000000..6d55daa
--- /dev/null
+++ b/src/pmns/stdpmid.local
@@ -0,0 +1,5 @@
+# Domain numbers for local PMDAs
+#
+# PMDA Name Domain Number
+#
+#EXAMPLE 123
diff --git a/src/pmns/stdpmid.pcp b/src/pmns/stdpmid.pcp
new file mode 100644
index 0000000..1296c81
--- /dev/null
+++ b/src/pmns/stdpmid.pcp
@@ -0,0 +1,128 @@
+# Domain numbers for PMDAs in the base PCP product
+#
+# PMDA Name Domain Number
+#
+IRIX 1
+PMCD 2
+PROC 3
+ENVIRON 4
+CISCO 5
+HIPPI 6
+HOTPROC 7
+NETPROBE 8
+MAILQ 9
+TRACE 10
+XFS 11
+FSAFE 12
+MPI 13
+DMF 14
+SENDMAIL 15
+XVM 16
+BROCADE 17
+ESPPING 18
+SHPING 19
+HPUX 20
+WEBPING 21
+WEBSERVER 22
+ARRAY0 23
+ARRAY1 24
+ARRAY2 25
+ARRAY3 26
+SYSSUMMARY 27
+NEWS 28
+SAMPLE 29
+SAMPLEDSO 30
+KERN_DEV 31
+ORACLE 32
+SYBASE 40
+INFORMIX 48
+SNMP 56
+LOCKSTAT 59
+LINUX 60
+MYRINET 61
+NFSCLIENT 62
+SNIA 63
+CLUSTER 65
+MYSQL 66
+LOGTAIL 67
+APACHE 68
+ROOMTEMP 69
+MMV 70
+PROCESS 71
+MOUNTS 72
+SNIFF 73
+LMSENSORS 74
+SOLARIS 75
+SAMBA 76
+NFS 77
+DARWIN 78
+WINDOWS 79
+AIX 80
+TG3 81
+CXFS 82
+FCSW 83
+CPUSET 84
+FREEBSD 85
+RPCBIND 86
+ETW 87
+SYSTEMTAP 88
+MEMCACHE 89
+VMWARE 90
+IB 91
+ISCSI 92
+LUSTRECOMM 93
+SDR 94
+KVM 95
+BONDING 96
+NETFILTER 97
+ZIMBRA 98
+UV 99
+NAMED 100
+PDNS 101
+DTSRUN 102
+POSTFIX 103
+IPMI 104
+GPSD 105
+LOGGER 106
+RSYSLOG 107
+ELASTICSEARCH 108
+MSSQL 109
+POSTGRESQL 110
+CTDB 111 /* Clustered Trivial Database (samba.org) */
+BASH 112
+FIBRECHANNEL 113
+SYSTEMD 114
+GFS2 115
+NETBSD 116
+NGINX 117
+GLUSTER 118
+PANASAS 119
+NVML 120
+CIFS 121
+JBD2 122
+RPM 123
+QPID 124
+ZSWAP 125
+PAPI 126
+PERFEVENT 127
+PIPE 128
+DMCACHE 129
+### NEXT FREE SLOT ###
+SLOW_PYTHON 242
+SLOW 243
+DBPING 244
+PMI_DOMAIN 245
+LAB 246
+DYNAMIC 247
+TXMON 248
+BROKEN 249
+TRIVIAL 250
+SYBPING 251
+INFMXPING 252
+SIMPLE 253
+ORAPING 254
+### MORE FREE SLOTS ###
+#
+# 511 is REALLY reserved ... see DYNAMIC_PMID in impl.h
+#
+RESERVED_DO_NOT_USE 511
diff --git a/src/pmns/unlockpmns b/src/pmns/unlockpmns
new file mode 100644
index 0000000..69d094c
--- /dev/null
+++ b/src/pmns/unlockpmns
@@ -0,0 +1,30 @@
+#!/bin/sh
+#
+# 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
+#
+# Unlock the PMNS for concurrent transactional updates
+#
+
+. $PCP_DIR/etc/pcp.env
+
+lock=${1-$PCP_VAR_DIR/pmns/root}.lock
+
+#DEBUG# echo "pmnsunlock: `date; ls -li $lock`"
+
+rm -f $lock
+
+exit 0