Update to pcp-6.3.7-5 with recent important fixes

Resolves: RHEL-93186
Resolves: RHEL-54039
This commit is contained in:
Nathan Scott 2025-06-27 10:35:41 +10:00
parent fb457f85a8
commit 14f54a75ff
8 changed files with 768 additions and 285 deletions

View File

@ -1,6 +1,6 @@
Name: pcp
Version: 6.3.7
Release: 4%{?dist}
Release: 5%{?dist}
Summary: System-level performance monitoring and performance management
License: GPL-2.0-or-later AND LGPL-2.1-or-later AND CC-BY-3.0
URL: https://pcp.io
@ -10,10 +10,14 @@ Source0: https://github.com/performancecopilot/pcp/releases/pcp-%{version}.src.t
# Keep xx-default-archive-version.patch for the life of RHEL9
Patch0: redhat-issues-RHEL-2317-default-archive-version.patch
Patch1: redhat-issues-RHEL-58953-perl-drop-Y2038-checks.patch
Patch2: fix-pmdabpf-noarch-man-page-build-failure.patch
Patch3: selinux-proc_psi_t.patch
Patch4: pcp-filter-exact.patch
Patch5: pcp_openmetrics.patch
Patch2: selinux-proc_psi_t.patch
Patch3: selinux-pcp_pmie_t.patch
Patch4: pmwebapi-filter-exact.patch
Patch5: pmda-openmetrics-rollup.patch
Patch6: pmapi-header-multilib-fix.patch
Patch7: python-pmda-wrapper-list-fix.patch
Patch8: systemd-tmpfiles.d-directories.patch
Patch9: fix-pmdabpf-noarch-man-page-build-failure.patch
%if 0%{?fedora} >= 40 || 0%{?rhel} >= 10
ExcludeArch: %{ix86}
@ -369,6 +373,8 @@ Requires: pcp-selinux = %{version}-%{release}
%global _ieconfdir %{_localstatedir}/lib/pcp/config/pmieconf
%global _selinuxdir %{_datadir}/selinux/packages/targeted
%global _with_multilib --enable-multilib=true
%if 0%{?fedora} >= 20 || 0%{?rhel} >= 8
%global _with_doc --with-docdir=%{_docdir}/%{name}
%endif
@ -2504,7 +2510,7 @@ sed -i "/PACKAGE_BUILD/s/=[0-9]*/=$_build/" VERSION.pcp
%if !%{disable_python2} && 0%{?default_python} != 3
export PYTHON=python%{?default_python}
%endif
%configure %{?_with_initd} %{?_with_doc} %{?_with_dstat} %{?_with_ib} %{?_with_gfs2} %{?_with_statsd} %{?_with_perfevent} %{?_with_bcc} %{?_with_bpf} %{?_with_bpftrace} %{?_with_json} %{?_with_mongodb} %{?_with_mysql} %{?_with_snmp} %{?_with_nutcracker} %{?_with_python2}
%configure %{?_with_multilib} %{?_with_initd} %{?_with_doc} %{?_with_dstat} %{?_with_ib} %{?_with_gfs2} %{?_with_statsd} %{?_with_perfevent} %{?_with_bcc} %{?_with_bpf} %{?_with_bpftrace} %{?_with_json} %{?_with_mongodb} %{?_with_mysql} %{?_with_snmp} %{?_with_nutcracker} %{?_with_python2}
make %{?_smp_mflags} default_pcp
%install
@ -3247,7 +3253,11 @@ for PMDA in dm nfsclient openmetrics ; do
fi
done
# managed via /usr/lib/systemd/system-preset/90-default.preset nowadays:
%if 0%{?rhel} > 0 && 0%{?rhel} < 10
%if 0%{?fedora} > 40 || 0%{?rhel} > 9
for s in pmcd pmlogger pmie; do
systemctl --quiet is-enabled $s && systemctl restart $s >/dev/null 2>&1
done
%else # old-school methods follow
%if !%{disable_systemd}
systemctl restart pmcd pmlogger pmie >/dev/null 2>&1
systemctl enable pmcd pmlogger pmie >/dev/null 2>&1
@ -3618,6 +3628,12 @@ fi
%files zeroconf -f pcp-zeroconf-files.rpm
%changelog
* Fri Jun 27 2025 Nathan Scott <nathans@redhat.com> - 6.3.7-5
- Backport some more fixes to the OpenMetrics PMDA (RHEL-54039)
- Fix a multilib regression in PCP header files (RHEL-93186)
- Fix python PMDA wrapper handling of list objects
- Improve tmpfiles.d handling of empty directories
* Wed Apr 30 2025 Lauren Chilton <lchilton@redhat.com> - 6.3.7-4
- Backport metric removal for pmdaopenmetrics

View File

@ -1,278 +0,0 @@
commit 26e7f6e58be9237c2fd37ae8a98b7941f0ff1345
Author: lmchilton <lauren.chilton26@gmail.com>
Date: Mon Apr 14 10:46:11 2025 -0400
added logic to support metric removal in pmdaopenmetrics.
A bug was discovered during development: the PMDA
did not support metric re-addition. So, logic was
added to address that scenario. Additionally, QA test
1976 was added to the testsuite.
Addressed changes from PR review. Tested with a script and found/fixed a bug.
diff --git a/qa/1976 b/qa/1976
new file mode 100755
index 000000000..1427ef119
--- /dev/null
+++ b/qa/1976
@@ -0,0 +1,90 @@
+#!/bin/sh
+# PCP QA Test No. 1976
+# test pmdaopenmetrics metric removal
+#
+# Copyright (c) 2017, 2025 Red Hat. All Rights Reserved.
+#
+# Note: if anything gets added or changed in qa/openmetrics/samples directory,
+# then this test (and all tests in group pmda.openmetrics) will need to be remade.
+
+seq=`basename $0`
+echo "QA output created by $seq"
+
+# get standard environment, filters and checks
+. ./common.openmetrics
+
+_pmdaopenmetrics_check || _notrun "openmetrics pmda not installed"
+
+status=1 # failure is the default!
+
+_cleanup()
+{
+ cd $here
+ _pmdaopenmetrics_cleanup
+ $sudo rm -rf $tmp $tmp.*
+}
+
+_prepare_pmda openmetrics
+trap "_cleanup; exit \$status" 0 1 2 3 15
+_stop_auto_restart pmcd
+
+_pmdaopenmetrics_save_config
+
+# add all the sample text files as urls.
+# need to be a place the user $PCP_USER (pmcd) can read
+#
+( cd $here/openmetrics/samples; ls -1 *.txt ) | sort | while read file
+do
+ cp $here/openmetrics/samples/$file $tmp.$file
+ urlbase=`basename "$file" .txt | tr .- _`
+ echo 'file://'$tmp.$file >$tmp.tmp
+ $sudo cp $tmp.tmp $PCP_PMDAS_DIR/openmetrics/config.d/$urlbase.url
+done
+ls -l $PCP_PMDAS_DIR/openmetrics/config.d >>$seq_full
+
+# add all the sample scripts
+cp -a $here/openmetrics/scripts/* $PCP_PMDAS_DIR/openmetrics/config.d
+find $PCP_PMDAS_DIR/openmetrics/config.d -name GNU\* -exec rm -f {} ";"
+
+_pmdaopenmetrics_install
+
+if ! _pmdaopenmetrics_wait_for_metric openmetrics.thermostat
+then
+ status=1
+ exit
+fi
+
+echo "-- metric removal of new source/metric --"
+$sudo rm $PCP_PMDAS_DIR/openmetrics/config.d/simple_metric.url
+if pminfo openmetrics | grep openmetrics.simple_metric
+then
+ echo "metric removal failed..exiting"
+ status=1
+ exit
+else
+ echo "metric removal success"
+fi
+echo
+
+echo "-- source re-addition --"
+path=$here/openmetrics/samples/simple_metric.txt
+echo 'file:///'$path > $PCP_PMDAS_DIR/openmetrics/config.d/simple_metric.url
+pminfo openmetrics.simple_metric
+echo
+
+echo "-- metric removal of recognized source/metric --"
+$sudo rm $PCP_PMDAS_DIR/openmetrics/config.d/simple_metric.url
+if pminfo openmetrics | grep openmetrics.simple_metric
+then
+ echo "metric removal failed..exiting"
+ status=1
+ exit
+else
+ echo "metric removal success"
+fi
+
+_pmdaopenmetrics_remove >/dev/null 2>&1
+
+# success, all done
+status=0
+exit
diff --git a/qa/1976.out b/qa/1976.out
new file mode 100644
index 000000000..bcfe03f8d
--- /dev/null
+++ b/qa/1976.out
@@ -0,0 +1,11 @@
+QA output created by 1976
+
+=== openmetrics agent installation ===
+-- metric removal of new source/metric --
+metric removal success
+
+-- source re-addition --
+openmetrics.simple_metric.metric1
+
+-- metric removal of recognized source/metric --
+metric removal success
diff --git a/qa/group b/qa/group
index 55828846a..83c8f2e8a 100644
--- a/qa/group
+++ b/qa/group
@@ -2216,6 +2216,7 @@ pmcd.pdu
1963 pmda.linux local
1970 pmda.bpf local
1973 pcp zoneinfo python local
+1976 pmdaopenmetrics python local
1978 atop local pmlogrewrite
1984 pmlogconf pmda.redis local
1985 pmfind local valgrind
diff --git a/src/pmdas/openmetrics/pmdaopenmetrics.python b/src/pmdas/openmetrics/pmdaopenmetrics.python
index c33a56400..a9e65af93 100755
--- a/src/pmdas/openmetrics/pmdaopenmetrics.python
+++ b/src/pmdas/openmetrics/pmdaopenmetrics.python
@@ -127,15 +127,9 @@ class Metric(object):
(name, pmContext.pmIDStr(self.pmid), self.mtype, self.msem, self.singular, self.mindom, self.labels))
self.obj = pmdaMetric(self.pmid, self.mtype, self.mindom, self.msem, self.munits)
+ self.source.pmda.all_metrics[self.mname] = self.obj
- if helpline: # it could be None!
- unescaped = helpline.replace('\\\\', '\\').replace('\\n', '\n')
- split = unescaped.split('\n')
- help_oneline = split[0] # must have at least one entry
- help_text = '\n'.join(split[1:]) # may have other entries
- else:
- help_oneline = ''
- help_text = ''
+ help_text, help_oneline = self.source.helptext(helpline)
try:
self.source.pmda.add_metric(self.mname, self.obj, help_oneline, help_text)
@@ -588,6 +582,16 @@ class Source(object):
self.metrics_by_name = {} # name -> Metric
self.metrics_by_num = {} # number (last component of pmid) -> Metric
+ def helptext(self, helpline):
+ if helpline: # it could be None!
+ unescaped = helpline.replace('\\\\', '\\').replace('\\n', '\n')
+ split = unescaped.split('\n')
+ help_oneline = split[0] # must have at least one entry
+ help_text = '\n'.join(split[1:]) # may have other entries
+ else:
+ help_oneline = ''
+ help_text = ''
+ return help_text, help_oneline
def old_enough_for_refresh(self):
'''But what is "old"? If it is empty (no metrics), then it
@@ -685,6 +689,23 @@ class Source(object):
self.pmda.debug("included_labels '%s'" % (included_labels)) if self.pmda.dbg else None
self.pmda.debug("optional_labels '%s'" % (optional_labels)) if self.pmda.dbg else None
if sp.name in self.metrics_by_name:
+ if ("openmetrics.%s.%s" % (self.name, sp.name)) not in self.pmda.all_metrics and self.name in self.pmda.re_add_list:
+ # re-add metric to namespace
+ if pcpline:
+ split = pcpline.split(" ")
+ fullname = "openmetrics.%s.%s" % (self.name, split[1])
+ else:
+ fullname = "openmetrics.%s.%s" % (self.name, sp.name.replace(":", "."))
+ help_oneline, help_text = self.helptext(helpline)
+ try:
+ obj = self.pmda.removed_metrics[fullname]
+ self.pmda.add_metric(fullname, obj, help_oneline, help_text)
+ self.pmda.debug("re-adding metric: %s to namespace" % fullname) if self.pmda.dbg else None
+ self.pmda.all_metrics[fullname] = obj
+ del self.pmda.removed_metrics[fullname]
+ self.pmda.set_need_refresh()
+ except Exception as e:
+ self.pmda.debug("Can't re-add metric: %s, see error: %s" % (fullname, e)) if self.pmda.dbg else None
m = self.metrics_by_name[sp.name]
assert self.metrics_by_num[m.metricnum] == m
if m.singular:
@@ -693,6 +714,7 @@ class Source(object):
else:
m.store_inst(naming_labels, sp.value)
self.pmda.debug("naming_labels '%s'" % (naming_labels)) if self.pmda.dbg else None
+ # new metric case
else:
# check metric is not excluded by filters
fullname = "openmetrics.%s.%s" % (self.name, sp.name)
@@ -1014,6 +1036,10 @@ class OpenMetricsPMDA(PMDA):
reserved_cluster = self.cluster_table.intern_lookup_value("control")
assert reserved_cluster == 0
self.source_by_cluster = {}
+ # all metrics added, to be used for removal
+ self.all_metrics = {}
+ # keep track of removed metrics, in case of re-addition
+ self.removed_metrics = {}
# compiled regex cache
self.regex_cache = {}
@@ -1148,8 +1174,40 @@ class OpenMetricsPMDA(PMDA):
self.log("Config change detected, traversed %d config entries in %.04fs, rescanning ..." % (len(conf_filelist), traverse_time))
nickname_regexp = self.lookup_regex(r"^[A-Za-z][A-Za-z0-9_.]*$")
+ self.re_add_list = []
+
+ # calculate config entry nicknames
+ nicknames = []
+ for file in conf_filelist:
+ file_split = os.path.splitext(file)
+ name = file_split[0].replace(self.config_dir + "/", "").replace("/", ".")
+ nicknames.append(name)
+
+ # check if config change adds a previously removed source
+ for key in self.removed_metrics:
+ split_name = key.split(".")
+ if split_name[1] in nicknames:
+ self.re_add_list.append(split_name[1])
+
+ # if source is not in config directory, remove the metric
+ for key, value in self.all_metrics.items():
+ split_name = key.split(".")
+ if split_name[1] in nicknames:
+ continue
+ try:
+ remove_name = key
+ remove_obj = value
+ self.remove_metric(remove_name, remove_obj)
+ self.removed_metrics[remove_name] = remove_obj
+ self.debug("removed metric name: %s" % remove_name) if self.dbg else None
+ self.set_need_refresh()
+ except Exception as e:
+ self.debug("can't remove metric: %s, see error: %s" % (key, e)) if self.dbg else None
+
+ for key in self.removed_metrics:
+ if key in self.all_metrics:
+ del self.all_metrics[key]
- # TODO: maybe nuke sources related to removed files
save_cluster_table = False
if sort_conf_list:
# sorted for indom cluster consistency
@@ -1177,6 +1235,16 @@ class OpenMetricsPMDA(PMDA):
if name in self.source_by_name:
# this source is already known
self.assert_source_invariants(name=name)
+ s = self.source_by_name[name]
+ cluster_for_refresh = []
+ cluster_for_refresh_names = []
+ if name in self.re_add_list:
+ for key,value in self.source_by_cluster.items():
+ if value == s:
+ cluster_for_refresh.append(key)
+ cluster_for_refresh_names.append(name)
+ self.debug("refreshing cluster list: %s" % cluster_for_refresh_names) if self.dbg else None
+ self.refresh_some_clusters_for_fetch(cluster_for_refresh)
else:
try:
path = file

View File

@ -0,0 +1,202 @@
diff -Naurp pcp-6.3.7.orig/configure pcp-6.3.7/configure
--- pcp-6.3.7.orig/configure 2025-03-31 12:41:26.000000000 +1100
+++ pcp-6.3.7/configure 2025-06-26 19:38:57.864414825 +1000
@@ -932,6 +932,7 @@ sed
SED
awk
AWK
+enable_multilib
enable_shared
cxx
ac_ct_CXX
@@ -1055,6 +1056,7 @@ enable_ssp
enable_pie
enable_visibility
enable_shared
+enable_multilib
with_perl_installdirs
with_perl_install_base
with_python_prefix
@@ -1748,7 +1750,8 @@ Optional Features:
--disable-ssp disable gcc stack-protector
--disable-pie disable position-independent-executable
--disable-visibility disable gcc symbol visibility
- --disable-shared disable core shared libary generation
+ --disable-shared disable core shared library generation
+ --enable-multilib enable multilib installations
Optional Packages:
--with-PACKAGE[=ARG] use PACKAGE [ARG=yes]
@@ -5696,6 +5699,21 @@ else case e in #(
esac
fi
+
+
+# Check whether --enable-multilib was given.
+if test ${enable_multilib+y}
+then :
+ enableval=$enable_multilib; PACKAGE_CONFIGURE="$PACKAGE_CONFIGURE --enable-multilib=$withval"
+
+printf "%s\n" "#define HAVE_MULTILIB 1" >>confdefs.h
+
+ enable_multilib=true
+else case e in #(
+ e) enable_multilib=false ;;
+esac
+fi
+
diff -Naurp pcp-6.3.7.orig/configure.ac pcp-6.3.7/configure.ac
--- pcp-6.3.7.orig/configure.ac 2025-03-31 12:41:26.000000000 +1100
+++ pcp-6.3.7/configure.ac 2025-06-26 19:38:57.867577540 +1000
@@ -655,11 +655,20 @@ AC_LANG_POP([C++])
dnl Prevent shared libraries from being built for libpcp and other core libraries
AC_ARG_ENABLE([shared],
- [AS_HELP_STRING([--disable-shared], [disable core shared libary generation])],
+ [AS_HELP_STRING([--disable-shared], [disable core shared library generation])],
[PACKAGE_CONFIGURE="$PACKAGE_CONFIGURE --disable-shared=$withval"],
[enable_shared=false])
AC_SUBST(enable_shared)
+dnl Support installing both devel package variants on either 32/64 bit platform
+AC_ARG_ENABLE([multilib],
+ [AS_HELP_STRING([--enable-multilib], [enable multilib installations])],
+ [PACKAGE_CONFIGURE="$PACKAGE_CONFIGURE --enable-multilib=$withval"
+ AC_DEFINE(HAVE_MULTILIB, [1], [multilib defined])
+ enable_multilib=true],
+ [enable_multilib=false])
+AC_SUBST(enable_multilib)
+
dnl check if user wants to use any of their own commands;
dnl ordering is important: some tests use earlier results
diff -Naurp pcp-6.3.7.orig/src/include/builddefs.in pcp-6.3.7/src/include/builddefs.in
--- pcp-6.3.7.orig/src/include/builddefs.in 2025-03-27 17:01:59.000000000 +1100
+++ pcp-6.3.7/src/include/builddefs.in 2025-06-26 19:38:57.869743878 +1000
@@ -259,6 +259,7 @@ QT_VERSION = @qt_version@
CLANG_MAJOR_VERSION = @CLANG_MAJOR_VERSION@
# configuration state affecting the entire build
+ENABLE_MULTILIB = @enable_multilib@
ENABLE_SHARED = @enable_shared@
ENABLE_SECURE = @enable_secure@
ENABLE_PROBES = @enable_probes@
diff -Naurp pcp-6.3.7.orig/src/include/pcp/config32.h pcp-6.3.7/src/include/pcp/config32.h
--- pcp-6.3.7.orig/src/include/pcp/config32.h 2024-07-30 16:43:55.000000000 +1000
+++ pcp-6.3.7/src/include/pcp/config32.h 2025-06-26 19:38:57.871096964 +1000
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2014,2016 Red Hat.
+ * Copyright (c) 2014,2016,2025 Red Hat.
* Headers for "multilib" support (32-bit and 64-bit packages co-existing)
*
* This library is free software; you can redistribute it and/or modify it
@@ -19,5 +19,7 @@
#define HAVE_32BIT_LONG 1
#define HAVE_32BIT_PTR 1
/* #undef HAVE_64BIT_PTR */
+#define PM_PAD_RESULT 4
+#define PM_PAD_TIMESPEC 4
#endif /* PCP_CONFIG32_H */
diff -Naurp pcp-6.3.7.orig/src/include/pcp/config64.h pcp-6.3.7/src/include/pcp/config64.h
--- pcp-6.3.7.orig/src/include/pcp/config64.h 2024-07-30 16:43:55.000000000 +1000
+++ pcp-6.3.7/src/include/pcp/config64.h 2025-06-26 19:38:57.871276548 +1000
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2014,2016 Red Hat.
+ * Copyright (c) 2014,2016,2025 Red Hat.
* Headers for "multilib" support (32-bit and 64-bit packages co-existing)
*
* This library is free software; you can redistribute it and/or modify it
@@ -19,5 +19,7 @@
/* #undef HAVE_32BIT_LONG */
/* #undef HAVE_32BIT_PTR */
#define HAVE_64BIT_PTR 1
+/* #undef PM_PAD_RESULT */
+/* #undef PM_PAD_TIMESPEC */
#endif /* PCP_CONFIG64_H */
diff -Naurp pcp-6.3.7.orig/src/include/pcp/config.h.in pcp-6.3.7/src/include/pcp/config.h.in
--- pcp-6.3.7.orig/src/include/pcp/config.h.in 2025-03-14 14:41:06.000000000 +1100
+++ pcp-6.3.7/src/include/pcp/config.h.in 2025-06-26 19:38:57.871702924 +1000
@@ -792,6 +792,9 @@
#undef HAVE__ETEXT
#undef HAVE_ETEXT
+/* multilib headers */
+#undef HAVE_MULTILIB
+
/* sizeof suseconds_t ... only ever going to exist on linux-like systems */
#undef PM_SIZEOF_SUSECONDS_T
@@ -801,9 +804,3 @@
#ifndef PM_SIZEOF_TIME_T
#error Unknown time_t size
#endif
-
-/* __pmResult padding */
-#undef PM_PAD_RESULT
-
-/* timespec padding */
-#undef PM_PAD_TIMESPEC
diff -Naurp pcp-6.3.7.orig/src/include/pcp/configsz.h.in pcp-6.3.7/src/include/pcp/configsz.h.in
--- pcp-6.3.7.orig/src/include/pcp/configsz.h.in 2024-07-30 16:43:55.000000000 +1000
+++ pcp-6.3.7/src/include/pcp/configsz.h.in 2025-06-26 19:38:57.872742426 +1000
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2014-2017 Red Hat.
+ * Copyright (c) 2014-2017,2025 Red Hat.
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published
@@ -26,4 +26,10 @@
/* pointer size */
#undef HAVE_64BIT_PTR
+/* __pmResult padding */
+#undef PM_PAD_RESULT
+
+/* timespec padding */
+#undef PM_PAD_TIMESPEC
+
#endif /* PCP_CONFIGSZ_H */
diff -Naurp pcp-6.3.7.orig/src/include/pcp/GNUmakefile pcp-6.3.7/src/include/pcp/GNUmakefile
--- pcp-6.3.7.orig/src/include/pcp/GNUmakefile 2024-07-30 16:43:55.000000000 +1000
+++ pcp-6.3.7/src/include/pcp/GNUmakefile 2025-06-26 19:38:57.873157593 +1000
@@ -22,11 +22,16 @@ SDSH_HFILES = sds.h sdsalloc.h
EXTERNAL_HFILES = dict.h
HFILES = pmapi.h impl.h pmda.h pmtime.h pmdaroot.h pmafm.h \
trace.h trace_dev.h mmv_stats.h mmv_dev.h import.h \
- config32.h config64.h platform32.h platform64.h \
pmjson.h pmhttp.h pmdbg.h pmwebapi.h deprecated.h \
ini.h sds.h dict.h archive.h
-INFILES = config.h.in configsz.h.in platform_defs.h.in platformsz.h.in
-CONFFILES = config.h configsz.h platform_defs.h platformsz.h
+INFILES = config.h.in platform_defs.h.in
+CONFFILES = config.h platform_defs.h
+ifeq "$(ENABLE_MULTILIB)" "true"
+HFILES += config32.h config64.h platform32.h platform64.h
+else
+INFILES += configsz.h.in platformsz.h.in
+CONFFILES += configsz.h platformsz.h
+endif
GENERATED_HFILES = $(CONFFILES)
NOSHIP_HFILES = libpcp.h sdsalloc.h
diff -Naurp pcp-6.3.7.orig/src/include/pcp/platform_defs.h.in pcp-6.3.7/src/include/pcp/platform_defs.h.in
--- pcp-6.3.7.orig/src/include/pcp/platform_defs.h.in 2025-03-31 12:41:26.000000000 +1100
+++ pcp-6.3.7/src/include/pcp/platform_defs.h.in 2025-06-26 19:38:57.873423552 +1000
@@ -36,7 +36,7 @@ extern "C" {
#define FMT_PID "@fmt_pid@"
#define FMT_PTHREAD "@fmt_pthread@"
-#ifdef HAVE_BITS_WORDSIZE_H
+#if defined(HAVE_MULTILIB) && defined(HAVE_BITS_WORDSIZE_H)
#include <bits/wordsize.h>
#if __WORDSIZE == 32
#include "config32.h"

View File

@ -0,0 +1,465 @@
diff -Naurp pcp-6.3.7.orig/qa/1976 pcp-6.3.7/qa/1976
--- pcp-6.3.7.orig/qa/1976 1970-01-01 10:00:00.000000000 +1000
+++ pcp-6.3.7/qa/1976 2025-06-26 18:02:39.313416612 +1000
@@ -0,0 +1,85 @@
+#!/bin/sh
+# PCP QA Test No. 1976
+# Test pmdaopenmetrics metric removal
+#
+# Copyright (c) 2017, 2025 Red Hat. All Rights Reserved.
+#
+seq=`basename $0`
+echo "QA output created by $seq"
+
+# get standard environment, filters and checks
+. ./common.openmetrics
+
+_pmdaopenmetrics_check || _notrun "openmetrics pmda not installed"
+
+status=1 # failure is the default!
+
+_cleanup()
+{
+ cd $here
+ _pmdaopenmetrics_cleanup
+ $sudo rm -rf $tmp $tmp.*
+}
+
+_prepare_pmda openmetrics
+trap "_cleanup; exit \$status" 0 1 2 3 15
+_stop_auto_restart pmcd
+
+_pmdaopenmetrics_save_config
+
+# add all the sample text files as urls.
+# need to be a place the user $PCP_USER (pmcd) can read
+#
+( cd $here/openmetrics/samples; ls -1 *.txt ) | sort | while read file
+do
+ cp $here/openmetrics/samples/$file $tmp.$file
+ urlbase=`basename "$file" .txt | tr .- _`
+ echo 'file://'$tmp.$file >$tmp.tmp
+ $sudo cp $tmp.tmp $PCP_PMDAS_DIR/openmetrics/config.d/$urlbase.url
+done
+ls -l $PCP_PMDAS_DIR/openmetrics/config.d >>$seq_full
+
+# add all the sample scripts
+cp -a $here/openmetrics/scripts/* $PCP_PMDAS_DIR/openmetrics/config.d
+find $PCP_PMDAS_DIR/openmetrics/config.d -name GNU\* -exec rm -f {} ";"
+
+_pmdaopenmetrics_install
+
+if ! _pmdaopenmetrics_wait_for_metric openmetrics.thermostat
+then
+ status=1
+ exit
+fi
+
+echo "-- metric removal of new source/metric --"
+$sudo rm $PCP_PMDAS_DIR/openmetrics/config.d/simple_metric.url
+pminfo openmetrics.simple_metric
+echo
+
+echo "-- source re-addition --"
+# same access controls logic as above, user $PCP_USER needs to be
+# able to read the file at the end of the URL
+#
+cp $here/openmetrics/samples/simple_metric.txt $tmp.simple_metric.txt
+echo 'file:///'$tmp.simple_metric.txt >$PCP_PMDAS_DIR/openmetrics/config.d/simple_metric.url
+pminfo openmetrics.simple_metric
+echo
+
+echo "-- metric removal of recognized source/metric --"
+$sudo rm $PCP_PMDAS_DIR/openmetrics/config.d/simple_metric.url
+pminfo openmetrics.simple_metric
+echo
+
+echo "-- source re-addition with epoch timestamp --"
+txtpath=$here/openmetrics/samples/simple_metric.txt
+urlfile=$PCP_PMDAS_DIR/openmetrics/config.d/simple_metric.url
+echo 'file:///'$txtpath > $urlfile
+$sudo touch -t 197001010000 $urlfile
+pminfo openmetrics.simple_metric
+echo
+
+_pmdaopenmetrics_remove >/dev/null 2>&1
+
+# success, all done
+status=0
+exit
diff -Naurp pcp-6.3.7.orig/qa/1976.out pcp-6.3.7/qa/1976.out
--- pcp-6.3.7.orig/qa/1976.out 1970-01-01 10:00:00.000000000 +1000
+++ pcp-6.3.7/qa/1976.out 2025-06-26 18:02:01.879861640 +1000
@@ -0,0 +1,17 @@
+QA output created by 1976
+
+=== openmetrics agent installation ===
+-- metric removal of new source/metric --
+Error: openmetrics.simple_metric: Unknown metric name
+
+-- source re-addition --
+openmetrics.simple_metric.metric2
+openmetrics.simple_metric.metric1
+
+-- metric removal of recognized source/metric --
+Error: openmetrics.simple_metric: Unknown metric name
+
+-- source re-addition with epoch timestamp --
+openmetrics.simple_metric.metric2
+openmetrics.simple_metric.metric1
+
diff -Naurp pcp-6.3.7.orig/qa/group pcp-6.3.7/qa/group
--- pcp-6.3.7.orig/qa/group 2025-06-26 17:55:37.790462638 +1000
+++ pcp-6.3.7/qa/group 2025-06-26 17:58:18.996389423 +1000
@@ -2205,6 +2205,7 @@ pmcd.pdu
1963 pmda.linux local
1970 pmda.bpf local
1973 pcp zoneinfo python local
+1976 pmdaopenmetrics python local
1978 atop local pmlogrewrite
1984 pmlogconf pmda.redis local
1985 pmfind local valgrind
diff -Naurp pcp-6.3.7.orig/qa/openmetrics/scripts/curl/script.sh pcp-6.3.7/qa/openmetrics/scripts/curl/script.sh
--- pcp-6.3.7.orig/qa/openmetrics/scripts/curl/script.sh 2023-11-16 17:51:39.000000000 +1100
+++ pcp-6.3.7/qa/openmetrics/scripts/curl/script.sh 2025-06-26 18:02:50.155355973 +1000
@@ -1,4 +1,4 @@
#! /bin/sh
. /etc/pcp.conf
-curl -Gq file://$PCP_PMDAS_DIR/openmetrics/config.d/some_metric.txt
+curl -Gqs file://$PCP_PMDAS_DIR/openmetrics/config.d/some_metric.txt
diff -Naurp pcp-6.3.7.orig/qa/openmetrics/scripts/curl_filtered.sh pcp-6.3.7/qa/openmetrics/scripts/curl_filtered.sh
--- pcp-6.3.7.orig/qa/openmetrics/scripts/curl_filtered.sh 2023-11-16 17:51:39.000000000 +1100
+++ pcp-6.3.7/qa/openmetrics/scripts/curl_filtered.sh 2025-06-26 18:02:50.155694515 +1000
@@ -7,6 +7,6 @@
. /etc/pcp.conf
-( curl -Gq file://$PCP_PMDAS_DIR/openmetrics/config.d/some_metric.txt ; \
-curl -Gq file://$PCP_PMDAS_DIR/openmetrics/config.d/some_other_metric.txt) \
+( curl -Gqs file://$PCP_PMDAS_DIR/openmetrics/config.d/some_metric.txt ; \
+curl -Gqs file://$PCP_PMDAS_DIR/openmetrics/config.d/some_other_metric.txt) \
| sed -e '/metric2/d'
diff -Naurp pcp-6.3.7.orig/qa/openmetrics/scripts/curl_hostname_label.sh pcp-6.3.7/qa/openmetrics/scripts/curl_hostname_label.sh
--- pcp-6.3.7.orig/qa/openmetrics/scripts/curl_hostname_label.sh 2023-11-16 17:51:39.000000000 +1100
+++ pcp-6.3.7/qa/openmetrics/scripts/curl_hostname_label.sh 2025-06-26 18:02:50.155942641 +1000
@@ -12,5 +12,5 @@
# here for QA purposes we're fetching from a local file
# and just pretending it came from a remote host.
-curl -q -G file://$PCP_PMDAS_DIR/openmetrics/config.d/some_metric.txt 2>/dev/null | \
+curl -Gqs file://$PCP_PMDAS_DIR/openmetrics/config.d/some_metric.txt 2>/dev/null | \
sed -e 's/[a-z0-9]*=/hostname="remotehost",&/'
diff -Naurp pcp-6.3.7.orig/qa/openmetrics/scripts/curl_scripted.sh pcp-6.3.7/qa/openmetrics/scripts/curl_scripted.sh
--- pcp-6.3.7.orig/qa/openmetrics/scripts/curl_scripted.sh 2023-11-16 17:51:39.000000000 +1100
+++ pcp-6.3.7/qa/openmetrics/scripts/curl_scripted.sh 2025-06-26 18:02:50.156229975 +1000
@@ -1,5 +1,5 @@
#! /bin/sh
. /etc/pcp.conf
-curl -Gq file://$PCP_PMDAS_DIR/openmetrics/config.d/some_metric.txt
-curl -Gq file://$PCP_PMDAS_DIR/openmetrics/config.d/some_other_metric.txt
+curl -Gqs file://$PCP_PMDAS_DIR/openmetrics/config.d/some_metric.txt
+curl -Gqs file://$PCP_PMDAS_DIR/openmetrics/config.d/some_other_metric.txt
diff -Naurp pcp-6.3.7.orig/src/pmdas/openmetrics/pmdaopenmetrics.python pcp-6.3.7/src/pmdas/openmetrics/pmdaopenmetrics.python
--- pcp-6.3.7.orig/src/pmdas/openmetrics/pmdaopenmetrics.python 2025-06-26 17:55:37.790973098 +1000
+++ pcp-6.3.7/src/pmdas/openmetrics/pmdaopenmetrics.python 2025-06-26 18:02:26.571388227 +1000
@@ -32,7 +32,8 @@ import subprocess
import sys
from ctypes import c_int
from socket import gethostname
-from stat import ST_MODE, S_IXUSR, ST_CTIME
+from urllib.parse import urlparse
+from stat import ST_MODE, S_IXUSR
import requests
from pcp.pmapi import pmUnits, pmContext
@@ -127,15 +128,9 @@ class Metric(object):
(name, pmContext.pmIDStr(self.pmid), self.mtype, self.msem, self.singular, self.mindom, self.labels))
self.obj = pmdaMetric(self.pmid, self.mtype, self.mindom, self.msem, self.munits)
+ self.source.pmda.all_metrics[self.mname] = self.obj
- if helpline: # it could be None!
- unescaped = helpline.replace('\\\\', '\\').replace('\\n', '\n')
- split = unescaped.split('\n')
- help_oneline = split[0] # must have at least one entry
- help_text = '\n'.join(split[1:]) # may have other entries
- else:
- help_oneline = ''
- help_text = ''
+ help_text, help_oneline = self.source.helptext(helpline)
try:
self.source.pmda.add_metric(self.mname, self.obj, help_oneline, help_text)
@@ -565,7 +560,7 @@ class Source(object):
self.path = path # pathname to .url or executable file
self.url = None
self.parse_error = False
- self.parse_url_time = 0 # timestamp of config file when it was last parsed
+ self.parse_time = None # last time config file was parsed
self.is_scripted = is_scripted
self.pmda = thispmda # the shared pmda
self.requests = None
@@ -588,6 +583,16 @@ class Source(object):
self.metrics_by_name = {} # name -> Metric
self.metrics_by_num = {} # number (last component of pmid) -> Metric
+ def helptext(self, helpline):
+ if helpline: # it could be None!
+ unescaped = helpline.replace('\\\\', '\\').replace('\\n', '\n')
+ split = unescaped.split('\n')
+ help_oneline = split[0] # must have at least one entry
+ help_text = '\n'.join(split[1:]) # may have other entries
+ else:
+ help_oneline = ''
+ help_text = ''
+ return help_text, help_oneline
def old_enough_for_refresh(self):
'''But what is "old"? If it is empty (no metrics), then it
@@ -685,6 +690,23 @@ class Source(object):
self.pmda.debug("included_labels '%s'" % (included_labels)) if self.pmda.dbg else None
self.pmda.debug("optional_labels '%s'" % (optional_labels)) if self.pmda.dbg else None
if sp.name in self.metrics_by_name:
+ if ("openmetrics.%s.%s" % (self.name, sp.name)) not in self.pmda.all_metrics and self.name in self.pmda.re_add_list:
+ # re-add metric to namespace
+ if pcpline:
+ split = pcpline.split(" ")
+ fullname = "openmetrics.%s.%s" % (self.name, split[1])
+ else:
+ fullname = "openmetrics.%s.%s" % (self.name, sp.name.replace(":", "."))
+ help_oneline, help_text = self.helptext(helpline)
+ try:
+ obj = self.pmda.removed_metrics[fullname]
+ self.pmda.add_metric(fullname, obj, help_oneline, help_text)
+ self.pmda.debug("re-adding metric: %s to namespace" % fullname) if self.pmda.dbg else None
+ self.pmda.all_metrics[fullname] = obj
+ del self.pmda.removed_metrics[fullname]
+ self.pmda.set_need_refresh()
+ except Exception as e:
+ self.pmda.debug("Can't re-add metric: %s, see error: %s" % (fullname, e)) if self.pmda.dbg else None
m = self.metrics_by_name[sp.name]
assert self.metrics_by_num[m.metricnum] == m
if m.singular:
@@ -693,6 +715,7 @@ class Source(object):
else:
m.store_inst(naming_labels, sp.value)
self.pmda.debug("naming_labels '%s'" % (naming_labels)) if self.pmda.dbg else None
+ # new metric case
else:
# check metric is not excluded by filters
fullname = "openmetrics.%s.%s" % (self.name, sp.name)
@@ -800,9 +823,9 @@ class Source(object):
return num_metrics
- def parse_url_config(self, filepath):
+ def parse_config(self, filepath):
'''
- Parse a URL config file. The first line is always the URL.
+ Parse a configuration file. The first line is always the URL.
Remaining lines are prefixed with a keyword. Supported keywords
include '#' for a comment, 'HEADER:' to add to the header passed
to the headers dict parameter to the get() call. Note the ':' are
@@ -900,12 +923,12 @@ class Source(object):
if not s[ST_MODE] & S_IXUSR:
self.pmda.err("cannot execute script '%s'" % self.path)
return
- elif self.parse_url_time < s[ST_CTIME]:
+ elif self.parse_time is None or self.parse_time < s.st_mtime_ns:
# (re)parse the URL from given file
- self.parse_url_config(self.path)
- self.parse_url_time = s[ST_CTIME]
+ self.parse_config(self.path)
+ self.parse_time = s.st_mtime_ns
except Exception as e:
- self.pmda.err("cannot read %s: %s" % (self.path, e))
+ self.pmda.err("cannot stat %s: %s" % (self.path, e))
return
# fetch the document
@@ -1001,7 +1024,7 @@ class OpenMetricsPMDA(PMDA):
# now everything else may take time
self.pmda_name = pmda_name
self.config_dir = os.path.normpath(config)
- self.config_dir_ctime = None
+ self.config_dir_mtime = 0.0
self.timeout = timeout
# a single central Session that all our sources can concurrently reuse
@@ -1014,6 +1037,11 @@ class OpenMetricsPMDA(PMDA):
reserved_cluster = self.cluster_table.intern_lookup_value("control")
assert reserved_cluster == 0
self.source_by_cluster = {}
+ # all metrics added, to be used for removal
+ self.all_metrics = {}
+ # keep track of removed metrics, in case of re-addition
+ self.removed_metrics = {}
+ self.controls = {0:0}
# compiled regex cache
self.regex_cache = {}
@@ -1106,26 +1134,53 @@ class OpenMetricsPMDA(PMDA):
assert s == self.source_by_name[s.name]
- def traverse(self, directory, ctime):
+ def traverse(self, directory, mtime):
''' Return list of files below dir, recursively '''
ret = []
- m = os.path.getctime(directory)
- if ctime is None or m > ctime:
- ctime = m
+ m = os.path.getmtime(directory)
+ if mtime is None or m > mtime:
+ mtime = m
for path, subdirs, files in os.walk(directory):
for f in files:
if not f.startswith("."):
fname = os.path.join(path, f)
- m = os.path.getctime(fname)
- if ctime is None or m > ctime:
- ctime = m
+ m = os.path.getmtime(fname)
+ if mtime is None or m > mtime:
+ mtime = m
ret.append(fname)
+ fname = os.path.join(path, f)
+ with open(fname, 'r') as name:
+ f_path = name.readline().strip()
+ if f_path.startswith("file:///"):
+ parsed = urlparse(f_path)
+ m = os.path.getmtime(parsed.path)
+ if mtime is None or m > mtime:
+ mtime = m
for d in subdirs:
- m, _ = self.traverse(os.path.join(path, d), ctime)
- if ctime is None or m > ctime:
- ctime = m
- return ctime, ret
+ m, _ = self.traverse(os.path.join(path, d), mtime)
+ if mtime is None or m > mtime:
+ mtime = m
+ return mtime, ret
+
+ def initialize_controls(self, cluster):
+ # initialize statistics
+ self.stats_fetch_calls[cluster] = 0
+ self.stats_fetch_time[cluster] = 0
+ self.stats_parse_time[cluster] = 0
+ self.stats_status[cluster] = "unknown"
+ self.stats_status_code[cluster] = 0
+
+ self.controls[cluster] = 1
+
+ def delete_controls(self, cluster):
+ del self.stats_fetch_calls[cluster]
+ del self.stats_fetch_time[cluster]
+ del self.stats_parse_time[cluster]
+ del self.stats_status[cluster]
+ del self.stats_status_code[cluster]
+
+ self.controls[cluster] = 0
def rescan_confdir(self):
'''Scan the configuration directories for any new .url files
@@ -1138,19 +1193,56 @@ class OpenMetricsPMDA(PMDA):
'''
traverse_time = time.time()
- dir_ctime, conf_filelist = self.traverse(self.config_dir, self.config_dir_ctime)
+ dir_mtime, conf_filelist = self.traverse(self.config_dir, self.config_dir_mtime)
traverse_time = time.time() - traverse_time
- if self.config_dir_ctime is None or self.config_dir_ctime < dir_ctime:
- self.config_dir_ctime = dir_ctime
+ if self.config_dir_mtime < dir_mtime:
+ self.config_dir_mtime = dir_mtime
else: # no new or changed conf files, don't rescan directory
return
self.log("Config change detected, traversed %d config entries in %.04fs, rescanning ..." % (len(conf_filelist), traverse_time))
nickname_regexp = self.lookup_regex(r"^[A-Za-z][A-Za-z0-9_.]*$")
+ self.re_add_list = []
+
+ # calculate config entry nicknames
+ nicknames = []
+ for file in conf_filelist:
+ file_split = os.path.splitext(file)
+ name = file_split[0].replace(self.config_dir + "/", "").replace("/", ".")
+ nicknames.append(name)
+
+ # check if config change adds a previously removed source
+ for key in self.removed_metrics:
+ split_name = key.split(".")
+ if split_name[1] in nicknames:
+ self.re_add_list.append(split_name[1])
+
+ # if source is not in config directory, remove the metric
+ for key, value in self.all_metrics.items():
+ split_name = key.split(".")
+ if split_name[1] in nicknames:
+ continue
+ try:
+ remove_name = key
+ remove_obj = value
+ cluster = self.cluster_table.intern_lookup_value(split_name[1])
+ if self.controls[cluster] == 1:
+ self.delete_controls(cluster)
+ self.remove_metric(remove_name, remove_obj)
+ self.removed_metrics[remove_name] = remove_obj
+ self.debug("removed metric name: %s" % remove_name) if self.dbg else None
+ self.set_need_refresh()
+ except Exception as e:
+ self.debug("can't remove metric: %s, see error: %s" % (key, e)) if self.dbg else None
+
+ for key in self.removed_metrics:
+ if key in self.all_metrics:
+ del self.all_metrics[key]
- # TODO: maybe nuke sources related to removed files
save_cluster_table = False
+ cluster_for_refresh_names = []
+ cluster_for_refresh = []
if sort_conf_list:
# sorted for indom cluster consistency
conf_filelist = sorted(conf_filelist)
@@ -1177,6 +1269,13 @@ class OpenMetricsPMDA(PMDA):
if name in self.source_by_name:
# this source is already known
self.assert_source_invariants(name=name)
+ s = self.source_by_name[name]
+ for key, value in self.source_by_cluster.items():
+ if value == s:
+ cluster_for_refresh.append(key)
+ cluster_for_refresh_names.append(name)
+ if name in self.re_add_list:
+ self.initialize_controls(key)
else:
try:
path = file
@@ -1185,17 +1284,16 @@ class OpenMetricsPMDA(PMDA):
self.source_by_name[source.name] = source
self.source_by_cluster[source.cluster] = source
- # initialize statistics
- self.stats_fetch_calls[cluster] = 0
- self.stats_fetch_time[cluster] = 0
- self.stats_parse_time[cluster] = 0
- self.stats_status[cluster] = "unknown"
- self.stats_status_code[cluster] = 0
+ self.initialize_controls(cluster)
save_cluster_table = True
self.log("Found source %s cluster %d" % (name, cluster))
except Exception as e:
self.err("Error allocating new cluster/source %s (%s)" % (name, e))
+
+ self.debug("refreshing cluster list: %s" % cluster_for_refresh_names) if self.dbg else None
+ self.refresh_some_clusters_for_fetch(cluster_for_refresh)
+
if save_cluster_table:
self.cluster_table.save()
self.set_notify_change()

View File

@ -0,0 +1,17 @@
diff -Naurp pcp-6.3.7.orig/src/python/pmda.c pcp-6.3.7/src/python/pmda.c
--- pcp-6.3.7.orig/src/python/pmda.c 2025-06-26 19:39:47.002695985 +1000
+++ pcp-6.3.7/src/python/pmda.c 2025-06-26 19:40:16.426718001 +1000
@@ -417,11 +417,11 @@ refresh_all_clusters(int numclusters, in
PyList_SET_ITEM(list, i, num);
}
- arglist = Py_BuildValue("(N)", list);
+ arglist = Py_BuildValue("(O)", list);
+ Py_DECREF(list);
if (arglist == NULL)
return -ENOMEM;
result = PyObject_Call(refresh_all_func, arglist, NULL);
- Py_DECREF(list);
Py_DECREF(arglist);
if (result == NULL)
return callback_error("refresh_all_clusters");

21
selinux-pcp_pmie_t.patch Normal file
View File

@ -0,0 +1,21 @@
commit 081aa84d3809b64f3e7765abf13a9a90f1072ec8
Author: Nathan Scott <nathans@redhat.com>
Date: Tue May 6 11:46:14 2025 +1000
selinux: additional policy needed for pcp_pmie_t using ps(1)
Resolves Fedora BZ 2363903.
diff --git a/src/selinux/pcp.te b/src/selinux/pcp.te
index a30144950..9cbd59bd2 100644
--- a/src/selinux/pcp.te
+++ b/src/selinux/pcp.te
@@ -247,7 +247,7 @@ optional_policy(`
#
# pcp_pmie local policy
#
-allow pcp_pmie_t self:capability { chown fsetid sys_ptrace };
+allow pcp_pmie_t self:capability { chown fsetid sys_admin sys_ptrace };
allow pcp_pmie_t self:cap_userns sys_ptrace;
allow pcp_pmie_t self:netlink_route_socket { create_socket_perms nlmsg_read };
allow pcp_pmie_t self:unix_dgram_socket { create_socket_perms sendto };

View File

@ -0,0 +1,40 @@
diff -Naurp pcp-6.3.7.orig/GNUmakefile pcp-6.3.7/GNUmakefile
--- pcp-6.3.7.orig/GNUmakefile 2025-03-27 17:01:59.000000000 +1100
+++ pcp-6.3.7/GNUmakefile 2025-06-26 19:23:24.874469186 +1000
@@ -135,6 +135,8 @@ pcp.lsm src/include/builddefs src/includ
tmpfiles.init.setup: tmpfiles.init.setup.in
sed < $< > $@ \
+ -e "s@PCP_VAR_DIR@$(PCP_VAR_DIR)@" \
+ -e "s@PCP_TMP_DIR@$(PCP_TMP_DIR)@" \
-e "s@PCP_RUN_DIR@$(PCP_RUN_DIR)@" \
-e "s@PCP_LOG_DIR@$(PCP_LOG_DIR)@" \
-e "s/PCP_GROUP/$(PCP_GROUP)/" \
diff -Naurp pcp-6.3.7.orig/tmpfiles.init.setup.in pcp-6.3.7/tmpfiles.init.setup.in
--- pcp-6.3.7.orig/tmpfiles.init.setup.in 2024-07-30 16:43:55.000000000 +1000
+++ pcp-6.3.7/tmpfiles.init.setup.in 2025-06-26 19:23:24.875070062 +1000
@@ -1,10 +1,16 @@
# tmpfiles.d(5) config for Performance Co-Pilot startup components
#
-# Type Path Mode User Group Age Argument
-D PCP_RUN_DIR 0775 PCP_USER PCP_GROUP - -
-d PCP_LOG_DIR 0775 PCP_USER PCP_GROUP - -
-d PCP_LOG_DIR/pmcd 0755 root root - -
-d PCP_LOG_DIR/pmfind 0775 PCP_USER PCP_GROUP - -
-d PCP_LOG_DIR/pmie 0775 PCP_USER PCP_GROUP - -
-d PCP_LOG_DIR/pmlogger 0775 PCP_USER PCP_GROUP - -
-d PCP_LOG_DIR/pmproxy 0775 PCP_USER PCP_GROUP - -
+# Path Mode User Group Age Argument
+D PCP_RUN_DIR 0775 PCP_USER PCP_GROUP - -
+d PCP_VAR_DIR 0755 root root - -
+d PCP_VAR_DIR/config 0755 root root - -
+d PCP_VAR_DIR/config/pmda 0775 PCP_USER PCP_GROUP - -
+d PCP_VAR_DIR/config/pmie 0775 PCP_USER PCP_GROUP - -
+d PCP_VAR_DIR/config/pmlogger 0775 PCP_USER PCP_GROUP - -
+d PCP_TMP_DIR 0775 PCP_USER PCP_GROUP - -
+d PCP_LOG_DIR 0775 PCP_USER PCP_GROUP - -
+d PCP_LOG_DIR/pmcd 0755 root root - -
+d PCP_LOG_DIR/pmfind 0775 PCP_USER PCP_GROUP - -
+d PCP_LOG_DIR/pmie 0775 PCP_USER PCP_GROUP - -
+d PCP_LOG_DIR/pmlogger 0775 PCP_USER PCP_GROUP - -
+d PCP_LOG_DIR/pmproxy 0775 PCP_USER PCP_GROUP - -