diff --git a/.gitignore b/.gitignore
index 036f7d7..cf495d2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1 @@
-SOURCES/pcp-5.1.1.src.tar.gz
+SOURCES/pcp-5.2.1.src.tar.gz
diff --git a/.pcp.metadata b/.pcp.metadata
index d432933..a4c8370 100644
--- a/.pcp.metadata
+++ b/.pcp.metadata
@@ -1 +1 @@
-ecc3f3a9163a2295816ca5eb8d918125670a727e SOURCES/pcp-5.1.1.src.tar.gz
+b514d7be6200caef6957571ab880db2f9815d774 SOURCES/pcp-5.2.1.src.tar.gz
diff --git a/SOURCES/redhat-bugzilla-1541406.patch b/SOURCES/redhat-bugzilla-1541406.patch
deleted file mode 100644
index 9841486..0000000
--- a/SOURCES/redhat-bugzilla-1541406.patch
+++ /dev/null
@@ -1,83 +0,0 @@
-91dd4ae6b logutil: use $PCP_TMPFILE_DIR for intermediate pmlogger_merge archives
-b0c90d858 packaging: activate pmlogger_rewrite on upgrades
-b5e602187 packaging: revert pcp_archive_dir subsitution in build/rpm/GNUmakefile
-
---- a/src/pmlogger/pmlogger_merge.sh	2018-06-18 16:24:25.000000000 +1000
-+++ b/src/pmlogger/pmlogger_merge.sh	2020-06-11 13:10:57.401576513 +1000
-@@ -26,8 +26,9 @@
- 
- prog=`basename $0`
- tmp=`mktemp -d /tmp/pcp.XXXXXXXXX` || exit 1
-+tmpmerge=`mktemp -d $PCP_TMPFILE_DIR/pcp.XXXXXXXXX` || exit 1
- status=0
--trap "rm -rf $tmp; exit \$status" 0 1 2 3 15
-+trap "rm -rf $tmp $tmpmerge; exit \$status" 0 1 2 3 15
- 
- force=false
- VERBOSE=false
-@@ -229,8 +230,8 @@
- 	    # output = 108 file descriptors which should be well below any
- 	    # shell-imposed or system-imposed limits
- 	    #
--	    $VERBOSE && echo "		-> partial merge to $tmp/$part"
--	    cmd="pmlogextract $list $tmp/$part"
-+	    $VERBOSE && echo "		-> partial merge to $tmpmerge/$part"
-+	    cmd="pmlogextract $list $tmpmerge/$part"
- 	    if $SHOWME
- 	    then
- 		echo "+ $cmd"
-@@ -239,13 +240,13 @@
- 		then
- 		    :
- 		else
--		    $VERBOSE || echo "		-> partial merge to $tmp/$part"
-+		    $VERBOSE || echo "		-> partial merge to $tmpmerge/$part"
- 		    echo "$prog: Directory: `pwd`"
--		    echo "$prog: Failed: pmlogextract $list $tmp/$part"
-+		    echo "$prog: Failed: pmlogextract $list $tmpmerge/$part"
- 		    _warning
- 		fi
- 	    fi
--	    list=$tmp/$part
-+	    list=$tmpmerge/$part
- 	    part=`expr $part + 1`
- 	    i=0
- 	fi
---- a/build/rpm/fedora.spec	2020-05-29 09:15:44.000000000 +1000
-+++ b/build/rpm/fedora.spec	2020-06-11 13:10:57.402576528 +1000
-@@ -2814,6 +2814,7 @@
- chown -R pcp:pcp %{_logsdir}/pmie 2>/dev/null
- chown -R pcp:pcp %{_logsdir}/pmproxy 2>/dev/null
- %{install_file "$PCP_PMNS_DIR" .NeedRebuild}
-+%{install_file "$PCP_LOG_DIR/pmlogger" .NeedRewrite}
- %if !%{disable_systemd}
-     %systemd_postun_with_restart pmcd.service
-     %systemd_post pmcd.service
---- a/build/rpm/pcp.spec.in	2020-05-29 09:16:19.000000000 +1000
-+++ b/build/rpm/pcp.spec.in	2020-06-11 13:10:57.402576528 +1000
-@@ -3149,6 +3149,7 @@
- chown -R pcp:pcp "$PCP_LOG_DIR/pmie" 2>/dev/null
- chown -R pcp:pcp "$PCP_LOG_DIR/pmproxy" 2>/dev/null
- %{install_file "$PCP_PMNS_DIR" .NeedRebuild}
-+%{install_file "$PCP_LOG_DIR/pmlogger" .NeedRewrite}
- %if "@enable_systemd@" == "true"
-     %systemd_postun_with_restart pmcd.service
-     %systemd_post pmcd.service
---- a/debian/pcp.postinst.tail	2019-06-13 09:59:16.000000000 +1000
-+++ b/debian/pcp.postinst.tail	2020-06-11 13:10:57.402576528 +1000
-@@ -6,6 +6,8 @@
- 
- touch /var/lib/pcp/pmns/.NeedRebuild
- chmod 644 /var/lib/pcp/pmns/.NeedRebuild
-+touch /var/log/pcp/pmlogger/.NeedRewrite
-+chmod 644 /var/log/pcp/pmlogger/.NeedRewrite
- 
- getent group pcp >/dev/null || groupadd -r pcp
- getent passwd pcp >/dev/null || \
---- a/debian/pcp.prerm	2017-08-17 10:54:50.000000000 +1000
-+++ b/debian/pcp.prerm	2020-06-11 13:10:57.402576528 +1000
-@@ -24,3 +24,4 @@
-     fi
- fi
- rm -f /var/lib/pcp/pmns/.NeedRebuild
-+rm -f /var/log/pcp/pmlogger/.NeedRewrite
diff --git a/SOURCES/redhat-bugzilla-1790433.patch b/SOURCES/redhat-bugzilla-1790433.patch
deleted file mode 100644
index 31643bd..0000000
--- a/SOURCES/redhat-bugzilla-1790433.patch
+++ /dev/null
@@ -1,21 +0,0 @@
-BZ 1790433 - Missing dependency of pcp-pmda-snmp on net-snmp-perl
-bc4abb291 pmdasnmp: add Install checks for the required Net::SNMP module
-
-diff --git a/src/pmdas/snmp/Install b/src/pmdas/snmp/Install
-index 7fe4193e4..21a76ab56 100755
---- a/src/pmdas/snmp/Install
-+++ b/src/pmdas/snmp/Install
-@@ -22,6 +22,13 @@ iam=snmp
- perl_opt=true
- daemon_opt=false
- 
-+perl -e "use Net::SNMP" 2>/dev/null
-+if test $? -ne 0; then
-+    echo "Net::SNMP (Simple Network Management Protocol) perl module is not installed"
-+    status=1
-+    exit
-+fi
-+
- pmdaSetup
- pmdaInstall
- exit
diff --git a/SOURCES/redhat-bugzilla-1790452.patch b/SOURCES/redhat-bugzilla-1790452.patch
deleted file mode 100644
index ef28a8f..0000000
--- a/SOURCES/redhat-bugzilla-1790452.patch
+++ /dev/null
@@ -1,45 +0,0 @@
-BZ 1790452 - Installation of pcp-pmda-samba causes SELinux issues
-73772a60f selinux: fix pmdasamba(1) operating with selinux enforcing
-
---- a/qa/917.out.in	2020-05-19 20:34:46.000000000 +1000
-+++ pcp-5.1.1/qa/917.out.in	2020-06-22 17:29:14.346713826 +1000
-@@ -34,6 +34,8 @@
- ! allow [pcp_pmcd_t] [unreserved_port_t] : [tcp_socket] { name_bind name_connect };
- ! allow [pcp_pmcd_t] [unreserved_port_t] : [udp_socket] { name_bind };
- ! allow [pcp_pmlogger_t] [unreserved_port_t] : [tcp_socket] { name_bind };
-+  allow [pcp_pmcd_t] [samba_var_t] : [dir] { add_name write };
-+  allow [pcp_pmcd_t] [samba_var_t] : [file] { create };
-   allow [pcp_pmcd_t] [websm_port_t] : [tcp_socket] { name_connect };
- ! allow [pcp_pmcd_t] [pcp_tmp_t] : [file] { execute execute_no_trans map };
-   allow [pcp_pmcd_t] [hostname_exec_t] : [file] { execute execute_no_trans getattr open read };
---- a/src/pmdas/samba/pmdasamba.pl	2020-02-04 14:51:57.000000000 +1100
-+++ pcp-5.1.1/src/pmdas/samba/pmdasamba.pl	2020-06-22 17:29:14.346713826 +1000
-@@ -41,6 +41,7 @@
- 	$pmda->err("pmdasamba failed to open $smbstats pipe: $!");
- 
-     while (<STATS>) {
-+	$_ =~ s/"//g;
- 	if (m/^\*\*\*\*\s+(\w+[^*]*)\**$/) {
- 	    my $heading = $1;
- 	    $heading =~ s/ +$//g;
---- a/src/selinux/pcpupstream.te.in	2020-05-19 20:34:32.000000000 +1000
-+++ pcp-5.1.1/src/selinux/pcpupstream.te.in	2020-06-22 17:29:14.347713837 +1000
-@@ -22,6 +22,7 @@
- 	type pcp_pmie_exec_t; # pmda.summary
- 	type ping_exec_t; # pmda.netcheck
- 	type openvswitch_exec_t; # pmda.openvswitch
-+	type samba_var_t; # pmda.samba
- 	type websm_port_t; # pmda.openmetrics
-         type system_cronjob_t;
-         type user_home_t;
-@@ -151,6 +152,10 @@
- #type=AVC msg=audit(YYY.94): avc: denied { name_bind } for pid=9365 comm=pmlogger src=4332 scontext=system_u:system_r:pcp_pmlogger_t:s0 tcontext=system_u:object_r:unreserved_port_t:s0 tclass=tcp_socket permissive=0
- @PCP_UNRESERVED_PORT_RULE_PMLOGGER@
- 
-+#type=AVC msg=audit(YYY.97): avc: denied { write } for pid=3507787 comm="smbstatus" name="msg.lock" dev="dm-0" ino=283321 scontext=system_u:system_r:pcp_pmcd_t:s0 tcontext=system_u:object_r:samba_var_t:s0 tclass=dir permissive=0
-+allow pcp_pmcd_t samba_var_t:dir { add_name write }; # pmda.samba
-+allow pcp_pmcd_t samba_var_t:file { create }; # pmda.samba
-+
- #type=AVC msg=audit(YYY.15): avc:  denied  { name_connect } for  pid=13816 comm="python3" dest=9090 scontext=system_u:system_r:pcp_pmcd_t:s0 tcontext=system_u:object_r:websm_port_t:s0 tclass=tcp_socket permissive=0
- allow pcp_pmcd_t websm_port_t:tcp_socket name_connect; # pmda.openmetrics
- 
diff --git a/SOURCES/redhat-bugzilla-1792971.patch b/SOURCES/redhat-bugzilla-1792971.patch
deleted file mode 100644
index 37e6e69..0000000
--- a/SOURCES/redhat-bugzilla-1792971.patch
+++ /dev/null
@@ -1,1351 +0,0 @@
-5af58c8af pmdastatsd: fix minor sizeof issues found by Coverity scan
-b3f78dc82 pmlogconf: fix resource leak found by coverity scan
-8a3ed1b26 pmdastatsd: initialize stack variable to keep Coverity happy
-6902959e5 pmdastatsd: fix Coverity LOCK issues on error paths
-548cad8c5 libpcp_web: ensure context is freed only after timer is fully closed
-01e8bb436 services: pmlogger and pmie services want pmcd on boot
-20959e794 Fix of 1845241 - Intermittent pmlogconf core dumps
-32d6febf4 pcp-atop: resolve other paths of potential null task pointer dereference
-cda567efe pmproxy: improve diagnostics, particularly relating to http requests
-e0bb9e66c pmproxy: cleanup, remove unused flags and dead code in http encoding
-9da331eb8 pmproxy: support the OPTIONS protocol in HTTP 1.1
-1d84081af libpcp_web: add resilience to descriptor lookup paths
-
---- a/src/pmdas/statsd/src/aggregator-metric-duration-exact.c	2019-08-21 11:33:26.000000000 +1000
-+++ b/src/pmdas/statsd/src/aggregator-metric-duration-exact.c	2020-06-11 13:10:57.393576397 +1000
-@@ -45,7 +45,7 @@
-     double** new_values = realloc(collection->values, sizeof(double*) * new_length);
-     ALLOC_CHECK("Unable to allocate memory for collection value.");
-     collection->values = new_values;
--    collection->values[collection->length] = (double*) malloc(sizeof(double*));
-+    collection->values[collection->length] = (double*) malloc(sizeof(double));
-     ALLOC_CHECK("Unable to allocate memory for duration collection value.");
-     *(collection->values[collection->length]) = value;
-     collection->length = new_length;
---- a/src/pmdas/statsd/src/aggregator-metric-labels.c	2020-02-18 16:32:40.000000000 +1100
-+++ b/src/pmdas/statsd/src/aggregator-metric-labels.c	2020-06-11 13:10:57.393576397 +1000
-@@ -140,7 +140,7 @@
- 
- static char*
- create_instance_label_segment_str(char* tags) {
--    char buffer[JSON_BUFFER_SIZE];
-+    char buffer[JSON_BUFFER_SIZE] = {'\0'};
-     size_t tags_length = strlen(tags) + 1;
-     if (tags_length > JSON_BUFFER_SIZE) {
-         return NULL;
-@@ -197,7 +197,7 @@
-     ALLOC_CHECK("Unable to allocate memory for labels string in metric label record.");
-     memcpy((*out)->labels, datagram->tags, labels_length);
-     struct metric_label_metadata* meta = 
--        (struct metric_label_metadata*) malloc(sizeof(struct metric_label_metadata*));
-+        (struct metric_label_metadata*) malloc(sizeof(struct metric_label_metadata));
-     ALLOC_CHECK("Unable to allocate memory for metric label metadata.");
-     (*out)->meta = meta;
-     (*out)->type = METRIC_TYPE_NONE;
---- a/src/pmdas/statsd/src/network-listener.c	2019-08-27 11:09:16.000000000 +1000
-+++ b/src/pmdas/statsd/src/network-listener.c	2020-06-11 13:10:57.393576397 +1000
-@@ -68,7 +68,7 @@
-     struct timeval tv;
-     freeaddrinfo(res);
-     int max_udp_packet_size = config->max_udp_packet_size;
--    char *buffer = (char *) malloc(max_udp_packet_size * sizeof(char*));
-+    char *buffer = (char *) malloc(max_udp_packet_size * sizeof(char));
-     struct sockaddr_storage src_addr;
-     socklen_t src_addr_len = sizeof(src_addr);
-     int rv;
---- a/src/pmlogconf/pmlogconf.c	2020-05-23 13:33:27.000000000 +1000
-+++ b/src/pmlogconf/pmlogconf.c	2020-06-11 13:10:57.394576411 +1000
-@@ -735,7 +735,7 @@
- static int
- evaluate_number_values(group_t *group, int type, numeric_cmp_t compare)
- {
--    unsigned int	i, found;
-+    int			i, found;
-     pmValueSet		*vsp;
-     pmValue		*vp;
-     pmAtomValue		atom;
-@@ -769,7 +769,7 @@
- static int
- evaluate_string_values(group_t *group, string_cmp_t compare)
- {
--    unsigned int	i, found;
-+    int			i, found;
-     pmValueSet		*vsp;
-     pmValue		*vp;
-     pmAtomValue		atom;
-@@ -828,7 +828,7 @@
- static int
- evaluate_string_regexp(group_t *group, regex_cmp_t compare)
- {
--    unsigned int	i, found;
-+    int			i, found;
-     pmValueSet		*vsp;
-     pmValue		*vp;
-     pmAtomValue		atom;
-@@ -1478,6 +1478,10 @@
- 	} else if (strncmp("#+ groupdir ", bytes, 12) == 0) {
- 	    group_dircheck(bytes + 12);
- 	} else if (strncmp("#+ ", bytes, 3) == 0) {
-+	    if (group) {
-+		/* reported by COVERITY RESOURCE LEAK */
-+	    	group_free(group);
-+	    }
- 	    group = group_create(bytes + 3, line);
- 	    head = 0;
- 	} else if (group) {
---- a/src/pmdas/statsd/src/aggregator-metrics.c	2020-02-18 16:32:40.000000000 +1100
-+++ b/src/pmdas/statsd/src/aggregator-metrics.c	2020-06-11 13:10:57.394576411 +1000
-@@ -212,7 +212,10 @@
-     VERBOSE_LOG(0, "Writing metrics to file...");
-     pthread_mutex_lock(&container->mutex);
-     metrics* m = container->metrics;
--    if (strlen(config->debug_output_filename) == 0) return; 
-+    if (strlen(config->debug_output_filename) == 0) {
-+        pthread_mutex_unlock(&container->mutex);
-+        return; 
-+    }
-     int sep = pmPathSeparator();
-     char debug_output[MAXPATHLEN];
-     pmsprintf(
---- a/src/pmdas/statsd/src/aggregator-stats.c	2020-02-18 16:32:40.000000000 +1100
-+++ b/src/pmdas/statsd/src/aggregator-stats.c	2020-06-11 13:10:57.394576411 +1000
-@@ -141,7 +141,10 @@
- write_stats_to_file(struct agent_config* config, struct pmda_stats_container* stats) {
-     VERBOSE_LOG(0, "Writing stats to file...");
-     pthread_mutex_lock(&stats->mutex);
--    if (strlen(config->debug_output_filename) == 0) return; 
-+    if (strlen(config->debug_output_filename) == 0) {
-+        pthread_mutex_unlock(&stats->mutex);
-+        return; 
-+    }
-     int sep = pmPathSeparator();
-     char debug_output[MAXPATHLEN];
-     pmsprintf(
---- a/src/libpcp_web/src/webgroup.c	2020-05-22 11:29:27.000000000 +1000
-+++ b/src/libpcp_web/src/webgroup.c	2020-06-11 13:10:57.394576411 +1000
-@@ -56,17 +56,28 @@
- }
- 
- static void
-+webgroup_release_context(uv_handle_t *handle)
-+{
-+    struct context	*context = (struct context *)handle->data;
-+
-+    if (pmDebugOptions.http)
-+	fprintf(stderr, "releasing context %p\n", context);
-+
-+    pmwebapi_free_context(context);
-+}
-+
-+static void
- webgroup_destroy_context(struct context *context, struct webgroups *groups)
- {
-     context->garbage = 1;
- 
-     if (pmDebugOptions.http)
--	fprintf(stderr, "freeing context %p\n", context);
-+	fprintf(stderr, "destroying context %p\n", context);
- 
-     uv_timer_stop(&context->timer);
-     if (groups)
- 	dictUnlink(groups->contexts, &context->randomid);
--    pmwebapi_free_context(context);
-+    uv_close((uv_handle_t *)&context->timer, webgroup_release_context);
- }
- 
- static void
---- a/src/pmie/pmie.service.in	2020-05-27 13:36:47.000000000 +1000
-+++ b/src/pmie/pmie.service.in	2020-06-11 13:10:57.394576411 +1000
-@@ -4,6 +4,7 @@
- After=network-online.target pmcd.service
- After=pmie_check.timer pmie_check.path pmie_daily.timer
- BindsTo=pmie_check.timer pmie_check.path pmie_daily.timer
-+Wants=pmcd.service
- 
- [Service]
- Type=notify
---- a/src/pmlogger/pmlogger.service.in	2020-05-22 16:48:32.000000000 +1000
-+++ b/src/pmlogger/pmlogger.service.in	2020-06-11 13:10:57.394576411 +1000
-@@ -4,6 +4,7 @@
- After=network-online.target pmcd.service
- After=pmlogger_check.timer pmlogger_check.path pmlogger_daily.timer pmlogger_daily-poll.timer
- BindsTo=pmlogger_check.timer pmlogger_check.path pmlogger_daily.timer pmlogger_daily-poll.timer
-+Wants=pmcd.service
- 
- [Service]
- Type=notify
---- a/src/pcp/atop/showgeneric.c	2020-03-30 12:13:55.000000000 +1100
-+++ b/src/pcp/atop/showgeneric.c	2020-06-11 13:10:57.395576426 +1000
-@@ -2024,6 +2024,9 @@
- 	*/
- 	for (numusers=i=0; i < numprocs; i++, curprocs++)
- 	{
-+	        if (*curprocs == NULL)
-+		        continue;
-+		
- 		if (procsuppress(*curprocs, &procsel))
- 			continue;
- 
-@@ -2069,6 +2072,9 @@
- 	*/
- 	for (numprogs=i=0; i < numprocs; i++, curprocs++)
- 	{
-+	        if (*curprocs == NULL)
-+		        continue;
-+		
- 		if (procsuppress(*curprocs, &procsel))
- 			continue;
- 
-@@ -2112,6 +2118,9 @@
- 	*/
- 	for (numconts=i=0; i < numprocs; i++, curprocs++)
- 	{
-+	        if (*curprocs == NULL)
-+		        continue;
-+		
- 		if (procsuppress(*curprocs, &procsel))
- 			continue;
- 
---- a/src/libpcp_web/src/exports	2020-05-22 15:38:47.000000000 +1000
-+++ b/src/libpcp_web/src/exports	2020-06-11 13:10:57.397576455 +1000
-@@ -189,3 +189,14 @@
-     pmWebGroupDestroy;
-     sdsKeyDictCallBacks;
- } PCP_WEB_1.12;
-+
-+PCP_WEB_1.14 {
-+  global:
-+    dictFetchValue;
-+    http_method_str;
-+    http_body_is_final;
-+    http_parser_version;
-+    http_parser_url_init;
-+    http_parser_parse_url;
-+    http_parser_settings_init;
-+} PCP_WEB_1.13;
---- a/src/pmproxy/src/http.c	2020-03-23 09:47:47.000000000 +1100
-+++ b/src/pmproxy/src/http.c	2020-06-11 13:10:57.398576470 +1000
-@@ -21,6 +21,18 @@
- static int chunked_transfer_size; /* pmproxy.chunksize, pagesize by default */
- static int smallest_buffer_size = 128;
- 
-+#define MAX_PARAMS_SIZE 4096
-+#define MAX_HEADERS_SIZE 128
-+
-+static sds HEADER_ACCESS_CONTROL_REQUEST_HEADERS,
-+	   HEADER_ACCESS_CONTROL_REQUEST_METHOD,
-+	   HEADER_ACCESS_CONTROL_ALLOW_METHODS,
-+	   HEADER_ACCESS_CONTROL_ALLOW_HEADERS,
-+	   HEADER_ACCESS_CONTROL_ALLOW_ORIGIN,
-+	   HEADER_ACCESS_CONTROL_ALLOWED_HEADERS,
-+	   HEADER_CONNECTION, HEADER_CONTENT_LENGTH,
-+	   HEADER_ORIGIN, HEADER_WWW_AUTHENTICATE;
-+
- /*
-  * Simple helpers to manage the cumulative addition of JSON
-  * (arrays and/or objects) to a buffer.
-@@ -121,45 +133,9 @@
- 	return "text/html";
-     if (flags & HTTP_FLAG_TEXT)
- 	return "text/plain";
--    if (flags & HTTP_FLAG_JS)
--	return "text/javascript";
--    if (flags & HTTP_FLAG_CSS)
--	return "text/css";
--    if (flags & HTTP_FLAG_ICO)
--	return "image/x-icon";
--    if (flags & HTTP_FLAG_JPG)
--	return "image/jpeg";
--    if (flags & HTTP_FLAG_PNG)
--	return "image/png";
--    if (flags & HTTP_FLAG_GIF)
--	return "image/gif";
-     return "application/octet-stream";
- }
- 
--http_flags
--http_suffix_type(const char *suffix)
--{
--    if (strcmp(suffix, "js") == 0)
--	return HTTP_FLAG_JS;
--    if (strcmp(suffix, "ico") == 0)
--	return HTTP_FLAG_ICO;
--    if (strcmp(suffix, "css") == 0)
--	return HTTP_FLAG_CSS;
--    if (strcmp(suffix, "png") == 0)
--	return HTTP_FLAG_PNG;
--    if (strcmp(suffix, "gif") == 0)
--	return HTTP_FLAG_GIF;
--    if (strcmp(suffix, "jpg") == 0)
--	return HTTP_FLAG_JPG;
--    if (strcmp(suffix, "jpeg") == 0)
--	return HTTP_FLAG_JPG;
--    if (strcmp(suffix, "html") == 0)
--	return HTTP_FLAG_HTML;
--    if (strcmp(suffix, "txt") == 0)
--	return HTTP_FLAG_TEXT;
--    return 0;
--}
--
- static const char * const
- http_content_encoding(http_flags flags)
- {
-@@ -259,26 +235,28 @@
- 
-     header = sdscatfmt(sdsempty(),
- 		"HTTP/%u.%u %u %s\r\n"
--		"Connection: Keep-Alive\r\n"
--		"Access-Control-Allow-Origin: *\r\n"
--		"Access-Control-Allow-Headers: Accept, Accept-Language, Content-Language, Content-Type\r\n",
-+		"%S: Keep-Alive\r\n",
- 		parser->http_major, parser->http_minor,
--		sts, http_status_mapping(sts));
-+		sts, http_status_mapping(sts), HEADER_CONNECTION);
-+    header = sdscatfmt(header,
-+		"%S: *\r\n"
-+		"%S: %S\r\n",
-+		HEADER_ACCESS_CONTROL_ALLOW_ORIGIN,
-+		HEADER_ACCESS_CONTROL_ALLOW_HEADERS,
-+		HEADER_ACCESS_CONTROL_ALLOWED_HEADERS);
- 
-     if (sts == HTTP_STATUS_UNAUTHORIZED && client->u.http.realm)
--	header = sdscatfmt(header, "WWW-Authenticate: Basic realm=\"%S\"\r\n",
--				client->u.http.realm);
-+	header = sdscatfmt(header, "%S: Basic realm=\"%S\"\r\n",
-+				HEADER_WWW_AUTHENTICATE, client->u.http.realm);
- 
--    if ((flags & HTTP_FLAG_STREAMING))
--	header = sdscatfmt(header, "Transfer-encoding: %s\r\n", "chunked");
--
--    if (!(flags & HTTP_FLAG_STREAMING))
--	header = sdscatfmt(header, "Content-Length: %u\r\n", length);
-+    if ((flags & (HTTP_FLAG_STREAMING | HTTP_FLAG_NO_BODY)))
-+	header = sdscatfmt(header, "Transfer-encoding: chunked\r\n");
-+    else
-+	header = sdscatfmt(header, "%S: %u\r\n", HEADER_CONTENT_LENGTH, length);
- 
--    header = sdscatfmt(header,
--		"Content-Type: %s%s\r\n"
--		"Date: %s\r\n\r\n",
--		http_content_type(flags), http_content_encoding(flags),
-+    header = sdscatfmt(header, "Content-Type: %s%s\r\n",
-+		http_content_type(flags), http_content_encoding(flags));
-+    header = sdscatfmt(header, "Date: %s\r\n\r\n",
- 		http_date_string(time(NULL), date, sizeof(date)));
- 
-     if (pmDebugOptions.http && pmDebugOptions.desperate) {
-@@ -288,8 +266,130 @@
-     return header;
- }
- 
-+static sds
-+http_header_value(struct client *client, sds header)
-+{
-+    if (client->u.http.headers == NULL)
-+	return NULL;
-+    return (sds)dictFetchValue(client->u.http.headers, header);
-+}
-+
-+static sds
-+http_headers_allowed(sds headers)
-+{
-+    (void)headers;
-+    return sdsdup(HEADER_ACCESS_CONTROL_ALLOWED_HEADERS);
-+}
-+
-+/* check whether the (preflight) method being proposed is acceptable */
-+static int
-+http_method_allowed(sds value, http_options options)
-+{
-+    if (strcmp(value, "GET") == 0 && (options & HTTP_OPT_GET))
-+	return 1;
-+    if (strcmp(value, "PUT") == 0 && (options & HTTP_OPT_PUT))
-+	return 1;
-+    if (strcmp(value, "POST") == 0 && (options & HTTP_OPT_POST))
-+	return 1;
-+    if (strcmp(value, "HEAD") == 0 && (options & HTTP_OPT_HEAD))
-+	return 1;
-+    if (strcmp(value, "TRACE") == 0 && (options & HTTP_OPT_TRACE))
-+	return 1;
-+    return 0;
-+}
-+
-+static char *
-+http_methods_string(char *buffer, size_t length, http_options options)
-+{
-+    char		*p = buffer;
-+
-+    /* ensure room for all options, spaces and comma separation */
-+    if (!options || length < 48)
-+	return NULL;
-+
-+    memset(buffer, 0, length);
-+    if (options & HTTP_OPT_GET)
-+	strcat(p, ", GET");
-+    if (options & HTTP_OPT_PUT)
-+	strcat(p, ", PUT");
-+    if (options & HTTP_OPT_HEAD)
-+	strcat(p, ", HEAD");
-+    if (options & HTTP_OPT_POST)
-+	strcat(p, ", POST");
-+    if (options & HTTP_OPT_TRACE)
-+	strcat(p, ", TRACE");
-+    if (options & HTTP_OPT_OPTIONS)
-+	strcat(p, ", OPTIONS");
-+    return p + 2; /* skip leading comma+space */
-+}
-+
-+static sds
-+http_response_trace(struct client *client)
-+{
-+    dictIterator	*iterator;
-+    dictEntry		*entry;
-+    sds			result = sdsempty();
-+
-+    iterator = dictGetSafeIterator(client->u.http.headers);
-+    while ((entry = dictNext(iterator)) != NULL)
-+	result = sdscatfmt("%S: %S\r\n", dictGetKey(entry), dictGetVal(entry));
-+    dictReleaseIterator(iterator);
-+    return result;
-+}
-+
-+static sds
-+http_response_access(struct client *client, http_code sts, http_options options)
-+{
-+    struct http_parser	*parser = &client->u.http.parser;
-+    char		buffer[64];
-+    sds			header, value, result;
-+
-+    value = http_header_value(client, HEADER_ACCESS_CONTROL_REQUEST_METHOD);
-+    if (value && http_method_allowed(value, options) == 0)
-+	sts = HTTP_STATUS_METHOD_NOT_ALLOWED;
-+
-+    parser->http_major = parser->http_minor = 1;
-+
-+    header = sdscatfmt(sdsempty(),
-+		"HTTP/%u.%u %u %s\r\n"
-+		"%S: Keep-Alive\r\n",
-+		parser->http_major, parser->http_minor,
-+		sts, http_status_mapping(sts), HEADER_CONNECTION);
-+    header = sdscatfmt(header, "%S: %u\r\n", HEADER_CONTENT_LENGTH, 0);
-+
-+    if (sts >= HTTP_STATUS_OK && sts < HTTP_STATUS_BAD_REQUEST) {
-+	if ((value = http_header_value(client, HEADER_ORIGIN)))
-+	    header = sdscatfmt(header, "%S: %S\r\n",
-+			        HEADER_ACCESS_CONTROL_ALLOW_ORIGIN, value);
-+
-+	header = sdscatfmt(header, "%S: %s\r\n",
-+			    HEADER_ACCESS_CONTROL_ALLOW_METHODS,
-+			    http_methods_string(buffer, sizeof(buffer), options));
-+
-+	value = http_header_value(client, HEADER_ACCESS_CONTROL_REQUEST_HEADERS);
-+	if (value && (result = http_headers_allowed(value)) != NULL) {
-+	    header = sdscatfmt(header, "%S: %S\r\n",
-+				HEADER_ACCESS_CONTROL_ALLOW_HEADERS, result);
-+	    sdsfree(result);
-+	}
-+    }
-+    if (sts == HTTP_STATUS_UNAUTHORIZED && client->u.http.realm)
-+	header = sdscatfmt(header, "%S: Basic realm=\"%S\"\r\n",
-+			    HEADER_WWW_AUTHENTICATE, client->u.http.realm);
-+
-+    header = sdscatfmt(header, "Date: %s\r\n\r\n",
-+		http_date_string(time(NULL), buffer, sizeof(buffer)));
-+
-+    if (pmDebugOptions.http && pmDebugOptions.desperate) {
-+	fprintf(stderr, "access response to client %p\n", client);
-+	fputs(header, stderr);
-+    }
-+    return header;
-+}
-+
- void
--http_reply(struct client *client, sds message, http_code sts, http_flags type)
-+http_reply(struct client *client, sds message,
-+		http_code sts, http_flags type, http_options options)
- {
-     http_flags		flags = client->u.http.flags;
-     char		length[32]; /* hex length */
-@@ -313,6 +413,15 @@
- 
- 	suffix = sdsnewlen("0\r\n\r\n", 5);		/* chunked suffix */
- 	client->u.http.flags &= ~HTTP_FLAG_STREAMING;	/* end of stream! */
-+
-+    } else if (flags & HTTP_FLAG_NO_BODY) {
-+	if (client->u.http.parser.method == HTTP_OPTIONS)
-+	    buffer = http_response_access(client, sts, options);
-+	else if (client->u.http.parser.method == HTTP_TRACE)
-+	    buffer = http_response_trace(client);
-+	else	/* HTTP_HEAD */
-+	    buffer = http_response_header(client, 0, sts, type);
-+	suffix = NULL;
-     } else {	/* regular non-chunked response - headers + response body */
- 	if (client->buffer == NULL) {
- 	    suffix = message;
-@@ -326,10 +435,11 @@
- 	buffer = http_response_header(client, sdslen(suffix), sts, type);
-     }
- 
--    if (pmDebugOptions.http) {
--	fprintf(stderr, "HTTP response (client=%p)\n%s%s",
--			client, buffer, suffix);
--    }
-+    if (pmDebugOptions.http)
-+	fprintf(stderr, "HTTP %s response (client=%p)\n%s%s",
-+			http_method_str(client->u.http.parser.method),
-+			client, buffer, suffix ? suffix : "");
-+
-     client_write(client, buffer, suffix);
- }
- 
-@@ -363,7 +473,7 @@
- 	if (pmDebugOptions.desperate)
- 	    fputs(message, stderr);
-     }
--    http_reply(client, message, status, HTTP_FLAG_HTML);
-+    http_reply(client, message, status, HTTP_FLAG_HTML, 0);
- }
- 
- void
-@@ -371,6 +481,7 @@
- {
-     struct http_parser	*parser = &client->u.http.parser;
-     http_flags		flags = client->u.http.flags;
-+    const char		*method;
-     sds			buffer, suffix;
- 
-     /* If the client buffer length is now beyond a set maximum size,
-@@ -390,16 +501,18 @@
- 		buffer = sdsempty();
- 	    }
- 	    /* prepend a chunked transfer encoding message length (hex) */
--	    buffer = sdscatprintf(buffer, "%lX\r\n", (unsigned long)sdslen(client->buffer));
-+	    buffer = sdscatprintf(buffer, "%lX\r\n",
-+				 (unsigned long)sdslen(client->buffer));
- 	    suffix = sdscatfmt(client->buffer, "\r\n");
- 	    /* reset for next call - original released on I/O completion */
- 	    client->buffer = NULL;	/* safe, as now held in 'suffix' */
- 
- 	    if (pmDebugOptions.http) {
--		fprintf(stderr, "HTTP chunked buffer (client %p, len=%lu)\n%s"
--				"HTTP chunked suffix (client %p, len=%lu)\n%s",
--				client, (unsigned long)sdslen(buffer), buffer,
--				client, (unsigned long)sdslen(suffix), suffix);
-+		method = http_method_str(client->u.http.parser.method);
-+		fprintf(stderr, "HTTP %s chunk buffer (client %p, len=%lu)\n%s"
-+				"HTTP %s chunk suffix (client %p, len=%lu)\n%s",
-+			method, client, (unsigned long)sdslen(buffer), buffer,
-+			method, client, (unsigned long)sdslen(suffix), suffix);
- 	    }
- 	    client_write(client, buffer, suffix);
- 
-@@ -527,6 +640,8 @@
- 
-     if (length == 0)
- 	return NULL;
-+    if (length > MAX_PARAMS_SIZE)
-+	return NULL;
-     for (p = url; p < end; p++) {
- 	if (*p == '\0')
- 	    break;
-@@ -558,6 +673,11 @@
-     struct servlet	*servlet;
-     sds			url;
- 
-+    if (pmDebugOptions.http || pmDebugOptions.appl0)
-+	fprintf(stderr, "HTTP %s %.*s\n",
-+			http_method_str(client->u.http.parser.method),
-+			(int)length, offset);
-+
-     if (!(url = http_url_decode(offset, length, &client->u.http.parameters)))
- 	return NULL;
-     for (servlet = proxy->servlets; servlet != NULL; servlet = servlet->next) {
-@@ -576,13 +696,24 @@
- {
-     struct client	*client = (struct client *)request->data;
-     struct servlet	*servlet;
-+    sds			buffer;
-     int			sts;
- 
-     http_client_release(client);	/* new URL, clean slate */
--
--    if ((servlet = servlet_lookup(client, offset, length)) != NULL) {
-+    /* server options - https://tools.ietf.org/html/rfc7231#section-4.3.7 */
-+    if (length == 1 && *offset == '*' &&
-+	client->u.http.parser.method == HTTP_OPTIONS) {
-+	buffer = http_response_access(client, HTTP_STATUS_OK, HTTP_SERVER_OPTIONS);
-+	client_write(client, buffer, NULL);
-+    } else if ((servlet = servlet_lookup(client, offset, length)) != NULL) {
- 	client->u.http.servlet = servlet;
- 	if ((sts = client->u.http.parser.status_code) == 0) {
-+	    if (client->u.http.parser.method == HTTP_OPTIONS ||
-+		client->u.http.parser.method == HTTP_TRACE ||
-+		client->u.http.parser.method == HTTP_HEAD)
-+		client->u.http.flags |= HTTP_FLAG_NO_BODY;
-+	    else
-+		client->u.http.flags &= ~HTTP_FLAG_NO_BODY;
- 	    client->u.http.headers = dictCreate(&sdsOwnDictCallBacks, NULL);
- 	    return 0;
- 	}
-@@ -616,6 +747,11 @@
- 
-     if (client->u.http.parser.status_code || !client->u.http.headers)
- 	return 0;	/* already in process of failing connection */
-+    if (dictSize(client->u.http.headers) >= MAX_HEADERS_SIZE) {
-+	client->u.http.parser.status_code =
-+		HTTP_STATUS_REQUEST_HEADER_FIELDS_TOO_LARGE;
-+	return 0;
-+    }
- 
-     field = sdsnewlen(offset, length);
-     if (pmDebugOptions.http)
-@@ -826,6 +962,17 @@
-     if (chunked_transfer_size < smallest_buffer_size)
- 	chunked_transfer_size = smallest_buffer_size;
- 
-+    HEADER_ACCESS_CONTROL_REQUEST_HEADERS = sdsnew("Access-Control-Request-Headers");
-+    HEADER_ACCESS_CONTROL_REQUEST_METHOD = sdsnew("Access-Control-Request-Method");
-+    HEADER_ACCESS_CONTROL_ALLOW_METHODS = sdsnew("Access-Control-Allow-Methods");
-+    HEADER_ACCESS_CONTROL_ALLOW_HEADERS = sdsnew("Access-Control-Allow-Headers");
-+    HEADER_ACCESS_CONTROL_ALLOW_ORIGIN = sdsnew("Access-Control-Allow-Origin");
-+    HEADER_ACCESS_CONTROL_ALLOWED_HEADERS = sdsnew("Accept, Accept-Language, Content-Language, Content-Type");
-+    HEADER_CONNECTION = sdsnew("Connection");
-+    HEADER_CONTENT_LENGTH = sdsnew("Content-Length");
-+    HEADER_ORIGIN = sdsnew("Origin");
-+    HEADER_WWW_AUTHENTICATE = sdsnew("WWW-Authenticate");
-+
-     register_servlet(proxy, &pmseries_servlet);
-     register_servlet(proxy, &pmwebapi_servlet);
- }
-@@ -839,4 +986,15 @@
- 	servlet->close(proxy);
- 
-     proxymetrics_close(proxy, METRICS_HTTP);
-+
-+    sdsfree(HEADER_ACCESS_CONTROL_REQUEST_HEADERS);
-+    sdsfree(HEADER_ACCESS_CONTROL_REQUEST_METHOD);
-+    sdsfree(HEADER_ACCESS_CONTROL_ALLOW_METHODS);
-+    sdsfree(HEADER_ACCESS_CONTROL_ALLOW_HEADERS);
-+    sdsfree(HEADER_ACCESS_CONTROL_ALLOW_ORIGIN);
-+    sdsfree(HEADER_ACCESS_CONTROL_ALLOWED_HEADERS);
-+    sdsfree(HEADER_CONNECTION);
-+    sdsfree(HEADER_CONTENT_LENGTH);
-+    sdsfree(HEADER_ORIGIN);
-+    sdsfree(HEADER_WWW_AUTHENTICATE);
- }
---- a/src/pmproxy/src/series.c	2020-02-25 17:47:56.000000000 +1100
-+++ b/src/pmproxy/src/series.c	2020-06-11 13:10:57.398576470 +1000
-@@ -1,5 +1,5 @@
- /*
-- * Copyright (c) 2019 Red Hat.
-+ * Copyright (c) 2019-2020 Red Hat.
-  *
-  * This program is free software; you can redistribute it and/or modify it
-  * under the terms of the GNU Lesser General Public License as published
-@@ -15,8 +15,7 @@
- #include <assert.h>
- 
- typedef enum pmSeriesRestKey {
--    RESTKEY_NONE	= 0,
--    RESTKEY_SOURCE,
-+    RESTKEY_SOURCE	= 1,
-     RESTKEY_DESC,
-     RESTKEY_INSTS,
-     RESTKEY_LABELS,
-@@ -29,7 +28,8 @@
- 
- typedef struct pmSeriesRestCommand {
-     const char		*name;
--    unsigned int	size;
-+    unsigned int	namelen : 16;
-+    unsigned int	options : 16;
-     pmSeriesRestKey	key;
- } pmSeriesRestCommand;
- 
-@@ -39,7 +39,8 @@
-     pmSeriesFlags	flags;
-     pmSeriesTimeWindow	window;
-     uv_work_t		loading;
--    unsigned int	working;
-+    unsigned int	working : 1;
-+    unsigned int	options : 16;
-     int			nsids;
-     pmSID		*sids;
-     pmSID		sid;
-@@ -55,16 +56,25 @@
- } pmSeriesBaton;
- 
- static pmSeriesRestCommand commands[] = {
--    { .key = RESTKEY_QUERY, .name = "query", .size = sizeof("query")-1 },
--    { .key = RESTKEY_DESC,  .name = "descs",  .size = sizeof("descs")-1 },
--    { .key = RESTKEY_INSTS, .name = "instances", .size = sizeof("instances")-1 },
--    { .key = RESTKEY_LABELS, .name = "labels", .size = sizeof("labels")-1 },
--    { .key = RESTKEY_METRIC, .name = "metrics", .size = sizeof("metrics")-1 },
--    { .key = RESTKEY_SOURCE, .name = "sources", .size = sizeof("sources")-1 },
--    { .key = RESTKEY_VALUES, .name = "values", .size = sizeof("values")-1 },
--    { .key = RESTKEY_LOAD, .name = "load", .size = sizeof("load")-1 },
--    { .key = RESTKEY_PING, .name = "ping", .size = sizeof("ping")-1 },
--    { .key = RESTKEY_NONE }
-+    { .key = RESTKEY_QUERY, .options = HTTP_OPTIONS_GET | HTTP_OPTIONS_POST,
-+	    .name = "query", .namelen = sizeof("query")-1 },
-+    { .key = RESTKEY_DESC, .options = HTTP_OPTIONS_GET | HTTP_OPTIONS_POST,
-+	    .name = "descs", .namelen = sizeof("descs")-1 },
-+    { .key = RESTKEY_INSTS, .options = HTTP_OPTIONS_GET | HTTP_OPTIONS_POST,
-+	    .name = "instances", .namelen = sizeof("instances")-1 },
-+    { .key = RESTKEY_LABELS, .options = HTTP_OPTIONS_GET | HTTP_OPTIONS_POST,
-+	    .name = "labels", .namelen = sizeof("labels")-1 },
-+    { .key = RESTKEY_METRIC, .options = HTTP_OPTIONS_GET | HTTP_OPTIONS_POST,
-+	    .name = "metrics", .namelen = sizeof("metrics")-1 },
-+    { .key = RESTKEY_SOURCE, .options = HTTP_OPTIONS_GET | HTTP_OPTIONS_POST,
-+	    .name = "sources", .namelen = sizeof("sources")-1 },
-+    { .key = RESTKEY_VALUES, .options = HTTP_OPTIONS_GET | HTTP_OPTIONS_POST,
-+	    .name = "values", .namelen = sizeof("values")-1 },
-+    { .key = RESTKEY_LOAD, .options = HTTP_OPTIONS_GET | HTTP_OPTIONS_POST,
-+	    .name = "load", .namelen = sizeof("load")-1 },
-+    { .key = RESTKEY_PING, .options = HTTP_OPTIONS_GET,
-+	    .name = "ping", .namelen = sizeof("ping")-1 },
-+    { .name = NULL }	/* sentinel */
- };
- 
- /* constant string keys (initialized during servlet setup) */
-@@ -78,8 +88,8 @@
- static const char pmseries_success[] = "{\"success\":true}\r\n";
- static const char pmseries_failure[] = "{\"success\":false}\r\n";
- 
--static pmSeriesRestKey
--pmseries_lookup_restkey(sds url)
-+static pmSeriesRestCommand *
-+pmseries_lookup_rest_command(sds url)
- {
-     pmSeriesRestCommand	*cp;
-     const char		*name;
-@@ -88,11 +98,11 @@
- 	strncmp(url, "/series/", sizeof("/series/") - 1) == 0) {
- 	name = (const char *)url + sizeof("/series/") - 1;
- 	for (cp = &commands[0]; cp->name; cp++) {
--	    if (strncmp(cp->name, name, cp->size) == 0)
--		return cp->key;
-+	    if (strncmp(cp->name, name, cp->namelen) == 0)
-+		return cp;
- 	}
-     }
--    return RESTKEY_NONE;
-+    return NULL;
- }
- 
- static void
-@@ -518,6 +528,7 @@
- {
-     pmSeriesBaton	*baton = (pmSeriesBaton *)arg;
-     struct client	*client = baton->client;
-+    http_options	options = baton->options;
-     http_flags		flags = client->u.http.flags;
-     http_code		code;
-     sds			msg;
-@@ -545,7 +556,7 @@
- 	    msg = sdsnewlen(pmseries_failure, sizeof(pmseries_failure) - 1);
- 	flags |= HTTP_FLAG_JSON;
-     }
--    http_reply(client, msg, code, flags);
-+    http_reply(client, msg, code, flags, options);
- }
- 
- static void
-@@ -555,6 +566,14 @@
- 	fprintf(stderr, "series module setup (arg=%p)\n", arg);
- }
- 
-+static void
-+pmseries_log(pmLogLevel level, sds message, void *arg)
-+{
-+    pmSeriesBaton	*baton = (pmSeriesBaton *)arg;
-+
-+    proxylog(level, message, baton->client->proxy);
-+}
-+
- static pmSeriesSettings pmseries_settings = {
-     .callbacks.on_match		= on_pmseries_match,
-     .callbacks.on_desc		= on_pmseries_desc,
-@@ -567,7 +586,7 @@
-     .callbacks.on_label		= on_pmseries_label,
-     .callbacks.on_done		= on_pmseries_done,
-     .module.on_setup		= pmseries_setup,
--    .module.on_info		= proxylog,
-+    .module.on_info		= pmseries_log,
- };
- 
- static void
-@@ -686,7 +705,6 @@
-     case RESTKEY_PING:
- 	break;
- 
--    case RESTKEY_NONE:
-     default:
- 	client->u.http.parser.status_code = HTTP_STATUS_BAD_REQUEST;
- 	break;
-@@ -702,15 +720,16 @@
- pmseries_request_url(struct client *client, sds url, dict *parameters)
- {
-     pmSeriesBaton	*baton;
--    pmSeriesRestKey	key;
-+    pmSeriesRestCommand	*command;
- 
--    if ((key = pmseries_lookup_restkey(url)) == RESTKEY_NONE)
-+    if ((command = pmseries_lookup_rest_command(url)) == NULL)
- 	return 0;
- 
-     if ((baton = calloc(1, sizeof(*baton))) != NULL) {
- 	client->u.http.data = baton;
- 	baton->client = client;
--	baton->restkey = key;
-+	baton->restkey = command->key;
-+	baton->options = command->options;
- 	pmseries_setup_request_parameters(client, baton, parameters);
-     } else {
- 	client->u.http.parser.status_code = HTTP_STATUS_INTERNAL_SERVER_ERROR;
-@@ -794,10 +813,12 @@
- 
-     if (baton->query == NULL) {
- 	message = sdsnewlen(failed, sizeof(failed) - 1);
--	http_reply(client, message, HTTP_STATUS_BAD_REQUEST, HTTP_FLAG_JSON);
-+	http_reply(client, message, HTTP_STATUS_BAD_REQUEST,
-+			HTTP_FLAG_JSON, baton->options);
-     } else if (baton->working) {
- 	message = sdsnewlen(loading, sizeof(loading) - 1);
--	http_reply(client, message, HTTP_STATUS_CONFLICT, HTTP_FLAG_JSON);
-+	http_reply(client, message, HTTP_STATUS_CONFLICT,
-+			HTTP_FLAG_JSON, baton->options);
-     } else {
- 	uv_queue_work(client->proxy->events, &baton->loading,
- 			pmseries_load_work, pmseries_load_done);
-@@ -810,8 +831,17 @@
-     pmSeriesBaton	*baton = (pmSeriesBaton *)client->u.http.data;
-     int			sts;
- 
--    if (client->u.http.parser.status_code)
-+    if (client->u.http.parser.status_code) {
-+	on_pmseries_done(-EINVAL, baton);
-+	return 1;
-+    }
-+
-+    if (client->u.http.parser.method == HTTP_OPTIONS ||
-+	client->u.http.parser.method == HTTP_TRACE ||
-+	client->u.http.parser.method == HTTP_HEAD) {
-+	on_pmseries_done(0, baton);
- 	return 0;
-+    }
- 
-     switch (baton->restkey) {
-     case RESTKEY_QUERY:
---- a/src/pmproxy/src/webapi.c	2020-04-17 15:39:17.000000000 +1000
-+++ b/src/pmproxy/src/webapi.c	2020-06-11 13:10:57.399576484 +1000
-@@ -1,5 +1,5 @@
- /*
-- * Copyright (c) 2019 Red Hat.
-+ * Copyright (c) 2019-2020 Red Hat.
-  *
-  * This program is free software; you can redistribute it and/or modify it
-  * under the terms of the GNU Lesser General Public License as published
-@@ -18,8 +18,7 @@
- #include "util.h"
- 
- typedef enum pmWebRestKey {
--    RESTKEY_NONE	= 0,
--    RESTKEY_CONTEXT,
-+    RESTKEY_CONTEXT	= 1,
-     RESTKEY_METRIC,
-     RESTKEY_FETCH,
-     RESTKEY_INDOM,
-@@ -32,7 +31,8 @@
- 
- typedef struct pmWebRestCommand {
-     const char		*name;
--    unsigned int	size;
-+    unsigned int	namelen : 16;
-+    unsigned int	options : 16;
-     pmWebRestKey	key;
- } pmWebRestCommand;
- 
-@@ -47,6 +47,7 @@
-     sds			password;	/* from basic auth header */
-     unsigned int	times : 1;
-     unsigned int	compat : 1;
-+    unsigned int	options : 16;
-     unsigned int	numpmids;
-     unsigned int	numvsets;
-     unsigned int	numinsts;
-@@ -56,21 +57,31 @@
- } pmWebGroupBaton;
- 
- static pmWebRestCommand commands[] = {
--    { .key = RESTKEY_CONTEXT, .name = "context", .size = sizeof("context")-1 },
--    { .key = RESTKEY_PROFILE, .name = "profile", .size = sizeof("profile")-1 },
--    { .key = RESTKEY_SCRAPE, .name = "metrics", .size = sizeof("metrics")-1 },
--    { .key = RESTKEY_METRIC, .name = "metric", .size = sizeof("metric")-1 },
--    { .key = RESTKEY_DERIVE, .name = "derive", .size = sizeof("derive")-1 },
--    { .key = RESTKEY_FETCH, .name = "fetch", .size = sizeof("fetch")-1 },
--    { .key = RESTKEY_INDOM, .name = "indom", .size = sizeof("indom")-1 },
--    { .key = RESTKEY_STORE, .name = "store", .size = sizeof("store")-1 },
--    { .key = RESTKEY_CHILD, .name = "children", .size = sizeof("children")-1 },
--    { .key = RESTKEY_NONE }
-+    { .key = RESTKEY_CONTEXT, .options = HTTP_OPTIONS_GET,
-+	    .name = "context", .namelen = sizeof("context")-1 },
-+    { .key = RESTKEY_PROFILE, .options = HTTP_OPTIONS_GET,
-+	    .name = "profile", .namelen = sizeof("profile")-1 },
-+    { .key = RESTKEY_SCRAPE, .options = HTTP_OPTIONS_GET,
-+	    .name = "metrics", .namelen = sizeof("metrics")-1 },
-+    { .key = RESTKEY_METRIC, .options = HTTP_OPTIONS_GET,
-+	    .name = "metric", .namelen = sizeof("metric")-1 },
-+    { .key = RESTKEY_DERIVE, .options = HTTP_OPTIONS_GET | HTTP_OPTIONS_POST,
-+	    .name = "derive", .namelen = sizeof("derive")-1 },
-+    { .key = RESTKEY_FETCH, .options = HTTP_OPTIONS_GET,
-+	    .name = "fetch", .namelen = sizeof("fetch")-1 },
-+    { .key = RESTKEY_INDOM, .options = HTTP_OPTIONS_GET,
-+	    .name = "indom", .namelen = sizeof("indom")-1 },
-+    { .key = RESTKEY_STORE, .options = HTTP_OPTIONS_GET,
-+	    .name = "store", .namelen = sizeof("store")-1 },
-+    { .key = RESTKEY_CHILD, .options = HTTP_OPTIONS_GET,
-+	    .name = "children", .namelen = sizeof("children")-1 },
-+    { .name = NULL }	/* sentinel */
- };
- 
- static pmWebRestCommand openmetrics[] = {
--    { .key = RESTKEY_SCRAPE, .name = "/metrics", .size = sizeof("/metrics")-1 },
--    { .key = RESTKEY_NONE }
-+    { .key = RESTKEY_SCRAPE, .options = HTTP_OPTIONS_GET,
-+	    .name = "/metrics", .namelen = sizeof("/metrics")-1 },
-+    { .name = NULL }	/* sentinel */
- };
- 
- static sds PARAM_NAMES, PARAM_NAME, PARAM_PMIDS, PARAM_PMID,
-@@ -78,8 +89,8 @@
- 	   PARAM_CONTEXT, PARAM_CLIENT;
- 
- 
--static pmWebRestKey
--pmwebapi_lookup_restkey(sds url, unsigned int *compat, sds *context)
-+static pmWebRestCommand *
-+pmwebapi_lookup_rest_command(sds url, unsigned int *compat, sds *context)
- {
-     pmWebRestCommand	*cp;
-     const char		*name, *ctxid = NULL;
-@@ -94,7 +105,7 @@
- 		name++;
- 	    } while (isdigit((int)(*name)));
- 	    if (*name++ != '/')
--		return RESTKEY_NONE;
-+		return NULL;
- 	    *context = sdsnewlen(ctxid, name - ctxid - 1);
- 	}
- 	if (*name == '_') {
-@@ -102,13 +113,13 @@
- 	    *compat = 1;	/* backward-compatibility mode */
- 	}
- 	for (cp = &commands[0]; cp->name; cp++)
--	    if (strncmp(cp->name, name, cp->size) == 0)
--		return cp->key;
-+	    if (strncmp(cp->name, name, cp->namelen) == 0)
-+		return cp;
-     }
-     for (cp = &openmetrics[0]; cp->name; cp++)
--	if (strncmp(cp->name, url, cp->size) == 0)
--	    return cp->key;
--    return RESTKEY_NONE;
-+	if (strncmp(cp->name, url, cp->namelen) == 0)
-+	    return cp;
-+    return NULL;
- }
- 
- static void
-@@ -584,9 +595,10 @@
- {
-     pmWebGroupBaton	*baton = (pmWebGroupBaton *)arg;
-     struct client	*client = (struct client *)baton->client;
--    sds			quoted, msg;
-+    http_options	options = baton->options;
-     http_flags		flags = client->u.http.flags;
-     http_code		code;
-+    sds			quoted, msg;
- 
-     if (pmDebugOptions.series)
- 	fprintf(stderr, "%s: client=%p (sts=%d,msg=%s)\n", "on_pmwebapi_done",
-@@ -596,7 +608,9 @@
- 	code = HTTP_STATUS_OK;
- 	/* complete current response with JSON suffix if needed */
- 	if ((msg = baton->suffix) == NULL) {	/* empty OK response */
--	    if (flags & HTTP_FLAG_JSON) {
-+	    if (flags & HTTP_FLAG_NO_BODY) {
-+		msg = sdsempty();
-+	    } else if (flags & HTTP_FLAG_JSON) {
- 		msg = sdsnewlen("{", 1);
- 		if (context)
- 		    msg = sdscatfmt(msg, "\"context\":%S,", context);
-@@ -628,10 +642,18 @@
- 	sdsfree(quoted);
-     }
- 
--    http_reply(client, msg, code, flags);
-+    http_reply(client, msg, code, flags, options);
-     client_put(client);
- }
- 
-+static void
-+on_pmwebapi_info(pmLogLevel level, sds message, void *arg)
-+{
-+    pmWebGroupBaton	*baton = (pmWebGroupBaton *)arg;
-+
-+    proxylog(level, message, baton->client->proxy);
-+}
-+
- static pmWebGroupSettings pmwebapi_settings = {
-     .callbacks.on_context	= on_pmwebapi_context,
-     .callbacks.on_metric	= on_pmwebapi_metric,
-@@ -645,7 +667,7 @@
-     .callbacks.on_scrape_labels	= on_pmwebapi_scrape_labels,
-     .callbacks.on_check		= on_pmwebapi_check,
-     .callbacks.on_done		= on_pmwebapi_done,
--    .module.on_info		= proxylog,
-+    .module.on_info		= on_pmwebapi_info,
- };
- 
- /*
-@@ -734,7 +756,6 @@
- 	client->u.http.flags |= HTTP_FLAG_JSON;
- 	break;
- 
--    case RESTKEY_NONE:
-     default:
- 	client->u.http.parser.status_code = HTTP_STATUS_BAD_REQUEST;
- 	break;
-@@ -750,11 +771,11 @@
- pmwebapi_request_url(struct client *client, sds url, dict *parameters)
- {
-     pmWebGroupBaton	*baton;
--    pmWebRestKey	key;
-+    pmWebRestCommand	*command;
-     unsigned int	compat = 0;
-     sds			context = NULL;
- 
--    if ((key = pmwebapi_lookup_restkey(url, &compat, &context)) == RESTKEY_NONE) {
-+    if (!(command = pmwebapi_lookup_rest_command(url, &compat, &context))) {
- 	sdsfree(context);
- 	return 0;
-     }
-@@ -762,7 +783,8 @@
-     if ((baton = calloc(1, sizeof(*baton))) != NULL) {
- 	client->u.http.data = baton;
- 	baton->client = client;
--	baton->restkey = key;
-+	baton->restkey = command->key;
-+	baton->options = command->options;
- 	baton->compat = compat;
- 	baton->context = context;
- 	pmwebapi_setup_request_parameters(client, baton, parameters);
-@@ -885,17 +907,27 @@
-     uv_loop_t		*loop = client->proxy->events;
-     uv_work_t		*work;
- 
--    /* fail early if something has already gone wrong */
--    if (client->u.http.parser.status_code != 0)
-+    /* take a reference on the client to prevent freeing races on close */
-+    client_get(client);
-+
-+    if (client->u.http.parser.status_code) {
-+	on_pmwebapi_done(NULL, -EINVAL, NULL, baton);
- 	return 1;
-+    }
-+
-+    if (client->u.http.parser.method == HTTP_OPTIONS ||
-+	client->u.http.parser.method == HTTP_TRACE ||
-+	client->u.http.parser.method == HTTP_HEAD) {
-+	on_pmwebapi_done(NULL, 0, NULL, baton);
-+	return 0;
-+    }
- 
--    if ((work = (uv_work_t *)calloc(1, sizeof(uv_work_t))) == NULL)
-+    if ((work = (uv_work_t *)calloc(1, sizeof(uv_work_t))) == NULL) {
-+	client_put(client);
- 	return 1;
-+    }
-     work->data = baton;
- 
--    /* take a reference on the client to prevent freeing races on close */
--    client_get(client);
--
-     /* submit command request to worker thread */
-     switch (baton->restkey) {
-     case RESTKEY_CONTEXT:
-@@ -925,11 +957,10 @@
-     case RESTKEY_SCRAPE:
- 	uv_queue_work(loop, work, pmwebapi_scrape, pmwebapi_work_done);
- 	break;
--    case RESTKEY_NONE:
-     default:
-+	pmwebapi_work_done(work, -EINVAL);
- 	client->u.http.parser.status_code = HTTP_STATUS_BAD_REQUEST;
--	client_put(client);
--	free(work);
-+	on_pmwebapi_done(NULL, -EINVAL, NULL, baton);
- 	return 1;
-     }
-     return 0;
---- a/src/pmproxy/src/http.h	2019-12-02 16:43:20.000000000 +1100
-+++ b/src/pmproxy/src/http.h	2020-06-11 13:10:57.398576470 +1000
-@@ -1,5 +1,5 @@
- /*
-- * Copyright (c) 2019 Red Hat.
-+ * Copyright (c) 2019-2020 Red Hat.
-  * 
-  * This program is free software; you can redistribute it and/or modify it
-  * under the terms of the GNU Lesser General Public License as published
-@@ -34,29 +34,39 @@
-     HTTP_FLAG_JSON	= (1<<0),
-     HTTP_FLAG_TEXT	= (1<<1),
-     HTTP_FLAG_HTML	= (1<<2),
--    HTTP_FLAG_JS	= (1<<3),
--    HTTP_FLAG_CSS	= (1<<4),
--    HTTP_FLAG_ICO	= (1<<5),
--    HTTP_FLAG_JPG	= (1<<6),
--    HTTP_FLAG_PNG	= (1<<7),
--    HTTP_FLAG_GIF	= (1<<8),
-     HTTP_FLAG_UTF8	= (1<<10),
-     HTTP_FLAG_UTF16	= (1<<11),
-+    HTTP_FLAG_NO_BODY	= (1<<13),
-     HTTP_FLAG_COMPRESS	= (1<<14),
-     HTTP_FLAG_STREAMING	= (1<<15),
-     /* maximum 16 for server.h */
- } http_flags;
- 
-+typedef enum http_options {
-+    HTTP_OPT_GET	= (1 << HTTP_GET),
-+    HTTP_OPT_PUT	= (1 << HTTP_PUT),
-+    HTTP_OPT_HEAD	= (1 << HTTP_HEAD),
-+    HTTP_OPT_POST	= (1 << HTTP_POST),
-+    HTTP_OPT_TRACE	= (1 << HTTP_TRACE),
-+    HTTP_OPT_OPTIONS	= (1 << HTTP_OPTIONS),
-+    /* maximum 16 in command opts fields */
-+} http_options;
-+
-+#define HTTP_COMMON_OPTIONS (HTTP_OPT_HEAD | HTTP_OPT_TRACE | HTTP_OPT_OPTIONS)
-+#define HTTP_OPTIONS_GET    (HTTP_COMMON_OPTIONS | HTTP_OPT_GET)
-+#define HTTP_OPTIONS_PUT    (HTTP_COMMON_OPTIONS | HTTP_OPT_PUT)
-+#define HTTP_OPTIONS_POST   (HTTP_COMMON_OPTIONS | HTTP_OPT_POST)
-+#define HTTP_SERVER_OPTIONS (HTTP_OPTIONS_GET | HTTP_OPT_PUT | HTTP_OPT_POST)
-+
- typedef unsigned int http_code;
- 
- extern void http_transfer(struct client *);
--extern void http_reply(struct client *, sds, http_code, http_flags);
-+extern void http_reply(struct client *, sds, http_code, http_flags, http_options);
- extern void http_error(struct client *, http_code, const char *);
- 
- extern int http_decode(const char *, size_t, sds);
- extern const char *http_status_mapping(http_code);
- extern const char *http_content_type(http_flags);
--extern http_flags http_suffix_type(const char *);
- 
- extern sds http_get_buffer(struct client *);
- extern void http_set_buffer(struct client *, sds, http_flags);
---- a/qa/1837	1970-01-01 10:00:00.000000000 +1000
-+++ b/qa/1837	2020-06-11 13:10:57.396576440 +1000
-@@ -0,0 +1,55 @@
-+#!/bin/sh
-+# PCP QA Test No. 1837
-+# Exercise PMWEBAPI handling server OPTIONS.
-+#
-+# Copyright (c) 2020 Red Hat.  All Rights Reserved.
-+#
-+
-+seq=`basename $0`
-+echo "QA output created by $seq"
-+
-+# get standard environment, filters and checks
-+. ./common.product
-+. ./common.filter
-+. ./common.check
-+
-+_check_series
-+which curl >/dev/null 2>&1 || _notrun "No curl binary installed"
-+curl --request-targets 2>&1 | grep -q 'requires parameter' && \
-+	_notrun "Test requires curl --request-targets option"
-+
-+status=1	# failure is the default!
-+$sudo rm -rf $tmp.* $seq.full
-+trap "cd $here; _cleanup; exit \$status" 0 1 2 3 15
-+
-+pmproxy_was_running=false
-+[ -f $PCP_RUN_DIR/pmproxy.pid ] && pmproxy_was_running=true
-+echo "pmproxy_was_running=$pmproxy_was_running" >>$here/$seq.full
-+
-+_cleanup()
-+{
-+    if $pmproxy_was_running
-+    then
-+	echo "Restart pmproxy ..." >>$here/$seq.full
-+	_service pmproxy restart >>$here/$seq.full 2>&1
-+	_wait_for_pmproxy
-+    else
-+	echo "Stopping pmproxy ..." >>$here/$seq.full
-+	_service pmproxy stop >>$here/$seq.full 2>&1
-+    fi
-+    $sudo rm -f $tmp.*
-+}
-+
-+# real QA test starts here
-+_service pmproxy restart >/dev/null 2>&1
-+
-+curl -isS --request-target "*" -X OPTIONS http://localhost:44322 \
-+	2>&1 | tee -a $here/$seq.full | _webapi_header_filter
-+
-+echo >>$here/$seq.full
-+echo "=== pmproxy log ===" >>$here/$seq.full
-+cat $PCP_LOG_DIR/pmproxy/pmproxy.log >>$here/$seq.full
-+
-+# success, all done
-+status=0
-+exit
---- a/qa/1837.out	1970-01-01 10:00:00.000000000 +1000
-+++ b/qa/1837.out	2020-06-11 13:10:57.397576455 +1000
-@@ -0,0 +1,6 @@
-+QA output created by 1837
-+
-+Access-Control-Allow-Methods: GET, PUT, HEAD, POST, TRACE, OPTIONS
-+Content-Length: 0
-+Date: DATE
-+HTTP/1.1 200 OK
---- a/qa/780	2020-04-14 14:41:41.000000000 +1000
-+++ b/qa/780	2020-06-11 13:10:57.397576455 +1000
-@@ -1,8 +1,8 @@
- #!/bin/sh
- # PCP QA Test No. 780
--# Exercise PMWEBAPI Access-Control-Allow-Origin HTTP header.
-+# Exercise PMWEBAPI CORS headers.
- #
--# Copyright (c) 2014,2019 Red Hat.
-+# Copyright (c) 2014,2019-2020 Red Hat.
- #
- 
- seq=`basename $0`
-@@ -16,7 +16,6 @@
- _check_series
- which curl >/dev/null 2>&1 || _notrun "No curl binary installed"
- 
--signal=$PCP_BINADM_DIR/pmsignal
- status=1	# failure is the default!
- $sudo rm -rf $tmp.* $seq.full
- trap "cd $here; _cleanup; exit \$status" 0 1 2 3 15
-@@ -39,13 +38,21 @@
-     $sudo rm -f $tmp.*
- }
- 
--unset http_proxy
--unset HTTP_PROXY
--
- # real QA test starts here
- _service pmproxy restart >/dev/null 2>&1
- 
--curl -s -S "http://localhost:44323/pmapi/context" -I | _webapi_header_filter
-+echo "=== Basic" | tee -a $here/$seq.full
-+curl -IsS "http://localhost:44323/pmapi/context" | _webapi_header_filter
-+
-+echo "=== Preflight" | tee -a $here/$seq.full
-+curl -isS -X OPTIONS "http://localhost:44323/series/query?expr=hinv*" | _webapi_header_filter
-+
-+echo "=== OK Request Method" | tee -a $here/$seq.full
-+curl -isS -X OPTIONS -H "Origin: http://example.com" -H "Access-Control-Request-Method: GET" "http://localhost:44323/pmapi/context" | _webapi_header_filter
-+
-+echo "=== Bad Request Method" | tee -a $here/$seq.full
-+curl -isS -X OPTIONS -H "Origin: http://example.com" -H "Access-Control-Request-Method: BAD" "http://localhost:44323/pmapi/context" | _webapi_header_filter
-+
- echo >>$here/$seq.full
- echo "=== pmproxy log ===" >>$here/$seq.full
- cat $PCP_LOG_DIR/pmproxy/pmproxy.log >>$here/$seq.full
---- a/qa/780.out	2020-03-23 09:47:47.000000000 +1100
-+++ b/qa/780.out	2020-06-11 13:10:57.397576455 +1000
-@@ -1,8 +1,27 @@
- QA output created by 780
-+=== Basic
- 
- Access-Control-Allow-Headers: Accept, Accept-Language, Content-Language, Content-Type
- Access-Control-Allow-Origin: *
--Content-Length: SIZE
- Content-Type: application/json
- Date: DATE
- HTTP/1.1 200 OK
-+Transfer-encoding: chunked
-+=== Preflight
-+
-+Access-Control-Allow-Methods: GET, HEAD, POST, TRACE, OPTIONS
-+Content-Length: 0
-+Date: DATE
-+HTTP/1.1 200 OK
-+=== OK Request Method
-+
-+Access-Control-Allow-Methods: GET, HEAD, TRACE, OPTIONS
-+Access-Control-Allow-Origin: http://example.com
-+Content-Length: 0
-+Date: DATE
-+HTTP/1.1 200 OK
-+=== Bad Request Method
-+
-+Content-Length: 0
-+Date: DATE
-+HTTP/1.1 405 Method Not Allowed
---- a/qa/common.check	2020-05-20 10:51:37.000000000 +1000
-+++ b/qa/common.check	2020-06-11 13:10:57.397576455 +1000
-@@ -2696,7 +2696,7 @@
-     tee -a $here/$seq.full \
-     | col -b \
-     | sed \
--	-e 's/^\(Content-Length:\) [0-9][0-9]*/\1 SIZE/g' \
-+	-e 's/^\(Content-Length:\) [1-9][0-9]*/\1 SIZE/g' \
- 	-e 's/^\(Date:\).*/\1 DATE/g' \
- 	-e 's/\(\"context\":\) [0-9][0-9]*/\1 CTXID/g' \
- 	-e '/^Connection: Keep-Alive/d' \
---- a/qa/group	2020-05-28 09:15:22.000000000 +1000
-+++ b/qa/group	2020-06-11 13:10:57.397576455 +1000
-@@ -1757,6 +1757,7 @@
- 1724 pmda.bpftrace local python
- 1768 pmfind local
- 1793 pmrep pcp2xxx python local
-+1837 pmproxy local
- 1855 pmda.rabbitmq local
- 1896 pmlogger logutil pmlc local
- 4751 libpcp threads valgrind local pcp
---- a/qa/1211.out	2020-01-20 16:53:42.000000000 +1100
-+++ b/qa/1211.out	2020-06-11 13:10:57.399576484 +1000
-@@ -507,9 +507,11 @@
- Perform simple source-based query ...
- 
- Error handling - descriptor for bad series identifier
--pmseries: [Error] no descriptor for series identifier no.such.identifier
- 
- no.such.identifier
-+    PMID: PM_ID_NULL
-+    Data Type: ???  InDom: unknown 0xffffffff
-+    Semantics: unknown  Units: unknown
- 
- Error handling - metric name for bad series identifier
- 
---- a/src/libpcp_web/src/query.c	2020-01-20 15:43:31.000000000 +1100
-+++ b/src/libpcp_web/src/query.c	2020-06-11 13:10:57.399576484 +1000
-@@ -1938,11 +1938,15 @@
- 	return -EPROTO;
-     }
- 
--    /* sanity check - were we given an invalid series identifier? */
-+    /* were we given a non-metric series identifier? (e.g. an instance) */
-     if (elements[0]->type == REDIS_REPLY_NIL) {
--	infofmt(msg, "no descriptor for series identifier %s", series);
--	batoninfo(baton, PMLOG_ERROR, msg);
--	return -EINVAL;
-+	desc->indom = sdscpylen(desc->indom, "unknown", 7);
-+	desc->pmid = sdscpylen(desc->pmid, "PM_ID_NULL", 10);
-+	desc->semantics = sdscpylen(desc->semantics, "unknown", 7);
-+	desc->source = sdscpylen(desc->source, "unknown", 7);
-+	desc->type = sdscpylen(desc->type, "unknown", 7);
-+	desc->units = sdscpylen(desc->units, "unknown", 7);
-+	return 0;
-     }
- 
-     if (extract_string(baton, series, elements[0], &desc->indom, "indom") < 0)
diff --git a/SOURCES/redhat-bugzilla-1846705.patch b/SOURCES/redhat-bugzilla-1846705.patch
deleted file mode 100644
index 6504334..0000000
--- a/SOURCES/redhat-bugzilla-1846705.patch
+++ /dev/null
@@ -1,36 +0,0 @@
-BZ 1846705 - Possible memory leak detected in pcp-atop
-f30aff90b qa: add valgrind suppressions needed for valgrind 3.16
-
-diff --git a/qa/valgrind-suppress-3.16.0 b/qa/valgrind-suppress-3.16.0
-new file mode 100644
-index 000000000..515591747
---- /dev/null
-+++ b/qa/valgrind-suppress-3.16.0
-@@ -0,0 +1,27 @@
-+# qa/1080 and qa/490 and qa/386 and qa/459 on Fedora 32
-+# at 0x483880B: malloc (vg_replace_malloc.c:299)
-+# by 0x4A0D490: tsearch (in /usr/lib64/libc-2.28.so)
-+# by 0x4871EA6: __pmFindPDUBuf (pdubuf.c:126)
-+# ...
-+{
-+   tsearch
-+   Memcheck:Leak
-+   match-leak-kinds: possible
-+   fun:malloc
-+   fun:tsearch
-+   fun:__pmFindPDUBuf
-+   ...
-+}
-+
-+# qa/1080 and qa/490 and qa/386 and qa/459 on Fedora 32
-+# at 0x483880B: malloc (vg_replace_malloc.c:299)
-+# by 0x4871E5F: __pmFindPDUBuf (pdubuf.c:115)
-+# ...
-+{
-+   findpdubuf
-+   Memcheck:Leak
-+   match-leak-kinds: possible
-+   fun:malloc
-+   fun:__pmFindPDUBuf
-+   ...
-+}
diff --git a/SOURCES/redhat-bugzilla-1846711.patch b/SOURCES/redhat-bugzilla-1846711.patch
deleted file mode 100644
index 1c57ff2..0000000
--- a/SOURCES/redhat-bugzilla-1846711.patch
+++ /dev/null
@@ -1,268 +0,0 @@
-BZ 1846711 - pcp-pmda-openmetrics produces warnings querying grafana in its default configuration
-0b2ef2d79 pmdaopenmetrics: add control.status metrics, de-verbosify the log, QA updates
-63605e3db qa/1102: tweak openmetrics QA to be more deterministic
-649a0c3a2 qa: improve _filter_pmda_remove() in common.filter
-
-commit 0b2ef2d79686d1e44901263093edeb9e1b9b5f77
-Author: Mark Goodwin <mgoodwin@redhat.com>
-Date:   Fri Jun 19 12:18:47 2020 +1000
-
-    pmdaopenmetrics: add control.status metrics, de-verbosify the log, QA updates
-    
-    Resolves: RHBZ#1846711
-    
-    Add openmetrics.control.status (string status per configured URL
-    of the last fetch) and openmetrics.control.status_code, which
-    is the integer response code (e.g. 200 is success) with discrete
-    semantics.
-    
-    In addition, we now only spam the PMDA log and systemd journal
-    when a URL fetch fails if openmetrics.control.debug is non-zero.
-    Users can instead rely on the new status metrics, which can also
-    be used for service availability monitoring. These metrics
-    complement the openmetrics.control.parse_time, fetch_time and
-    calls counters.
-    
-    Includes QA updates and pmdaopenmetrics(1) doc updates.
-
-diff --git a/qa/1321.out b/qa/1321.out
-index cee072cd2..4533bccd8 100644
---- a/qa/1321.out
-+++ b/qa/1321.out
-@@ -13,6 +13,8 @@ openmetrics.control.calls
- openmetrics.control.debug
- openmetrics.control.fetch_time
- openmetrics.control.parse_time
-+openmetrics.control.status
-+openmetrics.control.status_code
- openmetrics.source1.metric1
- 
- == Created URL file /var/lib/pcp/pmdas/openmetrics/config.d/source2.url
-@@ -22,6 +24,8 @@ openmetrics.control.calls
- openmetrics.control.debug
- openmetrics.control.fetch_time
- openmetrics.control.parse_time
-+openmetrics.control.status
-+openmetrics.control.status_code
- openmetrics.source1.metric1
- openmetrics.source2.metric1
- openmetrics.source2.metric2
-@@ -33,6 +37,8 @@ openmetrics.control.calls
- openmetrics.control.debug
- openmetrics.control.fetch_time
- openmetrics.control.parse_time
-+openmetrics.control.status
-+openmetrics.control.status_code
- openmetrics.source1.metric1
- openmetrics.source2.metric1
- openmetrics.source2.metric2
-@@ -47,6 +53,8 @@ openmetrics.control.calls
- openmetrics.control.debug
- openmetrics.control.fetch_time
- openmetrics.control.parse_time
-+openmetrics.control.status
-+openmetrics.control.status_code
- openmetrics.source1.metric1
- openmetrics.source2.metric1
- openmetrics.source2.metric2
-@@ -63,6 +71,8 @@ openmetrics.control.calls
- openmetrics.control.debug
- openmetrics.control.fetch_time
- openmetrics.control.parse_time
-+openmetrics.control.status
-+openmetrics.control.status_code
- openmetrics.source1.metric1
- openmetrics.source2.metric1
- openmetrics.source2.metric2
-diff --git a/src/pmdas/openmetrics/pmdaopenmetrics.1 b/src/pmdas/openmetrics/pmdaopenmetrics.1
-index d3c7aa85f..0c92e2a11 100644
---- a/src/pmdas/openmetrics/pmdaopenmetrics.1
-+++ b/src/pmdas/openmetrics/pmdaopenmetrics.1
-@@ -413,10 +413,37 @@ log mandatory on 2 second {
- The PMDA maintains special control metrics, as described below.
- Apart from
- .BR openmetrics.control.debug ,
--each of these metrics is a counter and has one instance for each configured metric source.
--The instance domain is adjusted dynamically as new sources are discovered.
-+each of these metrics has one instance for each configured metric source.
-+All of these metrics have integer values with counter semantics, except
-+.BR openmetrics.control.status ,
-+which has a string value.
-+It is important to note that fetching any of the
-+.B openmetrics.control
-+metrics will only update the counters and status values if the corresponding URL is actually fetched.
-+If the source URL is not fetched, the control metric values do not trigger a refresh and the control
-+values reported represent the most recent fetch of each corresponding source.
-+.PP
-+The instance domain for the
-+.B openmetrics.control
-+metrics is adjusted dynamically as new sources are discovered.
- If there are no sources configured, the metric names are still defined
- but the instance domain will be empty and a fetch will return no values.
-+.IP \fBopenmetrics.control.status\fP
-+A string representing the status of the last fetch of the corresponding source.
-+This will generally be
-+.B success
-+for an http response code of 200.
-+This metric can be used for service availability monitoring - provided, as stated above,
-+the corresponding source URL is fetched too.
-+.IP \fBopenmetrics.control.status_code\fP
-+This metric is similar to
-+.B openmetrics.control.status
-+except that it is the integer response code of the last fetch.
-+A value of
-+.B 200
-+usually signifies success and any other value failure.
-+This metric can also be used for service availability monitoring, with the same caveats as
-+.BR openmetrics.control.status .
- .IP \fBopenmetrics.control.calls\fP
- total number of times each configured metric source has been fetched (if it's a URL)
- or executed (if it's a script), since the PMDA started.
-diff --git a/src/pmdas/openmetrics/pmdaopenmetrics.python b/src/pmdas/openmetrics/pmdaopenmetrics.python
-index a5ed22f13..1486ed676 100755
---- a/src/pmdas/openmetrics/pmdaopenmetrics.python
-+++ b/src/pmdas/openmetrics/pmdaopenmetrics.python
-@@ -1,6 +1,6 @@
- #!/usr/bin/env pmpython
- #
--# Copyright (c) 2017-2019 Red Hat.
-+# Copyright (c) 2017-2020 Red Hat.
- # Copyright (c) 2017 Ronak Jain.
- #
- # This program is free software; you can redistribute it and/or modify it
-@@ -704,6 +704,7 @@ class Source(object):
-             return
- 
-         # fetch the document
-+        status_code = 0
-         try:
-             if self.is_scripted:
-                 # Execute file, expecting openmetrics metric data on stdout.
-@@ -715,6 +716,7 @@ class Source(object):
-                     self.document = open(self.url[7:], 'r').read()
-                 else:
-                     r = self.requests.get(self.url, headers=self.headers, timeout=timeout)
-+                    status_code = r.status_code
-                     r.raise_for_status() # non-200?  ERROR
-                     # NB: the requests package automatically enables http keep-alive and compression
-                     self.document = r.text
-@@ -723,9 +725,13 @@ class Source(object):
-             incr = int(1000 * (time.time() - fetch_time))
-             self.pmda.stats_fetch_time[self.cluster] += incr
-             self.pmda.stats_fetch_time[0] += incr # total for all sources
-+            self.pmda.stats_status[self.cluster] = "success"
-+            self.pmda.stats_status_code[self.cluster] = status_code
- 
-         except Exception as e:
--            self.pmda.err('Warning: cannot fetch URL or execute script %s: %s' % (self.path, e))
-+            self.pmda.stats_status[self.cluster] = 'failed to fetch URL or execute script %s: %s' % (self.path, e)
-+            self.pmda.stats_status_code[self.cluster] = status_code
-+            self.pmda.debug('Warning: cannot fetch URL or execute script %s: %s' % (self.path, e)) if self.pmda.dbg else None
-             return
- 
-     def refresh2(self, timeout):
-@@ -844,6 +850,20 @@ class OpenMetricsPMDA(PMDA):
-             pmUnits(0, 0, 0, 0, 0, 0)),
-             'debug flag to enable verbose log messages, to enable: pmstore %s.control.debug 1' % self.pmda_name)
- 
-+        # response status string, per-source end-point
-+        self.stats_status = {0:"none"} # status string, keyed by cluster number
-+        self.add_metric('%s.control.status' % self.pmda_name, pmdaMetric(self.pmid(0, 5),
-+            c_api.PM_TYPE_STRING, self.sources_indom, c_api.PM_SEM_INSTANT,
-+            pmUnits(0, 0, 0, 0, 0, 0)), # no units
-+            'per-end-point source URL response status after the most recent fetch')
-+
-+        # response status code, per-source end-point
-+        self.stats_status_code = {0:0} # status code, keyed by cluster number
-+        self.add_metric('%s.control.status_code' % self.pmda_name, pmdaMetric(self.pmid(0, 6),
-+            c_api.PM_TYPE_32, self.sources_indom, c_api.PM_SEM_DISCRETE,
-+            pmUnits(0, 0, 0, 0, 0, 0)), # no units
-+            'per-end-point source URL response status code after the most recent fetch')
-+
-         # schedule a refresh
-         self.set_need_refresh()
- 
-@@ -961,6 +981,8 @@ class OpenMetricsPMDA(PMDA):
-                     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
- 
-                     save_cluster_table = True
-                     self.log("Found source %s cluster %d" % (name, cluster))
-@@ -996,6 +1018,10 @@ class OpenMetricsPMDA(PMDA):
-                 return [self.stats_parse_time[inst], 1] if inst in self.stats_parse_time else [c_api.PM_ERR_INST, 0]
-             elif item == 4: # $(pmda_name).control.debug
-                 return [self.dbg, 1]
-+            elif item == 5: # per-source status string
-+                return [self.stats_status[inst], 1] if inst in self.stats_status else [c_api.PM_ERR_INST, 0]
-+            elif item == 6: # per-source status code
-+                return [self.stats_status_code[inst], 1] if inst in self.stats_status_code else [c_api.PM_ERR_INST, 0]
-             return [c_api.PM_ERR_PMID, 0]
- 
-         self.assert_source_invariants(cluster=cluster)
-
-commit 63605e3db4b2821df2a6ffb21507af91d97f3a8b
-Author: Mark Goodwin <mgoodwin@redhat.com>
-Date:   Fri Jun 19 10:02:04 2020 +1000
-
-    qa/1102: tweak openmetrics QA to be more deterministic
-    
-    Now that pmdaopenmetrics is Installed by default with the localhost
-    grafana metrics URL configured, after _pmdaopenmetrics_save_config
-    we need to _pmdaopenmetrics_remove before _pmdaopenmetrics_install
-    to make qa/1102 deterministic.
-
-diff --git a/qa/1102 b/qa/1102
-index f573d14f4..98ff61f5e 100755
---- a/qa/1102
-+++ b/qa/1102
-@@ -46,6 +46,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15
- _stop_auto_restart pmcd
- 
- _pmdaopenmetrics_save_config
-+_pmdaopenmetrics_remove
- _pmdaopenmetrics_install
- 
- port=`_find_free_port 10000`
-diff --git a/qa/1102.out b/qa/1102.out
-index 5094e4a82..aa74abe44 100644
---- a/qa/1102.out
-+++ b/qa/1102.out
-@@ -1,5 +1,12 @@
- QA output created by 1102
- 
-+=== remove openmetrics agent ===
-+Culling the Performance Metrics Name Space ...
-+openmetrics ... done
-+Updating the PMCD control file, and notifying PMCD ...
-+[...removing files...]
-+Check openmetrics metrics have gone away ... OK
-+
- === openmetrics agent installation ===
- Fetch and desc openmetrics metrics: success
- 
-
-commit 649a0c3a2745f549b139ce1250e38a1e90308426
-Author: Mark Goodwin <mgoodwin@redhat.com>
-Date:   Fri Jun 19 09:55:58 2020 +1000
-
-    qa: improve _filter_pmda_remove() in common.filter
-    
-    Filter "Job for pmcd.service canceled" in _filter_pmda_remove.
-    Systemd sometimes (uncommonly) prints this if a PMDA is still
-    starting when a QA test ./Removes it.
-
-diff --git a/qa/common.filter b/qa/common.filter
-index a53d4a49d..b327abedc 100644
---- a/qa/common.filter
-+++ b/qa/common.filter
-@@ -760,6 +760,7 @@ _filter_pmda_remove()
-     _filter_pmda_install |
-     sed \
- 	-e '/Removing files/d' \
-+	-e '/Job for pmcd.service canceled/d' \
- 	-e '/Updating the PMCD control file/c\
- Updating the PMCD control file, and notifying PMCD ...\
- [...removing files...]'
diff --git a/SOURCES/redhat-bugzilla-1848995.patch b/SOURCES/redhat-bugzilla-1848995.patch
deleted file mode 100644
index edabc1a..0000000
--- a/SOURCES/redhat-bugzilla-1848995.patch
+++ /dev/null
@@ -1,17 +0,0 @@
-BZ 1848995 - Intermittent pminfo crashes (core dumps)
-51bb36a84 libpcp: minor clarification to previous fix to use __pmHashInit
-d0df00ad1 derive_parser.y.in: fix of incomplete initialization of __pmHashCtl struct for F_REGEX node
-
-diff --git a/src/libpcp/src/derive_parser.y.in b/src/libpcp/src/derive_parser.y.in
-index 9ed375e01..6756daa77 100644
---- a/src/libpcp/src/derive_parser.y.in
-+++ b/src/libpcp/src/derive_parser.y.in
-@@ -2598,6 +2598,8 @@ regexpr	: opt_bang L_PATTERN
- 		      return -1;
- 		  }
- 		  np->data.pattern->invert = $1;
-+		  np->data.pattern->used = 0;
-+		  __pmHashInit(&np->data.pattern->hash);
- 		  $$ = np;
- 		}
- 
diff --git a/SOURCES/redhat-bugzilla-1849511.patch b/SOURCES/redhat-bugzilla-1849511.patch
deleted file mode 100644
index 864a91d..0000000
--- a/SOURCES/redhat-bugzilla-1849511.patch
+++ /dev/null
@@ -1,996 +0,0 @@
-BZ 1849511 - resolve covscan and other issues from upstream QA 
-f7f1dd546 pmproxy: complete handling of HTTP/1.1 TRACE requests
-cc662872b qa: add pcp-free-tera archive to pcp-testsuite package
-80639d05b pmlogger_check.sh: major overhaul (diags and systemd fixups)
-460b7ac2a src/pmlogger/rc_pmlogger: use --quick to pmlogger_check
-0b3b4d4ee src/pmlogger/pmlogger_check.service.in: add --skip-primary arg to pmlogger_check
-3a68366a8 src/pmlogger/pmlogger.service.in: change ancillary services from After to Before
-5d65a6035 src/pmlogger/pmlogger_daily.sh: similar changes to pmlogger_check.sh
-ace576907 src/pmlogger/pmlogger_check.sh: fix locking snarfoo
-2b2c3db11 src/pmlogger/pmlogger_daily.sh: fix diagnostic spaghetti
-4cc54287f pmproxy: allow URLs up to 8k in length
-
-diff -auNr pcp-5.1.1-004/man/man1/pmlogger_check.1 pcp-5.1.1-005/man/man1/pmlogger_check.1
---- pcp-5.1.1-004/man/man1/pmlogger_check.1	2020-04-07 13:31:03.000000000 +1000
-+++ pcp-5.1.1-005/man/man1/pmlogger_check.1	2020-06-22 20:08:18.454403788 +1000
-@@ -19,7 +19,7 @@
- \f3pmlogger_daily\f1 \- administration of Performance Co-Pilot archive log files
- .SH SYNOPSIS
- .B $PCP_BINADM_DIR/pmlogger_check
--[\f3\-CNsTV?\f1]
-+[\f3\-CNpqsTV?\f1]
- [\f3\-c\f1 \f2control\f1]
- [\f3\-l\f1 \f2logfile\f1]
- .br
-@@ -269,6 +269,20 @@
- .TP
- \fB\-p\fR
- If this option is specified for
-+.B pmlogger_check
-+then any line from the control files for the
-+.I primary
-+.B pmlogger
-+will be ignored.
-+This option is intended for environments where some system daemon,
-+like
-+.BR systemd (1),
-+is responsible for controlling (starting, stopping, restarting, etc.) the
-+.I primary
-+.BR pmlogger .
-+.TP
-+\fB\-p\fR
-+If this option is specified for
- .B pmlogger_daily
- then the status of the daily processing is polled and if the daily
- .BR pmlogger (1)
-@@ -296,6 +310,12 @@
- .B pmlogger_daily
- are mutually exclusive.
- .TP
-+\fB\-q\fR
-+If this option is specified for
-+.B pmlogger_check
-+then the script will ``quickstart'' avoiding any optional processing
-+like file compression.
-+.TP
- \fB\-r\fR, \fB\-\-norewrite\fR
- This command line option acts as an override and prevents all archive
- rewriting with
-diff -auNr pcp-5.1.1-004/qa/1837 pcp-5.1.1-005/qa/1837
---- pcp-5.1.1-004/qa/1837	2020-06-22 20:00:17.636331169 +1000
-+++ pcp-5.1.1-005/qa/1837	2020-06-22 20:08:18.457403819 +1000
-@@ -1,6 +1,6 @@
- #!/bin/sh
- # PCP QA Test No. 1837
--# Exercise PMWEBAPI handling server OPTIONS.
-+# Exercise PMWEBAPI handling server OPTIONS and TRACE.
- #
- # Copyright (c) 2020 Red Hat.  All Rights Reserved.
- #
-@@ -43,7 +43,12 @@
- # real QA test starts here
- _service pmproxy restart >/dev/null 2>&1
- 
--curl -isS --request-target "*" -X OPTIONS http://localhost:44322 \
-+echo; echo "=== OPTIONS"
-+curl -isS -X OPTIONS --request-target "*" http://localhost:44322 \
-+	2>&1 | tee -a $here/$seq.full | _webapi_header_filter
-+
-+echo; echo "=== TRACE"
-+curl -isS -X TRACE http://localhost:44322 \
- 	2>&1 | tee -a $here/$seq.full | _webapi_header_filter
- 
- echo >>$here/$seq.full
-diff -auNr pcp-5.1.1-004/qa/1837.out pcp-5.1.1-005/qa/1837.out
---- pcp-5.1.1-004/qa/1837.out	2020-06-22 20:00:17.637331179 +1000
-+++ pcp-5.1.1-005/qa/1837.out	2020-06-22 20:08:18.457403819 +1000
-@@ -1,6 +1,17 @@
- QA output created by 1837
- 
-+=== OPTIONS
-+
- Access-Control-Allow-Methods: GET, PUT, HEAD, POST, TRACE, OPTIONS
- Content-Length: 0
- Date: DATE
- HTTP/1.1 200 OK
-+
-+=== TRACE
-+
-+Accept: */*
-+Content-Length: 0
-+Date: DATE
-+HTTP/1.1 200 OK
-+Host: localhost:44322
-+User-Agent: curl VERSION
-diff -auNr pcp-5.1.1-004/qa/archives/GNUmakefile pcp-5.1.1-005/qa/archives/GNUmakefile
---- pcp-5.1.1-004/qa/archives/GNUmakefile	2020-03-19 15:15:42.000000000 +1100
-+++ pcp-5.1.1-005/qa/archives/GNUmakefile	2020-06-22 20:08:18.461403861 +1000
-@@ -35,6 +35,7 @@
- 	  pcp-atop.0.xz pcp-atop.meta pcp-atop.index \
- 	  pcp-atop-boot.0.xz pcp-atop-boot.meta pcp-atop-boot.index \
- 	  pcp-dstat.0.xz pcp-dstat.meta pcp-dstat.index \
-+	  pcp-free-tera.0.xz pcp-free-tera.meta.xz pcp-free-tera.index \
- 	  pcp-hotatop.0.xz pcp-hotatop.meta pcp-hotatop.index \
- 	  pcp-zeroconf.0.xz pcp-zeroconf.meta pcp-zeroconf.index \
- 	  value-test.0.xz value-test.meta value-test.index \
-diff -auNr pcp-5.1.1-004/qa/common.check pcp-5.1.1-005/qa/common.check
---- pcp-5.1.1-004/qa/common.check	2020-06-22 20:00:17.637331179 +1000
-+++ pcp-5.1.1-005/qa/common.check	2020-06-22 20:08:18.459403840 +1000
-@@ -2697,6 +2697,7 @@
-     | col -b \
-     | sed \
- 	-e 's/^\(Content-Length:\) [1-9][0-9]*/\1 SIZE/g' \
-+	-e 's/^\(User-Agent: curl\).*/\1 VERSION/g' \
- 	-e 's/^\(Date:\).*/\1 DATE/g' \
- 	-e 's/\(\"context\":\) [0-9][0-9]*/\1 CTXID/g' \
- 	-e '/^Connection: Keep-Alive/d' \
-diff -auNr pcp-5.1.1-004/src/pmlogger/pmlogger_check.service.in pcp-5.1.1-005/src/pmlogger/pmlogger_check.service.in
---- pcp-5.1.1-004/src/pmlogger/pmlogger_check.service.in	2020-05-22 16:40:09.000000000 +1000
-+++ pcp-5.1.1-005/src/pmlogger/pmlogger_check.service.in	2020-06-22 20:08:18.452403767 +1000
-@@ -6,7 +6,7 @@
- [Service]
- Type=oneshot
- TimeoutStartSec=25m
--Environment="PMLOGGER_CHECK_PARAMS=-C"
-+Environment="PMLOGGER_CHECK_PARAMS=-C --skip-primary"
- EnvironmentFile=-@PCP_SYSCONFIG_DIR@/pmlogger_timers
- ExecStart=@PCP_BINADM_DIR@/pmlogger_check $PMLOGGER_CHECK_PARAMS
- WorkingDirectory=@PCP_VAR_DIR@
-diff -auNr pcp-5.1.1-004/src/pmlogger/pmlogger_check.sh pcp-5.1.1-005/src/pmlogger/pmlogger_check.sh
---- pcp-5.1.1-004/src/pmlogger/pmlogger_check.sh	2020-05-04 09:52:04.000000000 +1000
-+++ pcp-5.1.1-005/src/pmlogger/pmlogger_check.sh	2020-06-22 20:13:04.029416598 +1000
-@@ -36,16 +36,24 @@
- echo >$tmp/lock
- prog=`basename $0`
- PROGLOG=$PCP_LOG_DIR/pmlogger/$prog.log
-+MYPROGLOG=$PROGLOG.$$
- USE_SYSLOG=true
- 
- _cleanup()
- {
-+    if [ -s "$MYPROGLOG" ]
-+    then
-+	rm -f "$PROGLOG"
-+	mv "$MYPROGLOG" "$PROGLOG"
-+    else
-+	rm -f "$MYPROGLOG"
-+    fi
-     $USE_SYSLOG && [ $status -ne 0 ] && \
-     $PCP_SYSLOG_PROG -p daemon.error "$prog failed - see $PROGLOG"
--    [ -s "$PROGLOG" ] || rm -f "$PROGLOG"
-     lockfile=`cat $tmp/lock 2>/dev/null`
-     rm -f "$lockfile"
-     rm -rf $tmp
-+    $VERY_VERBOSE && echo "End: `date '+%F %T.%N'`"
- }
- trap "_cleanup; exit \$status" 0 1 2 3 15
- 
-@@ -86,6 +94,8 @@
- CHECK_RUNLEVEL=false
- START_PMLOGGER=true
- STOP_PMLOGGER=false
-+QUICKSTART=false
-+SKIP_PRIMARY=false
- 
- echo > $tmp/usage
- cat >> $tmp/usage << EOF
-@@ -94,6 +104,8 @@
-   -l=FILE,--logfile=FILE  send important diagnostic messages to FILE
-   -C                      query system service runlevel information
-   -N,--showme             perform a dry run, showing what would be done
-+  -p,--skip-primary       do not start or stop the primary pmlogger instance
-+  -q,--quick              quick start, no compression
-   -s,--stop               stop pmlogger processes instead of starting them
-   -T,--terse              produce a terser form of output
-   -V,--verbose            increase diagnostic verbosity
-@@ -117,6 +129,7 @@
- 	-C)	CHECK_RUNLEVEL=true
- 		;;
- 	-l)	PROGLOG="$2"
-+		MYPROGLOG="$PROGLOG".$$
- 		USE_SYSLOG=false
- 		daily_args="${daily_args} -l $2.from.check"
- 		shift
-@@ -129,6 +142,10 @@
- 		KILL="echo + kill"
- 		daily_args="${daily_args} -N"
- 		;;
-+	-p)	SKIP_PRIMARY=true
-+		;;
-+	-q)	QUICKSTART=true
-+		;;
- 	-s)	START_PMLOGGER=false
- 		STOP_PMLOGGER=true
- 		;;
-@@ -162,9 +179,15 @@
- 
- _compress_now()
- {
--    # If $PCP_COMPRESSAFTER=0 in the control file(s), compress archives now.
--    # Invoked just before exit when this script has finished successfully.
--    $PCP_BINADM_DIR/pmlogger_daily -K $daily_args
-+    if $QUICKSTART
-+    then
-+	$VERY_VERBOSE && echo "Skip compression, -q/--quick on command line"
-+    else
-+	# If $PCP_COMPRESSAFTER=0 in the control file(s), compress archives now.
-+	# Invoked just before exit when this script has finished successfully.
-+	$VERY_VERBOSE && echo "Doing compression ..."
-+	$PCP_BINADM_DIR/pmlogger_daily -K $daily_args
-+    fi
- }
- 
- # after argument checking, everything must be logged to ensure no mail is
-@@ -187,26 +210,37 @@
-     #
-     # Exception ($SHOWME, above) is for -N where we want to see the output.
-     #
--    touch "$PROGLOG"
--    chown $PCP_USER:$PCP_GROUP "$PROGLOG" >/dev/null 2>&1
--    exec 1>"$PROGLOG" 2>&1
-+    touch "$MYPROGLOG"
-+    chown $PCP_USER:$PCP_GROUP "$MYPROGLOG" >/dev/null 2>&1
-+    exec 1>"$MYPROGLOG" 2>&1
-+fi
-+
-+if $VERY_VERBOSE
-+then
-+    echo "Start: `date '+%F %T.%N'`"
-+    if `which pstree >/dev/null 2>&1`
-+    then
-+	echo "Called from:"
-+	pstree -spa $$
-+	echo "--- end of pstree output ---"
-+    fi
- fi
- 
- # if SaveLogs exists in the $PCP_LOG_DIR/pmlogger directory then save
--# $PROGLOG there as well with a unique name that contains the date and time
-+# $MYPROGLOG there as well with a unique name that contains the date and time
- # when we're run
- #
- if [ -d $PCP_LOG_DIR/pmlogger/SaveLogs ]
- then
--    now="`date '+%Y%m%d.%H.%M'`"
--    link=`echo $PROGLOG | sed -e "s/$prog/SaveLogs\/$prog.$now/"`
-+    now="`date '+%Y%m%d.%H.%M.%S'`"
-+    link=`echo $MYPROGLOG | sed -e "s/$prog/SaveLogs\/$prog.$now/"`
-     if [ ! -f "$link" ]
-     then
- 	if $SHOWME
- 	then
--	    echo "+ ln $PROGLOG $link"
-+	    echo "+ ln $MYPROGLOG $link"
- 	else
--	    ln $PROGLOG $link
-+	    ln $MYPROGLOG $link
- 	fi
-     fi
- fi
-@@ -273,7 +307,7 @@
- 
- _unlock()
- {
--    rm -f lock
-+    rm -f "$1/lock"
-     echo >$tmp/lock
- }
- 
-@@ -395,6 +429,41 @@
-     echo "$pid"
- }
- 
-+# wait for the local pmcd to get going for a primary pmlogger
-+# (borrowed from qa/common.check)
-+#
-+# wait_for_pmcd [maxdelay]
-+#
-+_wait_for_pmcd()
-+{
-+    # 5 seconds default seems like a reasonable max time to get going
-+    _can_wait=${1-5}
-+    _limit=`expr $_can_wait \* 10`
-+    _i=0
-+    _dead=true
-+    while [ $_i -lt $_limit ]
-+    do
-+	_sts=`pmprobe pmcd.numclients 2>/dev/null | $PCP_AWK_PROG '{print $2}'`
-+	if [ "${_sts:-0}" -gt 0 ]
-+	then
-+	    # numval really > 0, we're done
-+	    #
-+	    _dead=false
-+	    break
-+	fi
-+	pmsleep 0.1
-+	_i=`expr $_i + 1`
-+    done
-+    if $_dead
-+    then
-+	date
-+	echo "Arrgghhh ... pmcd at localhost failed to start after $_can_wait seconds"
-+	echo "=== failing pmprobes ==="
-+	pmprobe pmcd.numclients
-+	status=1
-+    fi
-+}
-+
- _check_archive()
- {
-     if [ ! -e "$logfile" ]
-@@ -531,7 +600,17 @@
- 	cd "$here"
- 	line=`expr $line + 1`
- 
--	$VERY_VERBOSE && echo "[$controlfile:$line] host=\"$host\" primary=\"$primary\" socks=\"$socks\" dir=\"$dir\" args=\"$args\""
-+
-+	if $VERY_VERBOSE 
-+	then
-+	    case "$host"
-+	    in
-+	    \#*|'')	# comment or empty
-+			;;
-+	    *)		echo "[$controlfile:$line] host=\"$host\" primary=\"$primary\" socks=\"$socks\" dir=\"$dir\" args=\"$args\""
-+	    		;;
-+	    esac
-+	fi
- 
- 	case "$host"
- 	in
-@@ -599,6 +678,15 @@
- 	    continue
- 	fi
- 
-+	# if -s/--skip-primary on the command line, do not process
-+	# a control file line for the primary pmlogger
-+	#
-+	if $SKIP_PRIMARY && [ $primary = y ]
-+	then
-+	    $VERY_VERBOSE && echo "Skip, -s/--skip-primary on command line"
-+	    continue
-+	fi
-+
- 	# substitute LOCALHOSTNAME marker in this config line
- 	# (differently for directory and pcp -h HOST arguments)
- 	#
-@@ -610,7 +698,7 @@
- 	then
- 	    pflag=''
- 	    [ $primary = y ] && pflag=' -P'
--	    echo "Check pmlogger$pflag -h $host ... in $dir ..."
-+	    echo "Checking for: pmlogger$pflag -h $host ... in $dir ..."
- 	fi
- 
- 	# check for directory duplicate entries
-@@ -664,19 +752,25 @@
- 	    delay=200	# tenths of a second
- 	    while [ $delay -gt 0 ]
- 	    do
--		if pmlock -v lock >$tmp/out 2>&1
-+		if pmlock -v "$dir/lock" >$tmp/out 2>&1
- 		then
--		    echo $dir/lock >$tmp/lock
-+		    echo "$dir/lock" >$tmp/lock
-+		    if $VERY_VERBOSE
-+		    then
-+			echo "Acquired lock:"
-+			ls -l $dir/lock
-+		    fi
- 		    break
- 		else
- 		    [ -f $tmp/stamp ] || touch -t `pmdate -30M %Y%m%d%H%M` $tmp/stamp
--		    if [ -z "`find lock -newer $tmp/stamp -print 2>/dev/null`" ]
-+		    find $tmp/stamp -newer "$dir/lock" -print 2>/dev/null >$tmp/tmp
-+		    if [ -s $tmp/tmp ]
- 		    then
--			if [ -f lock ]
-+			if [ -f "$dir/lock" ]
- 			then
- 			    echo "$prog: Warning: removing lock file older than 30 minutes"
- 			    LC_TIME=POSIX ls -l $dir/lock
--			    rm -f lock
-+			    rm -f "$dir/lock"
- 			else
- 			    # there is a small timing window here where pmlock
- 			    # might fail, but the lock file has been removed by
-@@ -714,7 +808,7 @@
- 			continue
- 		    fi
- 		fi
--		if [ -f lock ]
-+		if [ -f "$dir/lock" ]
- 		then
- 		    echo "$prog: Warning: is another PCP cron job running concurrently?"
- 		    LC_TIME=POSIX ls -l $dir/lock
-@@ -753,6 +847,14 @@
- 		    $VERY_VERBOSE && echo "primary pmlogger process $pid not running"
- 		    pid=''
- 		fi
-+	    else
-+		if $VERY_VERBOSE
-+		then
-+		    echo "$PCP_TMP_DIR/pmlogger/primary: missing?"
-+		    echo "Contents of $PCP_TMP_DIR/pmlogger"
-+		    ls -l $PCP_TMP_DIR/pmlogger
-+		    echo "--- end of ls output ---"
-+		fi
- 	    fi
- 	else
- 	    for log in $PCP_TMP_DIR/pmlogger/[0-9]*
-@@ -798,6 +900,17 @@
- 		#
- 		PM_LOG_PORT_DIR="$PCP_TMP_DIR/pmlogger"
- 		rm -f "$PM_LOG_PORT_DIR/primary"
-+		# We really starting the primary pmlogger to work, especially
-+		# in the systemd world, so make sure pmcd is ready to accept
-+		# connections.
-+		#
-+		_wait_for_pmcd
-+		if [ "$status" = 1 ]
-+		then
-+		    $VERY_VERBOSE && echo "pmcd not running, skip primary pmlogger"
-+		    _unlock "$dir"
-+		    continue
-+		fi
- 	    else
- 		args="-h $host $args"
- 		envs=""
-@@ -870,7 +983,7 @@
- 	    then
- 		echo
- 		echo "+ ${sock_me}$PMLOGGER $args $LOGNAME"
--		_unlock
-+		_unlock "$dir"
- 		continue
- 	    else
- 		$PCP_BINADM_DIR/pmpost "start pmlogger from $prog for host $host"
-@@ -903,7 +1016,7 @@
- 	    $PCP_ECHO_PROG $PCP_ECHO_N "$pid ""$PCP_ECHO_C" >> $tmp/pmloggers
- 	fi
- 
--	_unlock
-+	_unlock "$dir"
-     done
- }
- 
-diff -auNr pcp-5.1.1-004/src/pmlogger/pmlogger_daily.sh pcp-5.1.1-005/src/pmlogger/pmlogger_daily.sh
---- pcp-5.1.1-004/src/pmlogger/pmlogger_daily.sh	2020-04-07 13:31:03.000000000 +1000
-+++ pcp-5.1.1-005/src/pmlogger/pmlogger_daily.sh	2020-06-22 20:08:18.451403756 +1000
-@@ -31,16 +31,24 @@
- echo >$tmp/lock
- prog=`basename $0`
- PROGLOG=$PCP_LOG_DIR/pmlogger/$prog.log
-+MYPROGLOG=$PROGLOG.$$
- USE_SYSLOG=true
- 
- _cleanup()
- {
-+    if [ -s "$MYPROGLOG" ]
-+    then
-+	rm -f "$PROGLOG"
-+	mv "$MYPROGLOG" "$PROGLOG"
-+    else
-+	rm -f "$MYPROGLOG"
-+    fi
-     $USE_SYSLOG && [ $status -ne 0 ] && \
-     $PCP_SYSLOG_PROG -p daemon.error "$prog failed - see $PROGLOG"
--    [ -s "$PROGLOG" ] || rm -f "$PROGLOG"
-     lockfile=`cat $tmp/lock 2>/dev/null`
-     rm -f "$lockfile" "$PCP_RUN_DIR/pmlogger_daily.pid"
-     rm -rf $tmp
-+    $VERY_VERBOSE && echo "End: `date '+%F %T.%N'`"
- }
- trap "_cleanup; exit \$status" 0 1 2 3 15
- 
-@@ -215,8 +223,10 @@
- 		fi
- 		COMPRESSONLY=true
- 		PROGLOG=$PCP_LOG_DIR/pmlogger/$prog-K.log
-+		MYPROGLOG=$PROGLOG.$$
- 		;;
- 	-l)	PROGLOG="$2"
-+		MYPROGLOG=$PROGLOG.$$
- 		USE_SYSLOG=false
- 		shift
- 		;;
-@@ -278,6 +288,7 @@
- 		# $PCP_LOG_DIR/pmlogger/daily.<date>.trace
- 		#
- 		PROGLOG=$PCP_LOG_DIR/pmlogger/daily.`date "+%Y%m%d.%H.%M"`.trace
-+		MYPROGLOG=$PROGLOG.$$
- 		VERBOSE=true
- 		VERY_VERBOSE=true
- 		MYARGS="$MYARGS -V -V"
-@@ -418,13 +429,23 @@
-     #
-     # Exception ($SHOWME, above) is for -N where we want to see the output.
-     #
--    touch "$PROGLOG"
--    chown $PCP_USER:$PCP_GROUP "$PROGLOG" >/dev/null 2>&1
--    exec 1>"$PROGLOG" 2>&1
-+    touch "$MYPROGLOG"
-+    chown $PCP_USER:$PCP_GROUP "$MYPROGLOG" >/dev/null 2>&1
-+    exec 1>"$MYPROGLOG" 2>&1
-+fi
-+
-+if $VERY_VERBOSE
-+then
-+    echo "Start: `date '+%F %T.%N'`"
-+    if `which pstree >/dev/null 2>&1`
-+    then
-+	echo "Called from:"
-+	pstree -spa $$
-+    fi
- fi
- 
- # if SaveLogs exists in the $PCP_LOG_DIR/pmlogger directory then save
--# $PROGLOG there as well with a unique name that contains the date and time
-+# $MYPROGLOG there as well with a unique name that contains the date and time
- # when we're run ... skip if -N (showme)
- #
- if $SHOWME
-@@ -433,15 +454,15 @@
- else
-     if [ -d $PCP_LOG_DIR/pmlogger/SaveLogs ]
-     then
--	now="`date '+%Y%m%d.%H.%M'`"
--	link=`echo $PROGLOG | sed -e "s/$prog/SaveLogs\/$prog.$now/"`
-+	now="`date '+%Y%m%d.%H.%M.%S'`"
-+	link=`echo $MYPROGLOG | sed -e "s/$prog/SaveLogs\/$prog.$now/"`
- 	if [ ! -f "$link" ]
- 	then
- 	    if $SHOWME
- 	    then
--		echo "+ ln $PROGLOG $link"
-+		echo "+ ln $MYPROGLOG $link"
- 	    else
--		ln $PROGLOG $link
-+		ln $MYPROGLOG $link
- 	    fi
- 	fi
-     fi
-@@ -487,19 +508,20 @@
- 	delay=200	# tenths of a second
- 	while [ $delay -gt 0 ]
- 	do
--	    if pmlock -v lock >>$tmp/out 2>&1
-+	    if pmlock -v "$1/lock" >>$tmp/out 2>&1
- 	    then
--		echo $1/lock >$tmp/lock
-+		echo "$1/lock" >$tmp/lock
- 		break
- 	    else
- 		[ -f $tmp/stamp ] || touch -t `pmdate -30M %Y%m%d%H%M` $tmp/stamp
--		if [ ! -z "`find lock -newer $tmp/stamp -print 2>/dev/null`" ]
-+		find $tmp/stamp -newer "$1/lock" -print 2>/dev/null >$tmp/tmp
-+		if [ -s $tmp/tmp ]
- 		then
--		    if [ -f lock ]
-+		    if [ -f "$1/lock" ]
- 		    then
- 			_warning "removing lock file older than 30 minutes"
--			LC_TIME=POSIX ls -l $1/lock
--			rm -f lock
-+			LC_TIME=POSIX ls -l "$1/lock"
-+			rm -f "$1/lock"
- 		    else
- 			# there is a small timing window here where pmlock
- 			# might fail, but the lock file has been removed by
-@@ -517,10 +539,10 @@
- 	then
- 	    # failed to gain mutex lock
- 	    #
--	    if [ -f lock ]
-+	    if [ -f "$1/lock" ]
- 	    then
- 		_warning "is another PCP cron job running concurrently?"
--		LC_TIME=POSIX ls -l $1/lock
-+		LC_TIME=POSIX ls -l "$1/lock"
- 	    else
- 		echo "$prog: `cat $tmp/out`"
- 	    fi
-@@ -534,7 +556,7 @@
- 
- _unlock()
- {
--    rm -f lock
-+    rm -f "$1/lock"
-     echo >$tmp/lock
- }
- 
-@@ -703,6 +725,9 @@
- # if the directory containing the archive matches, then the name
- # of the file is the pid.
- #
-+# The pid(s) (if any) appear on stdout, so be careful to send any
-+# diagnostics to stderr.
-+#
- _get_non_primary_logger_pid()
- {
-     pid=''
-@@ -713,7 +738,7 @@
- 	then
- 	    _host=`sed -n 2p <$log`
- 	    _arch=`sed -n 3p <$log`
--	    $PCP_ECHO_PROG $PCP_ECHO_N "... try $log host=$_host arch=$_arch: ""$PCP_ECHO_C"
-+	    $PCP_ECHO_PROG >&2 $PCP_ECHO_N "... try $log host=$_host arch=$_arch: ""$PCP_ECHO_C"
- 	fi
- 	# throw away stderr in case $log has been removed by now
- 	match=`sed -e '3s@/[^/]*$@@' $log 2>/dev/null | \
-@@ -721,19 +746,19 @@
- BEGIN				{ m = 0 }
- NR == 3 && $0 == "'$dir'"	{ m = 2; next }
- END				{ print m }'`
--	$VERY_VERBOSE && $PCP_ECHO_PROG $PCP_ECHO_N "match=$match ""$PCP_ECHO_C"
-+	$VERY_VERBOSE && $PCP_ECHO_PROG >&2 $PCP_ECHO_N "match=$match ""$PCP_ECHO_C"
- 	if [ "$match" = 2 ]
- 	then
- 	    pid=`echo $log | sed -e 's,.*/,,'`
- 	    if _get_pids_by_name pmlogger | grep "^$pid\$" >/dev/null
- 	    then
--		$VERY_VERBOSE && echo "pmlogger process $pid identified, OK"
-+		$VERY_VERBOSE && echo >&2 "pmlogger process $pid identified, OK"
- 		break
- 	    fi
--	    $VERY_VERBOSE && echo "pmlogger process $pid not running, skip"
-+	    $VERY_VERBOSE && echo >&2 "pmlogger process $pid not running, skip"
- 	    pid=''
- 	else
--	    $VERY_VERBOSE && echo "different directory, skip"
-+	    $VERY_VERBOSE && echo >&2 "different directory, skip"
- 	fi
-     done
-     echo "$pid"
-@@ -1028,6 +1053,8 @@
- 		pid=''
- 	    fi
- 	else
-+	    # pid(s) on stdout, diagnostics on stderr
-+	    #
- 	    pid=`_get_non_primary_logger_pid`
- 	    if $VERY_VERBOSE
- 	    then
-@@ -1458,7 +1485,7 @@
- 	    fi
- 	fi
- 
--	_unlock
-+	_unlock "$dir"
-     done
- }
- 
-diff -auNr pcp-5.1.1-004/src/pmlogger/pmlogger.service.in pcp-5.1.1-005/src/pmlogger/pmlogger.service.in
---- pcp-5.1.1-004/src/pmlogger/pmlogger.service.in	2020-06-22 20:00:17.634331148 +1000
-+++ pcp-5.1.1-005/src/pmlogger/pmlogger.service.in	2020-06-22 20:08:18.452403767 +1000
-@@ -2,7 +2,7 @@
- Description=Performance Metrics Archive Logger
- Documentation=man:pmlogger(1)
- After=network-online.target pmcd.service
--After=pmlogger_check.timer pmlogger_check.path pmlogger_daily.timer pmlogger_daily-poll.timer
-+Before=pmlogger_check.timer pmlogger_check.path pmlogger_daily.timer pmlogger_daily-poll.timer
- BindsTo=pmlogger_check.timer pmlogger_check.path pmlogger_daily.timer pmlogger_daily-poll.timer
- Wants=pmcd.service
- 
-diff -auNr pcp-5.1.1-004/src/pmlogger/rc_pmlogger pcp-5.1.1-005/src/pmlogger/rc_pmlogger
---- pcp-5.1.1-004/src/pmlogger/rc_pmlogger	2020-04-21 10:42:02.000000000 +1000
-+++ pcp-5.1.1-005/src/pmlogger/rc_pmlogger	2020-06-22 20:08:18.453403777 +1000
-@@ -96,7 +96,7 @@
-     bgtmp=`mktemp -d $PCP_DIR/var/tmp/pcp.XXXXXXXXX` || exit 1
-     trap "rm -rf $bgtmp; exit \$bgstatus" 0 1 2 3 15
- 
--    pmlogger_check $VFLAG >$bgtmp/pmcheck.out 2>$bgtmp/pmcheck
-+    pmlogger_check --quick $VFLAG >$bgtmp/pmcheck.out 2>$bgtmp/pmcheck
-     bgstatus=$?
-     if [ -s $bgtmp/pmcheck ]
-     then
-@@ -125,8 +125,6 @@
- 	    false
- 	else
- 	    # Really start the pmlogger instances based on the control file.
--	    # Done in the background to avoid delaying the init script,
--	    # failure notification is external (syslog, log files).
- 	    #
- 	    $ECHO $PCP_ECHO_N "Starting pmlogger ..." "$PCP_ECHO_C"
- 
-@@ -234,11 +232,9 @@
- if [ $VERBOSE_CTL = on ]
- then				# For a verbose startup and shutdown
-     ECHO=$PCP_ECHO_PROG
--    REBUILDOPT=''
-     VFLAG='-V'
- else				# For a quiet startup and shutdown
-     ECHO=:
--    REBUILDOPT=-s
-     VFLAG=
- fi
- 
-diff -auNr pcp-5.1.1-004/src/pmproxy/src/http.c pcp-5.1.1-005/src/pmproxy/src/http.c
---- pcp-5.1.1-004/src/pmproxy/src/http.c	2020-06-22 20:00:17.635331158 +1000
-+++ pcp-5.1.1-005/src/pmproxy/src/http.c	2020-06-22 20:08:18.460403851 +1000
-@@ -324,17 +324,36 @@
- }
- 
- static sds
--http_response_trace(struct client *client)
-+http_response_trace(struct client *client, int sts)
- {
-+    struct http_parser	*parser = &client->u.http.parser;
-     dictIterator	*iterator;
-     dictEntry		*entry;
--    sds			result = sdsempty();
-+    char		buffer[64];
-+    sds			header;
-+
-+    parser->http_major = parser->http_minor = 1;
-+
-+    header = sdscatfmt(sdsempty(),
-+		"HTTP/%u.%u %u %s\r\n"
-+		"%S: Keep-Alive\r\n",
-+		parser->http_major, parser->http_minor,
-+		sts, http_status_mapping(sts), HEADER_CONNECTION);
-+    header = sdscatfmt(header, "%S: %u\r\n", HEADER_CONTENT_LENGTH, 0);
- 
-     iterator = dictGetSafeIterator(client->u.http.headers);
-     while ((entry = dictNext(iterator)) != NULL)
--	result = sdscatfmt("%S: %S\r\n", dictGetKey(entry), dictGetVal(entry));
-+	header = sdscatfmt(header, "%S: %S\r\n", dictGetKey(entry), dictGetVal(entry));
-     dictReleaseIterator(iterator);
--    return result;
-+
-+    header = sdscatfmt(header, "Date: %s\r\n\r\n",
-+		http_date_string(time(NULL), buffer, sizeof(buffer)));
-+
-+    if (pmDebugOptions.http && pmDebugOptions.desperate) {
-+	fprintf(stderr, "trace response to client %p\n", client);
-+	fputs(header, stderr);
-+    }
-+    return header;
- }
- 
- static sds
-@@ -418,7 +437,7 @@
- 	if (client->u.http.parser.method == HTTP_OPTIONS)
- 	    buffer = http_response_access(client, sts, options);
- 	else if (client->u.http.parser.method == HTTP_TRACE)
--	    buffer = http_response_trace(client);
-+	    buffer = http_response_trace(client, sts);
- 	else	/* HTTP_HEAD */
- 	    buffer = http_response_header(client, 0, sts, type);
- 	suffix = NULL;
-@@ -533,6 +552,8 @@
-     if (servlet && servlet->on_release)
- 	servlet->on_release(client);
-     client->u.http.privdata = NULL;
-+    client->u.http.servlet = NULL;
-+    client->u.http.flags = 0;
- 
-     if (client->u.http.headers) {
- 	dictRelease(client->u.http.headers);
-@@ -696,29 +717,39 @@
- {
-     struct client	*client = (struct client *)request->data;
-     struct servlet	*servlet;
--    sds			buffer;
-     int			sts;
- 
-     http_client_release(client);	/* new URL, clean slate */
--    /* server options - https://tools.ietf.org/html/rfc7231#section-4.3.7 */
--    if (length == 1 && *offset == '*' &&
--	client->u.http.parser.method == HTTP_OPTIONS) {
--	buffer = http_response_access(client, HTTP_STATUS_OK, HTTP_SERVER_OPTIONS);
--	client_write(client, buffer, NULL);
--    } else if ((servlet = servlet_lookup(client, offset, length)) != NULL) {
-+    /* pass to servlets handling each of our internal request endpoints */
-+    if ((servlet = servlet_lookup(client, offset, length)) != NULL) {
- 	client->u.http.servlet = servlet;
--	if ((sts = client->u.http.parser.status_code) == 0) {
-+	if ((sts = client->u.http.parser.status_code) != 0)
-+	    http_error(client, sts, "failed to process URL");
-+	else {
- 	    if (client->u.http.parser.method == HTTP_OPTIONS ||
- 		client->u.http.parser.method == HTTP_TRACE ||
- 		client->u.http.parser.method == HTTP_HEAD)
- 		client->u.http.flags |= HTTP_FLAG_NO_BODY;
--	    else
--		client->u.http.flags &= ~HTTP_FLAG_NO_BODY;
- 	    client->u.http.headers = dictCreate(&sdsOwnDictCallBacks, NULL);
--	    return 0;
- 	}
--	http_error(client, sts, "failed to process URL");
--    } else {
-+    }
-+    /* server options - https://tools.ietf.org/html/rfc7231#section-4.3.7 */
-+    else if (client->u.http.parser.method == HTTP_OPTIONS) {
-+	if (length == 1 && *offset == '*') {
-+	    client->u.http.flags |= HTTP_FLAG_NO_BODY;
-+	    client->u.http.headers = dictCreate(&sdsOwnDictCallBacks, NULL);
-+	} else {
-+	    sts = client->u.http.parser.status_code = HTTP_STATUS_BAD_REQUEST;
-+	    http_error(client, sts, "no handler for OPTIONS");
-+	}
-+    }
-+    /* server trace - https://tools.ietf.org/html/rfc7231#section-4.3.8 */
-+    else if (client->u.http.parser.method == HTTP_TRACE) {
-+	client->u.http.flags |= HTTP_FLAG_NO_BODY;
-+	client->u.http.headers = dictCreate(&sdsOwnDictCallBacks, NULL);
-+    }
-+    /* nothing available to respond to this request - inform the client */
-+    else {
- 	sts = client->u.http.parser.status_code = HTTP_STATUS_BAD_REQUEST;
- 	http_error(client, sts, "no handler for URL");
-     }
-@@ -734,7 +765,7 @@
-     if (pmDebugOptions.http && pmDebugOptions.desperate)
- 	printf("Body: %.*s\n(client=%p)\n", (int)length, offset, client);
- 
--    if (servlet->on_body)
-+    if (servlet && servlet->on_body)
- 	return servlet->on_body(client, offset, length);
-     return 0;
- }
-@@ -828,7 +859,7 @@
-     }
- 
-     client->u.http.privdata = NULL;
--    if (servlet->on_headers)
-+    if (servlet && servlet->on_headers)
- 	sts = servlet->on_headers(client, client->u.http.headers);
- 
-     /* HTTP Basic Auth for all servlets */
-@@ -857,13 +888,31 @@
- {
-     struct client	*client = (struct client *)request->data;
-     struct servlet	*servlet = client->u.http.servlet;
-+    sds			buffer;
-+    int			sts;
- 
-     if (pmDebugOptions.http)
- 	fprintf(stderr, "HTTP message complete (client=%p)\n", client);
- 
--    if (servlet && servlet->on_done)
--	return servlet->on_done(client);
--    return 0;
-+    if (servlet) {
-+	if (servlet->on_done)
-+	    return servlet->on_done(client);
-+	return 0;
-+    }
-+
-+    sts = HTTP_STATUS_OK;
-+    if (client->u.http.parser.method == HTTP_OPTIONS) {
-+	buffer = http_response_access(client, sts, HTTP_SERVER_OPTIONS);
-+	client_write(client, buffer, NULL);
-+	return 0;
-+    }
-+    if (client->u.http.parser.method == HTTP_TRACE) {
-+	buffer = http_response_trace(client, sts);
-+	client_write(client, buffer, NULL);
-+	return 0;
-+    }
-+
-+    return 1;
- }
- 
- void
-diff -auNr pcp-5.1.1.orig/qa/1608 pcp-5.1.1/qa/1608
---- pcp-5.1.1.orig/qa/1608	1970-01-01 10:00:00.000000000 +1000
-+++ pcp-5.1.1/qa/1608	2020-06-23 12:16:04.005557293 +1000
-@@ -0,0 +1,58 @@
-+#!/bin/sh
-+# PCP QA Test No. 1608
-+# Exercise a long URL handling in pmproxy.
-+#
-+# Copyright (c) 2020 Red Hat.  All Rights Reserved.
-+#
-+
-+seq=`basename $0`
-+echo "QA output created by $seq"
-+
-+# get standard environment, filters and checks
-+. ./common.product
-+. ./common.filter
-+. ./common.check
-+
-+_check_series
-+which curl >/dev/null 2>&1 || _notrun "No curl binary installed"
-+
-+status=1	# failure is the default!
-+$sudo rm -rf $tmp $tmp.* $seq.full
-+trap "_cleanup; exit \$status" 0 1 2 3 15
-+
-+pmproxy_was_running=false
-+[ -f $PCP_RUN_DIR/pmproxy.pid ] && pmproxy_was_running=true
-+echo "pmproxy_was_running=$pmproxy_was_running" >>$here/$seq.full
-+
-+_cleanup()
-+{
-+    if $pmproxy_was_running
-+    then
-+        echo "Restart pmproxy ..." >>$here/$seq.full
-+        _service pmproxy restart >>$here/$seq.full 2>&1
-+        _wait_for_pmproxy
-+    else
-+        echo "Stopping pmproxy ..." >>$here/$seq.full
-+        _service pmproxy stop >>$here/$seq.full 2>&1
-+    fi
-+    $sudo rm -f $tmp.*
-+}
-+
-+_webapi_failure_filter()
-+{
-+    _webapi_header_filter | \
-+    sed \
-+	-e 's/pmproxy.[0-9][0-9]*.[0-9][0-9]*.[0-9][0-9]*/PMPROXY\/VERSION/g' \
-+    #end
-+}
-+
-+# real QA test starts here
-+_service pmproxy restart >/dev/null 2>&1
-+
-+url="http://localhost:44322/pmapi/context"
-+aaa=`head -c 10000 < /dev/zero | tr '\0' '\141'`
-+curl -isS -X OPTIONS "${url}?${aaa}" | _webapi_failure_filter
-+
-+# success, all done
-+status=0
-+exit
-diff -auNr pcp-5.1.1.orig/qa/1608.out pcp-5.1.1/qa/1608.out
---- pcp-5.1.1.orig/qa/1608.out	1970-01-01 10:00:00.000000000 +1000
-+++ pcp-5.1.1/qa/1608.out	2020-06-23 12:16:04.005557293 +1000
-@@ -0,0 +1,16 @@
-+QA output created by 1608
-+
-+</body>
-+</html>
-+<body>
-+<h1>414 URI Too Long</h1>
-+<head><title>414 URI Too Long</title></head>
-+<html>
-+<p><b>unknown servlet</b>: request URL too long</p><hr>
-+<p><small><i>PMPROXY/VERSION</i></small></p>
-+Access-Control-Allow-Headers: Accept, Accept-Language, Content-Language, Content-Type
-+Access-Control-Allow-Origin: *
-+Content-Length: SIZE
-+Content-Type: text/html
-+Date: DATE
-+HTTP/1.1 414 URI Too Long
-diff -auNr pcp-5.1.1.orig/qa/group pcp-5.1.1/qa/group
---- pcp-5.1.1.orig/qa/group	2020-06-23 12:15:21.335094106 +1000
-+++ pcp-5.1.1/qa/group	2020-06-23 12:16:54.256102754 +1000
-@@ -1717,6 +1717,7 @@
- 1600 pmseries pmcd pmproxy pmlogger local
- 1601 pmseries pmproxy local
- 1602 pmproxy local
-+1608 pmproxy local
- 1622 selinux local
- 1623 libpcp_import collectl local
- 1644 pmda.perfevent local
-diff -auNr pcp-5.1.1.orig/src/pmproxy/src/http.c pcp-5.1.1/src/pmproxy/src/http.c
---- pcp-5.1.1.orig/src/pmproxy/src/http.c	2020-06-23 12:15:21.364094421 +1000
-+++ pcp-5.1.1/src/pmproxy/src/http.c	2020-06-23 12:16:04.008557325 +1000
-@@ -21,7 +21,9 @@
- static int chunked_transfer_size; /* pmproxy.chunksize, pagesize by default */
- static int smallest_buffer_size = 128;
- 
--#define MAX_PARAMS_SIZE 4096
-+/* https://tools.ietf.org/html/rfc7230#section-3.1.1 */
-+#define MAX_URL_SIZE	8192
-+#define MAX_PARAMS_SIZE 8000
- #define MAX_HEADERS_SIZE 128
- 
- static sds HEADER_ACCESS_CONTROL_REQUEST_HEADERS,
-@@ -720,8 +722,13 @@
-     int			sts;
- 
-     http_client_release(client);	/* new URL, clean slate */
-+
-+    if (length >= MAX_URL_SIZE) {
-+	sts = client->u.http.parser.status_code = HTTP_STATUS_URI_TOO_LONG;
-+	http_error(client, sts, "request URL too long");
-+    }
-     /* pass to servlets handling each of our internal request endpoints */
--    if ((servlet = servlet_lookup(client, offset, length)) != NULL) {
-+    else if ((servlet = servlet_lookup(client, offset, length)) != NULL) {
- 	client->u.http.servlet = servlet;
- 	if ((sts = client->u.http.parser.status_code) != 0)
- 	    http_error(client, sts, "failed to process URL");
diff --git a/SPECS/pcp.spec b/SPECS/pcp.spec
index 297467b..4972dca 100644
--- a/SPECS/pcp.spec
+++ b/SPECS/pcp.spec
@@ -1,6 +1,6 @@
 Name:    pcp
-Version: 5.1.1
-Release: 3%{?dist}
+Version: 5.2.1
+Release: 2%{?dist}
 Summary: System-level performance monitoring and performance management
 License: GPLv2+ and LGPLv2+ and CC-BY
 URL:     https://pcp.io
@@ -8,15 +8,6 @@ URL:     https://pcp.io
 %global  bintray https://bintray.com/artifact/download
 Source0: %{bintray}/pcp/source/pcp-%{version}.src.tar.gz
 
-Patch000: redhat-bugzilla-1792971.patch
-Patch001: redhat-bugzilla-1541406.patch
-Patch002: redhat-bugzilla-1846711.patch
-Patch003: redhat-bugzilla-1848995.patch
-Patch004: redhat-bugzilla-1790452.patch
-Patch005: redhat-bugzilla-1846705.patch
-Patch006: redhat-bugzilla-1849511.patch
-Patch007: redhat-bugzilla-1790433.patch
-
 %if 0%{?fedora} >= 26 || 0%{?rhel} > 7
 %global __python2 python2
 %else
@@ -210,6 +201,10 @@ Obsoletes: pcp-webapi-debuginfo < 5.0.0
 Obsoletes: pcp-webapi < 5.0.0
 Provides: pcp-webapi
 
+# PCP discovery service now provided by pmfind
+Obsoletes: pcp-manager-debuginfo < 5.2.0
+Obsoletes: pcp-manager < 5.2.0
+
 # https://fedoraproject.org/wiki/Packaging "C and C++"
 BuildRequires: gcc gcc-c++
 BuildRequires: procps autoconf bison flex
@@ -227,6 +222,7 @@ BuildRequires: %{__python2}-devel
 %endif
 %if !%{disable_python3}
 BuildRequires: python3-devel
+BuildRequires: python3-setuptools
 %endif
 BuildRequires: ncurses-devel
 BuildRequires: readline-devel
@@ -261,7 +257,7 @@ BuildRequires: perl(ExtUtils::MakeMaker) perl(LWP::UserAgent) perl(JSON)
 BuildRequires: perl(LWP::UserAgent) perl(Time::HiRes) perl(Digest::MD5)
 BuildRequires: man %{_hostname_executable}
 %if !%{disable_systemd}
-BuildRequires: systemd-devel
+BuildRequires: systemd-devel systemd-rpm-macros
 %endif
 %if !%{disable_qt}
 BuildRequires: desktop-file-utils
@@ -291,20 +287,23 @@ Obsoletes: pcp-pmda-nvidia < 3.10.5
 
 Requires: pcp-libs = %{version}-%{release}
 
-%global _confdir	%{_sysconfdir}/pcp
-%global _logsdir	%{_localstatedir}/log/pcp
-%global _pmnsdir	%{_localstatedir}/lib/pcp/pmns
-%global _tempsdir	%{_localstatedir}/lib/pcp/tmp
-%global _pmdasdir	%{_localstatedir}/lib/pcp/pmdas
-%global _testsdir	%{_localstatedir}/lib/pcp/testsuite
-%global _selinuxdir	%{_localstatedir}/lib/pcp/selinux
-%global _logconfdir	%{_localstatedir}/lib/pcp/config/pmlogconf
-%global _ieconfdir	%{_localstatedir}/lib/pcp/config/pmieconf
-%global _tapsetdir	%{_datadir}/systemtap/tapset
-%global _bashcompdir	%{_datadir}/bash-completion/completions
-%global _pixmapdir	%{_datadir}/pcp-gui/pixmaps
-%global _hicolordir	%{_datadir}/icons/hicolor
-%global _booksdir	%{_datadir}/doc/pcp-doc
+%global _confdir        %{_sysconfdir}/pcp
+%global _logsdir        %{_localstatedir}/log/pcp
+%global _pmnsdir        %{_localstatedir}/lib/pcp/pmns
+%global _pmnsexecdir    %{_libexecdir}/pcp/pmns
+%global _tempsdir       %{_localstatedir}/lib/pcp/tmp
+%global _pmdasdir       %{_localstatedir}/lib/pcp/pmdas
+%global _pmdasexecdir   %{_libexecdir}/pcp/pmdas
+%global _testsdir       %{_localstatedir}/lib/pcp/testsuite
+%global _selinuxdir     %{_localstatedir}/lib/pcp/selinux
+%global _selinuxexecdir %{_libexecdir}/pcp/selinux
+%global _logconfdir     %{_localstatedir}/lib/pcp/config/pmlogconf
+%global _ieconfdir      %{_localstatedir}/lib/pcp/config/pmieconf
+%global _tapsetdir      %{_datadir}/systemtap/tapset
+%global _bashcompdir    %{_datadir}/bash-completion/completions
+%global _pixmapdir      %{_datadir}/pcp-gui/pixmaps
+%global _hicolordir     %{_datadir}/icons/hicolor
+%global _booksdir       %{_datadir}/doc/pcp-doc
 
 %if 0%{?fedora} >= 20 || 0%{?rhel} >= 8
 %global _with_doc --with-docdir=%{_docdir}/%{name}
@@ -319,7 +318,7 @@ Requires: pcp-libs = %{version}-%{release}
 %endif
 
 %if !%{disable_systemd}
-%global _initddir %{_datadir}/pcp/lib
+%global _initddir %{_libexecdir}/pcp/lib
 %else
 %global _initddir %{_sysconfdir}/rc.d/init.d
 %global _with_initd --with-rcdir=%{_initddir}
@@ -396,7 +395,7 @@ then
     PCP_PMCDCONF_PATH=%{_confdir}/pmcd/pmcd.conf
     if [ -f "$PCP_PMCDCONF_PATH" -a -f "$PCP_PMDAS_DIR/%2/domain.h" ]
     then
-	(cd "$PCP_PMDAS_DIR/%2/" && ./Remove >/dev/null 2>&1)
+        (cd "$PCP_PMDAS_DIR/%2/" && ./Remove >/dev/null 2>&1)
     fi
 fi
 }
@@ -569,23 +568,6 @@ Quality assurance test suite for Performance Co-Pilot (PCP).
 # end testsuite
 
 #
-# pcp-manager
-#
-%package manager
-License: GPLv2+
-Summary: Performance Co-Pilot (PCP) manager daemon
-URL: https://pcp.io
-Requires: pcp = %{version}-%{release} pcp-libs = %{version}-%{release}
-
-%description manager
-An optional daemon (pmmgr) that manages a collection of pmlogger and
-pmie daemons, for a set of discovered local and remote hosts running
-the performance metrics collection daemon (pmcd).  It ensures these
-daemons are running when appropriate, and manages their log rotation
-needs.  It is an alternative to the cron-based pmlogger/pmie service
-scripts.
-
-#
 # perl-PCP-PMDA. This is the PCP agent perl binding.
 #
 %package -n perl-PCP-PMDA
@@ -2076,10 +2058,13 @@ collecting metrics about web server logs.
 License: GPLv2+
 Summary: Performance Co-Pilot (PCP) Zeroconf Package
 URL: https://pcp.io
-Requires: pcp pcp-doc pcp-system-tools
-Requires: pcp-pmda-dm
+Requires: pcp = %{version}-%{release} pcp-libs = %{version}-%{release}
+Requires: pcp-system-tools = %{version}-%{release}
+Requires: pcp-doc = %{version}-%{release}
+Requires: pcp-pmda-dm = %{version}-%{release}
 %if !%{disable_python2} || !%{disable_python3}
-Requires: pcp-pmda-nfsclient pcp-pmda-openmetrics
+Requires: pcp-pmda-nfsclient = %{version}-%{release}
+Requires: pcp-pmda-openmetrics = %{version}-%{release}
 %endif
 %description zeroconf
 This package contains configuration tweaks and files to increase metrics
@@ -2223,14 +2208,6 @@ updated policy package.
 
 %prep
 %setup -q
-%patch000 -p1
-%patch001 -p1
-%patch002 -p1
-%patch003 -p1
-%patch004 -p1
-%patch005 -p1
-%patch006 -p1
-%patch007 -p1
 
 %build
 # fix up build version
@@ -2245,7 +2222,11 @@ make %{?_smp_mflags} default_pcp
 
 %install
 rm -Rf $RPM_BUILD_ROOT
-export NO_CHOWN=true DIST_ROOT=$RPM_BUILD_ROOT
+BACKDIR=`pwd`
+NO_CHOWN=true
+DIST_ROOT=$RPM_BUILD_ROOT
+DIST_TMPFILES=$BACKDIR/install.tmpfiles
+export NO_CHOWN DIST_ROOT DIST_TMPFILES
 make install_pcp
 
 PCP_GUI='pmchart|pmconfirm|pmdumptext|pmmessage|pmquery|pmsnap|pmtime'
@@ -2269,6 +2250,12 @@ rm -fr $RPM_BUILD_ROOT/%{_pmdasdir}/infiniband
 %if %{disable_mssql}
 # remove pmdamssql on platforms lacking MSODBC driver packages.
 rm -fr $RPM_BUILD_ROOT/%{_pmdasdir}/mssql
+rm -fr $RPM_BUILD_ROOT/%{_pmdasexecdir}/mssql
+rm -fr $RPM_BUILD_ROOT/%{_confdir}/mssql
+%endif
+
+%if %{disable_sdt}
+rm -fr $RPM_BUILD_ROOT/%{_tapsetdir}
 %endif
 
 %if %{disable_selinux}
@@ -2299,92 +2286,39 @@ sed -i -e '/^# .*_LOCAL=1/s/^# //' $RPM_BUILD_ROOT/%{_sysconfdir}/sysconfig/{pmc
 %endif
 
 # default chkconfig off (all RPM platforms)
-for f in $RPM_BUILD_ROOT/%{_initddir}/{pcp,pmcd,pmlogger,pmie,pmmgr,pmproxy}; do
-	test -f "$f" || continue
-	sed -i -e '/^# chkconfig/s/:.*$/: - 95 05/' -e '/^# Default-Start:/s/:.*$/:/' $f
+for f in $RPM_BUILD_ROOT/%{_initddir}/{pcp,pmcd,pmlogger,pmie,pmproxy}; do
+    test -f "$f" || continue
+    sed -i -e '/^# chkconfig/s/:.*$/: - 95 05/' -e '/^# Default-Start:/s/:.*$/:/' $f
+done
+
+# list of PMDAs in the base pcp package
+for pmda in jbd2 kvm linux mmv pipe pmcd proc root xfs; do
+    for alt in %{_pmdasdir} %{_pmdasexecdir} %{_confdir}; do
+        [ -d $RPM_BUILD_ROOT/$alt/$pmda ] && echo $alt/$pmda >>base_pmdas.list
+    done
+done
+
+# list of conf directories in base pcp package
+for conf in discover labels nssdb pmafm pmcd pmfind pmie pmieconf pmlogconf \
+    proc simple pipe linux pmlogger pmlogrewrite pmproxy pmsearch pmseries; do
+    for alt in %{_confdir} %{_localstatedir}/lib/pcp/config; do
+        replace=""; [ "$alt" = "%{_confdir}" ] && replace="%config(noreplace)"
+        [ -d $RPM_BUILD_ROOT/$alt/$conf ] && echo "$replace $alt/$conf" >>base_conf.list
+    done
 done
 
-# list of PMDAs in the base pkg
-ls -1 $RPM_BUILD_ROOT/%{_pmdasdir} |\
-  grep -E -v '^simple|sample|trivial|txmon' |\
-  grep -E -v '^perfevent|perfalloc.1' |\
-  grep -E -v '^ib$|^infiniband' |\
-  grep -E -v '^activemq' |\
-  grep -E -v '^bonding' |\
-  grep -E -v '^bind2' |\
-  grep -E -v '^dbping' |\
-  grep -E -v '^docker' |\
-  grep -E -v '^ds389log'|\
-  grep -E -v '^ds389' |\
-  grep -E -v '^elasticsearch' |\
-  grep -E -v '^gpfs' |\
-  grep -E -v '^gpsd' |\
-  grep -E -v '^lio' |\
-  grep -E -v '^lustre' |\
-  grep -E -v '^lustrecomm' |\
-  grep -E -v '^memcache' |\
-  grep -E -v '^mysql' |\
-  grep -E -v '^named' |\
-  grep -E -v '^netfilter' |\
-  grep -E -v '^news' |\
-  grep -E -v '^nfsclient' |\
-  grep -E -v '^nginx' |\
-  grep -E -v '^nutcracker' |\
-  grep -E -v '^oracle' |\
-  grep -E -v '^openmetrics' |\
-  grep -E -v '^pdns' |\
-  grep -E -v '^podman' |\
-  grep -E -v '^postfix' |\
-  grep -E -v '^postgresql' |\
-  grep -E -v '^redis' |\
-  grep -E -v '^rsyslog' |\
-  grep -E -v '^samba' |\
-  grep -E -v '^slurm' |\
-  grep -E -v '^snmp' |\
-  grep -E -v '^statsd' |\
-  grep -E -v '^vmware' |\
-  grep -E -v '^zimbra' |\
-  grep -E -v '^dm' |\
-  grep -E -v '^apache' |\
-  grep -E -v '^bash' |\
-  grep -E -v '^cifs' |\
-  grep -E -v '^cisco' |\
-  grep -E -v '^gfs2' |\
-  grep -E -v '^libvirt' |\
-  grep -E -v '^lmsensors' |\
-  grep -E -v '^logger' |\
-  grep -E -v '^mailq' |\
-  grep -E -v '^mounts' |\
-  grep -E -v '^mssql' |\
-  grep -E -v '^netcheck' |\
-  grep -E -v '^nvidia' |\
-  grep -E -v '^openvswitch' |\
-  grep -E -v '^rabbitmq' |\
-  grep -E -v '^roomtemp' |\
-  grep -E -v '^sendmail' |\
-  grep -E -v '^shping' |\
-  grep -E -v '^smart' |\
-  grep -E -v '^summary' |\
-  grep -E -v '^trace' |\
-  grep -E -v '^weblog' |\
-  grep -E -v '^rpm' |\
-  grep -E -v '^json' |\
-  grep -E -v '^mic' |\
-  grep -E -v '^bcc' |\
-  grep -E -v '^bpftrace' |\
-  grep -E -v '^gluster' |\
-  grep -E -v '^zswap' |\
-  grep -E -v '^unbound' |\
-  grep -E -v '^haproxy' |\
-  sed -e 's#^#'%{_pmdasdir}'\/#' >base_pmdas.list
-
-# all base pcp package files except those split out into sub-packages
+# all base binary files except those split out into sub-packages
 ls -1 $RPM_BUILD_ROOT/%{_bindir} |\
   grep -E -v 'pmiostat|zabbix|zbxpcp|dstat|pmrep|pcp2csv' |\
   grep -E -v 'pcp2spark|pcp2graphite|pcp2influxdb|pcp2zabbix' |\
   grep -E -v 'pcp2elasticsearch|pcp2json|pcp2xlsx|pcp2xml' |\
   grep -E -v 'pmdbg|pmclient|pmerr|genpmda' |\
 sed -e 's#^#'%{_bindir}'\/#' >base_bin.list
+
+# all base pmns files
+echo %{_pmnsdir} >>base_pmns.list
+echo %{_pmnsexecdir} >>base_pmns.list
+
 ls -1 $RPM_BUILD_ROOT/%{_bashcompdir} |\
   grep -E -v 'pcp2spark|pcp2graphite|pcp2influxdb|pcp2zabbix' |\
   grep -E -v 'pcp2elasticsearch|pcp2json|pcp2xlsx|pcp2xml' |\
@@ -2396,10 +2330,10 @@ sed -e 's#^#'%{_bashcompdir}'\/#' >base_bashcomp.list
 # so its also in pcp-system-tools.
 %if !%{disable_python2} || !%{disable_python3}
 ls -1 $RPM_BUILD_ROOT/%{_bindir} |\
-  egrep -e 'pmiostat|pmrep|dstat|pcp2csv' |\
+  grep -E -e 'pmiostat|pmrep|dstat|pcp2csv' |\
   sed -e 's#^#'%{_bindir}'\/#' >pcp-system-tools.list
 ls -1 $RPM_BUILD_ROOT/%{_libexecdir}/pcp/bin |\
-  egrep -e 'atop|dmcache|dstat|free|iostat|ipcs|lvmcache|mpstat' \
+  grep -E -e 'atop|dmcache|dstat|free|iostat|ipcs|lvmcache|mpstat' \
         -e 'numastat|pidstat|shping|tapestat|uptime|verify' |\
   sed -e 's#^#'%{_libexecdir}/pcp/bin'\/#' >>pcp-system-tools.list
 %endif
@@ -2407,6 +2341,8 @@ ls -1 $RPM_BUILD_ROOT/%{_libexecdir}/pcp/bin |\
 %if !%{disable_selinux}
 ls -1 $RPM_BUILD_ROOT/%{_selinuxdir} |\
   sed -e 's#^#'%{_selinuxdir}'\/#' > pcp-selinux.list
+ls -1 $RPM_BUILD_ROOT/%{_selinuxexecdir} |\
+  sed -e 's#^#'%{_selinuxexecdir}'\/#' >> pcp-selinux.list
 ls -1 $RPM_BUILD_ROOT/%{_libexecdir}/pcp/bin |\
   grep -E 'selinux-setup' |\
   sed -e 's#^#'%{_libexecdir}/pcp/bin'\/#' >> pcp-selinux.list
@@ -2418,7 +2354,10 @@ ls -1 $RPM_BUILD_ROOT/%{_libexecdir}/pcp/bin |\
   grep -E -v 'numastat|shping|tapestat|uptime|verify|selinux-setup' |\
 %endif
   grep -E -v 'pmlogger_daily_report' |\
+  grep -E -v 'pmsnap' |\
   sed -e 's#^#'%{_libexecdir}/pcp/bin'\/#' >base_exec.list
+echo %{_libexecdir}/pcp/lib >>base_exec.list
+
 ls -1 $RPM_BUILD_ROOT/%{_booksdir} |\
   sed -e 's#^#'%{_booksdir}'\/#' > pcp-doc.list
 ls -1 $RPM_BUILD_ROOT/%{_mandir}/man1 |\
@@ -2427,6 +2366,7 @@ ls -1 $RPM_BUILD_ROOT/%{_mandir}/man5 |\
   sed -e 's#^#'%{_mandir}'\/man5\/#' >>pcp-doc.list
 ls -1 $RPM_BUILD_ROOT/%{_datadir}/pcp/demos/tutorials |\
   sed -e 's#^#'%{_datadir}/pcp/demos/tutorials'\/#' >>pcp-doc.list
+
 %if !%{disable_qt}
 ls -1 $RPM_BUILD_ROOT/%{_pixmapdir} |\
   sed -e 's#^#'%{_pixmapdir}'\/#' > pcp-gui.list
@@ -2434,18 +2374,28 @@ ls -1 $RPM_BUILD_ROOT/%{_hicolordir} |\
   sed -e 's#^#'%{_hicolordir}'\/#' >> pcp-gui.list
 cat base_bin.list base_exec.list base_bashcomp.list |\
   grep -E "$PCP_GUI" >> pcp-gui.list
+echo %{_confdir}/pmchart >>pcp-gui.list
+echo %{_libexecdir}/pcp/bin/pmsnap >>pcp-gui.list
 %endif
+
 ls -1 $RPM_BUILD_ROOT/%{_logconfdir}/ |\
     sed -e 's#^#'%{_logconfdir}'\/#' |\
     grep -E -v 'zeroconf' >pcp-logconf.list
 ls -1 $RPM_BUILD_ROOT/%{_ieconfdir}/ |\
     sed -e 's#^#'%{_ieconfdir}'\/#' |\
     grep -E -v 'zeroconf' >pcp-ieconf.list
-cat base_pmdas.list base_bin.list base_exec.list base_bashcomp.list \
-    pcp-logconf.list pcp-ieconf.list |\
-  grep -E -v 'pmdaib|pmmgr|pmsnap|2pcp|pmdas/systemd|zeroconf' |\
+
+# generate full base package file list
+cat base_pmdas.list base_conf.list base_bin.list base_exec.list base_bashcomp.list \
+    pcp-logconf.list pcp-ieconf.list base_pmns.list |\
+  grep -E -v 'pmdaib|pmsnap|2pcp|pmdas/systemd|zeroconf' |\
   grep -E -v "$PCP_GUI|pixmaps|hicolor|pcp-doc|tutorials|selinux" |\
-  grep -E -v %{_confdir} | grep -E -v %{_logsdir} > base.list
+  grep -E -v %{_logsdir} > base.list
+%if !%{disable_systemd}
+mkdir -p $RPM_BUILD_ROOT/%{_tmpfilesdir}
+mv $DIST_TMPFILES $RPM_BUILD_ROOT/%{_tmpfilesdir}/pcp.conf
+echo %{_tmpfilesdir}/pcp.conf >> base.list
+%endif
 
 # all devel pcp package files except those split out into sub packages
 ls -1 $RPM_BUILD_ROOT/%{_mandir}/man3 |\
@@ -2455,6 +2405,10 @@ sed -e 's#^#'%{_datadir}'\/pcp\/demos\/#' | grep -E -v tutorials >> devel.list
 ls -1 $RPM_BUILD_ROOT/%{_bindir} |\
 grep -E 'pmdbg|pmclient|pmerr|genpmda' |\
 sed -e 's#^#'%{_bindir}'\/#' >>devel.list
+for pmda in sample simple trivial txmon; do
+    echo %{_libexecdir}/pcp/pmdas/$pmda >>devel.list
+done
+echo %{_confdir}/simple/simple.conf >>devel.list
 
 %pre testsuite
 test -d %{_testsdir} || mkdir -p -m 755 %{_testsdir}
@@ -2487,18 +2441,6 @@ getent passwd pcp >/dev/null || \
   useradd -c "Performance Co-Pilot" -g pcp -d %{_localstatedir}/lib/pcp -M -r -s /sbin/nologin pcp
 exit 0
 
-%preun manager
-if [ "$1" -eq 0 ]
-then
-%if !%{disable_systemd}
-    systemctl --no-reload disable pmmgr.service >/dev/null 2>&1
-    systemctl stop pmmgr.service >/dev/null 2>&1
-%else
-    /sbin/service pmmgr stop >/dev/null 2>&1
-    /sbin/chkconfig --del pmmgr >/dev/null 2>&1
-%endif
-fi
-
 %if !%{disable_rpm}
 %preun pmda-rpm
 %{pmda_remove "$1" "rpm"}
@@ -2753,36 +2695,27 @@ then
        %systemd_preun pmie.service
        %systemd_preun pmproxy.service
        %systemd_preun pmcd.service
-	systemctl stop pmlogger.service >/dev/null 2>&1
-	systemctl stop pmie.service >/dev/null 2>&1
-	systemctl stop pmproxy.service >/dev/null 2>&1
-	systemctl stop pmcd.service >/dev/null 2>&1
+       systemctl stop pmlogger.service >/dev/null 2>&1
+       systemctl stop pmie.service >/dev/null 2>&1
+       systemctl stop pmproxy.service >/dev/null 2>&1
+       systemctl stop pmcd.service >/dev/null 2>&1
     %else
-	/sbin/service pmlogger stop >/dev/null 2>&1
-	/sbin/service pmie stop >/dev/null 2>&1
-	/sbin/service pmproxy stop >/dev/null 2>&1
-	/sbin/service pmcd stop >/dev/null 2>&1
-
-	/sbin/chkconfig --del pcp >/dev/null 2>&1
-	/sbin/chkconfig --del pmcd >/dev/null 2>&1
-	/sbin/chkconfig --del pmlogger >/dev/null 2>&1
-	/sbin/chkconfig --del pmie >/dev/null 2>&1
-	/sbin/chkconfig --del pmproxy >/dev/null 2>&1
+       /sbin/service pmlogger stop >/dev/null 2>&1
+       /sbin/service pmie stop >/dev/null 2>&1
+       /sbin/service pmproxy stop >/dev/null 2>&1
+       /sbin/service pmcd stop >/dev/null 2>&1
+
+       /sbin/chkconfig --del pcp >/dev/null 2>&1
+       /sbin/chkconfig --del pmcd >/dev/null 2>&1
+       /sbin/chkconfig --del pmlogger >/dev/null 2>&1
+       /sbin/chkconfig --del pmie >/dev/null 2>&1
+       /sbin/chkconfig --del pmproxy >/dev/null 2>&1
     %endif
     # cleanup namespace state/flag, may still exist
     PCP_PMNS_DIR=%{_pmnsdir}
     rm -f "$PCP_PMNS_DIR/.NeedRebuild" >/dev/null 2>&1
 fi
 
-%post manager
-chown -R pcp:pcp %{_logsdir}/pmmgr 2>/dev/null
-%if !%{disable_systemd}
-    systemctl condrestart pmmgr.service >/dev/null 2>&1
-%else
-    /sbin/chkconfig --add pmmgr >/dev/null 2>&1
-    /sbin/service pmmgr condrestart
-%endif
-
 %post zeroconf
 PCP_PMDAS_DIR=%{_pmdasdir}
 PCP_SYSCONFIG_DIR=%{_sysconfdir}/sysconfig
@@ -2791,7 +2724,7 @@ PCP_PMCDCONF_PATH=%{_confdir}/pmcd/pmcd.conf
 for PMDA in dm nfsclient openmetrics ; do
     if ! grep -q "$PMDA/pmda$PMDA" "$PCP_PMCDCONF_PATH"
     then
-	%{install_file "$PCP_PMDAS_DIR/$PMDA" .NeedInstall}
+        %{install_file "$PCP_PMDAS_DIR/$PMDA" .NeedInstall}
     fi
 done
 # increase default pmlogger recording frequency
@@ -2873,8 +2806,8 @@ chown -R pcp:pcp %{_logsdir}/pmproxy 2>/dev/null
 
 %triggerun selinux -- container-selinux
 %{selinux_handle_policy "$1" "pcpupstream-container"}
-
 %endif
+
 %files -f base.list
 #
 # Note: there are some headers (e.g. domain.h) and in a few cases some
@@ -2914,6 +2847,7 @@ chown -R pcp:pcp %{_logsdir}/pmproxy 2>/dev/null
 %attr(0775,pcp,pcp) %{_logsdir}/pmlogger
 %attr(0775,pcp,pcp) %{_logsdir}/pmie
 %attr(0775,pcp,pcp) %{_logsdir}/pmproxy
+%attr(0775,pcp,pcp) %{_logsdir}/pmfind
 %{_localstatedir}/lib/pcp/pmns
 %{_initddir}/pcp
 %{_initddir}/pmcd
@@ -2929,20 +2863,16 @@ chown -R pcp:pcp %{_logsdir}/pmproxy 2>/dev/null
 # services and timers replacing the old cron scripts
 %{_unitdir}/pmlogger_check.service
 %{_unitdir}/pmlogger_check.timer
-%{_unitdir}/pmlogger_check.path
 %{_unitdir}/pmlogger_daily.service
 %{_unitdir}/pmlogger_daily.timer
 %{_unitdir}/pmlogger_daily-poll.service
 %{_unitdir}/pmlogger_daily-poll.timer
 %{_unitdir}/pmie_check.timer
-%{_unitdir}/pmie_check.path
 %{_unitdir}/pmie_check.service
 %{_unitdir}/pmie_check.timer
-%{_unitdir}/pmie_check.path
 %{_unitdir}/pmie_daily.service
 %{_unitdir}/pmie_daily.timer
 %{_unitdir}/pmfind.timer
-%{_unitdir}/pmfind.path
 %config(noreplace) %{_sysconfdir}/sysconfig/pmie_timers
 %config(noreplace) %{_sysconfdir}/sysconfig/pmlogger_timers
 %else
@@ -2986,7 +2916,6 @@ chown -R pcp:pcp %{_logsdir}/pmproxy 2>/dev/null
 %ghost %dir %attr(0775,pcp,pcp) %{_localstatedir}/run/pcp
 %{_localstatedir}/lib/pcp/config/pmafm
 %dir %attr(0775,pcp,pcp) %{_localstatedir}/lib/pcp/config/pmie
-%{_localstatedir}/lib/pcp/config/pmie
 %{_localstatedir}/lib/pcp/config/pmieconf
 %dir %attr(0775,pcp,pcp) %{_localstatedir}/lib/pcp/config/pmlogger
 %{_localstatedir}/lib/pcp/config/pmlogger/*
@@ -3009,8 +2938,11 @@ chown -R pcp:pcp %{_logsdir}/pmproxy 2>/dev/null
 %else
 %config(noreplace) %{_sysconfdir}/cron.d/pcp-pmlogger-daily-report
 %endif
-%{_ieconfdir}/zeroconf
-%{_logconfdir}/zeroconf
+%config(noreplace) %{_ieconfdir}/zeroconf
+%config(noreplace) %{_logconfdir}/zeroconf
+%config(noreplace) %{_confdir}/pmlogconf/zeroconf
+%config(noreplace) %{_confdir}/pmieconf/zeroconf
+%dir %attr(0775,pcp,pcp) %{_logsdir}/sa
 
 #additional pmlogger config files
 
@@ -3019,6 +2951,8 @@ chown -R pcp:pcp %{_logsdir}/pmproxy 2>/dev/null
 %{_includedir}/pcp/builddefs
 %{_includedir}/pcp/buildrules
 %config %{_sysconfdir}/pcp.conf
+%dir %{_confdir}/derived
+%config %{_confdir}/derived/*
 %dir %{_localstatedir}/lib/pcp/config/derived
 %config %{_localstatedir}/lib/pcp/config/derived/*
 
@@ -3046,10 +2980,12 @@ chown -R pcp:pcp %{_logsdir}/pmproxy 2>/dev/null
 
 %files devel -f devel.list
 %{_datadir}/pcp/examples
+%dir %{_datadir}/pcp/demos
 
 # PMDAs that ship src and are not for production use
 # straight out-of-the-box, for devel or QA use only.
 %{_pmdasdir}/simple
+%config(noreplace) %{_confdir}/simple
 %{_pmdasdir}/sample
 %{_pmdasdir}/trivial
 %{_pmdasdir}/txmon
@@ -3058,16 +2994,6 @@ chown -R pcp:pcp %{_logsdir}/pmproxy 2>/dev/null
 %defattr(-,pcpqa,pcpqa)
 %{_testsdir}
 
-%files manager
-%{_initddir}/pmmgr
-%if !%{disable_systemd}
-%{_unitdir}/pmmgr.service
-%endif
-%{_libexecdir}/pcp/bin/pmmgr
-%attr(0775,pcp,pcp) %{_logsdir}/pmmgr
-%config(missingok,noreplace) %{_confdir}/pmmgr
-%config(noreplace) %{_confdir}/pmmgr/pmmgr.options
-
 %files import-sar2pcp
 %{_bindir}/sar2pcp
 
@@ -3086,168 +3012,233 @@ chown -R pcp:pcp %{_logsdir}/pmproxy 2>/dev/null
 %if !%{disable_podman}
 %files pmda-podman
 %{_pmdasdir}/podman
+%{_pmdasexecdir}/podman
 %endif
 
 %if !%{disable_statsd}
 %files pmda-statsd
 %{_pmdasdir}/statsd
+%{_pmdasexecdir}/statsd
+%{_confdir}/statsd
 %config(noreplace) %{_pmdasdir}/statsd/pmdastatsd.ini
 %endif
 
 %if !%{disable_perfevent}
 %files pmda-perfevent
 %{_pmdasdir}/perfevent
+%{_pmdasexecdir}/perfevent
+%{_confdir}/perfevent
 %config(noreplace) %{_pmdasdir}/perfevent/perfevent.conf
 %endif
 
 %if !%{disable_infiniband}
 %files pmda-infiniband
-%{_pmdasdir}/ib
 %{_pmdasdir}/infiniband
+%{_pmdasexecdir}/infiniband
 %endif
 
 %files pmda-activemq
 %{_pmdasdir}/activemq
+%{_pmdasexecdir}/activemq
 
 %files pmda-bonding
 %{_pmdasdir}/bonding
+%{_pmdasexecdir}/bonding
 
 %files pmda-bind2
 %{_pmdasdir}/bind2
+%{_pmdasexecdir}/bind2
+%{_confdir}/bind2
 
 %files pmda-dbping
 %{_pmdasdir}/dbping
+%{_pmdasexecdir}/dbping
 
 %files pmda-ds389log
 %{_pmdasdir}/ds389log
+%{_pmdasexecdir}/ds389log
 
 %files pmda-ds389
 %{_pmdasdir}/ds389
+%{_pmdasexecdir}/ds389
 
 %files pmda-elasticsearch
 %{_pmdasdir}/elasticsearch
+%{_pmdasexecdir}/elasticsearch
+%{_confdir}/elasticsearch
 
 %files pmda-openvswitch
 %{_pmdasdir}/openvswitch
+%{_pmdasexecdir}/openvswitch
 
 %files pmda-rabbitmq
 %{_pmdasdir}/rabbitmq
+%{_pmdasexecdir}/rabbitmq
+%{_confdir}/rabbitmq
 
 %files pmda-gpfs
 %{_pmdasdir}/gpfs
+%{_pmdasexecdir}/gpfs
 
 %files pmda-gpsd
 %{_pmdasdir}/gpsd
+%{_pmdasexecdir}/gpsd
 
 %files pmda-docker
 %{_pmdasdir}/docker
+%{_pmdasexecdir}/docker
 
 %files pmda-lio
 %{_pmdasdir}/lio
+%{_pmdasexecdir}/lio
 
 %files pmda-openmetrics
 %{_pmdasdir}/openmetrics
+%{_pmdasexecdir}/openmetrics
+%config(noreplace) %{_confdir}/openmetrics
 
 %files pmda-lustre
 %{_pmdasdir}/lustre
+%{_pmdasexecdir}/lustre
+%config(noreplace) %{_confdir}/lustre
 
 %files pmda-lustrecomm
 %{_pmdasdir}/lustrecomm
+%{_pmdasexecdir}/lustrecomm
 
 %files pmda-memcache
 %{_pmdasdir}/memcache
+%{_pmdasexecdir}/memcache
 
 %files pmda-mysql
 %{_pmdasdir}/mysql
+%{_pmdasexecdir}/mysql
 
 %files pmda-named
 %{_pmdasdir}/named
+%{_pmdasexecdir}/named
 
 %files pmda-netfilter
 %{_pmdasdir}/netfilter
+%{_pmdasexecdir}/netfilter
 
 %files pmda-news
 %{_pmdasdir}/news
+%{_pmdasexecdir}/news
 
 %files pmda-nginx
 %{_pmdasdir}/nginx
+%{_pmdasexecdir}/nginx
+%config(noreplace) %{_confdir}/nginx
 
 %files pmda-nfsclient
 %{_pmdasdir}/nfsclient
+%{_pmdasexecdir}/nfsclient
 
 %if !%{disable_nutcracker}
 %files pmda-nutcracker
 %{_pmdasdir}/nutcracker
+%{_pmdasexecdir}/nutcracker
+%config(noreplace) %{_confdir}/nutcracker
 %endif
 
 %files pmda-oracle
 %{_pmdasdir}/oracle
+%{_pmdasexecdir}/oracle
+%config(noreplace) %{_confdir}/oracle
 
 %files pmda-pdns
 %{_pmdasdir}/pdns
+%{_pmdasexecdir}/pdns
 
 %files pmda-postfix
 %{_pmdasdir}/postfix
+%{_pmdasexecdir}/postfix
 
 %files pmda-postgresql
 %{_pmdasdir}/postgresql
-%config(noreplace) %{_pmdasdir}/postgresql/pmdapostgresql.conf
+%{_pmdasexecdir}/postgresql
+%dir %{_confdir}/postgresql
+%config(noreplace) %{_confdir}/postgresql/pmdapostgresql.conf
 
 %files pmda-redis
 %{_pmdasdir}/redis
+%{_pmdasexecdir}/redis
+%{_confdir}/redis
 
 %files pmda-rsyslog
 %{_pmdasdir}/rsyslog
+%{_pmdasexecdir}/rsyslog
 
 %files pmda-samba
 %{_pmdasdir}/samba
+%{_pmdasexecdir}/samba
 
 %if !%{disable_snmp}
 %files pmda-snmp
 %{_pmdasdir}/snmp
+%{_pmdasexecdir}/snmp
+%{_confdir}/snmp
 %endif
 
 %files pmda-slurm
 %{_pmdasdir}/slurm
+%{_pmdasexecdir}/slurm
 
 %files pmda-vmware
 %{_pmdasdir}/vmware
+%{_pmdasexecdir}/vmware
 
 %files pmda-zimbra
 %{_pmdasdir}/zimbra
+%{_pmdasexecdir}/zimbra
 
 %files pmda-dm
 %{_pmdasdir}/dm
+%{_pmdasexecdir}/dm
 %{_ieconfdir}/dm
+%dir %{_confdir}/pmieconf/dm
+%config(noreplace) %{_confdir}/pmieconf/dm
 
 %if !%{disable_bcc}
 %files pmda-bcc
 %{_pmdasdir}/bcc
+%{_pmdasexecdir}/bcc
 %endif
 
 %if !%{disable_bpftrace}
 %files pmda-bpftrace
 %{_pmdasdir}/bpftrace
+%{_pmdasexecdir}/bpftrace
+%config(noreplace) %{_confdir}/bpftrace
 %endif
 
 %if !%{disable_python2} || !%{disable_python3}
 %files pmda-gluster
 %{_pmdasdir}/gluster
+%{_pmdasexecdir}/gluster
 
 %files pmda-zswap
 %{_pmdasdir}/zswap
+%{_pmdasexecdir}/zswap
 
 %files pmda-unbound
 %{_pmdasdir}/unbound
+%{_pmdasexecdir}/unbound
 
 %files pmda-mic
 %{_pmdasdir}/mic
+%{_pmdasexecdir}/mic
 
 %files pmda-haproxy
 %{_pmdasdir}/haproxy
+%{_pmdasexecdir}/haproxy
+%{_confdir}/haproxy
 
 %files pmda-libvirt
 %{_pmdasdir}/libvirt
+%{_pmdasexecdir}/libvirt
+%{_confdir}/libvirt
 
 %files export-pcp2elasticsearch
 %{_bindir}/pcp2elasticsearch
@@ -3285,83 +3276,112 @@ chown -R pcp:pcp %{_logsdir}/pmproxy 2>/dev/null
 
 %files pmda-lmsensors
 %{_pmdasdir}/lmsensors
+%{_pmdasexecdir}/lmsensors
 
 %files pmda-netcheck
 %{_pmdasdir}/netcheck
+%{_pmdasexecdir}/netcheck
+%{_confdir}/netcheck
 
 %endif
 
 %files export-zabbix-agent
 %{_libdir}/zabbix
-%{_sysconfdir}/zabbix/zabbix_agentd.d/zbxpcp.conf
+%dir %{_sysconfdir}/zabbix/zabbix_agentd.d
+%config(noreplace) %{_sysconfdir}/zabbix/zabbix_agentd.d/zbxpcp.conf
 
 %if !%{disable_mssql}
 %files pmda-mssql
 %{_pmdasdir}/mssql
+%{_pmdasexecdir}/mssql
+%config(noreplace) %{_confdir}/mssql
 %endif
 
 %if !%{disable_json}
 %files pmda-json
 %{_pmdasdir}/json
+%{_pmdasexecdir}/json
+%config(noreplace) %{_confdir}/json
 %endif
 
 %files pmda-apache
 %{_pmdasdir}/apache
+%{_pmdasexecdir}/apache
 
 %files pmda-bash
 %{_pmdasdir}/bash
+%{_pmdasexecdir}/bash
 
 %files pmda-cifs
 %{_pmdasdir}/cifs
+%{_pmdasexecdir}/cifs
 
 %files pmda-cisco
 %{_pmdasdir}/cisco
+%{_pmdasexecdir}/cisco
 
 %files pmda-gfs2
 %{_pmdasdir}/gfs2
+%{_pmdasexecdir}/gfs2
 
 %files pmda-logger
 %{_pmdasdir}/logger
+%{_pmdasexecdir}/logger
 
 %files pmda-mailq
 %{_pmdasdir}/mailq
+%{_pmdasexecdir}/mailq
 
 %files pmda-mounts
 %{_pmdasdir}/mounts
+%{_pmdasexecdir}/mounts
+%config(noreplace) %{_confdir}/mounts
 
 %files pmda-nvidia-gpu
 %{_pmdasdir}/nvidia
+%{_pmdasexecdir}/nvidia
 
 %files pmda-roomtemp
 %{_pmdasdir}/roomtemp
+%{_pmdasexecdir}/roomtemp
 
 %if !%{disable_rpm}
 %files pmda-rpm
 %{_pmdasdir}/rpm
+%{_pmdasexecdir}/rpm
 %endif
 
 %files pmda-sendmail
 %{_pmdasdir}/sendmail
+%{_pmdasexecdir}/sendmail
 
 %files pmda-shping
 %{_pmdasdir}/shping
+%{_pmdasexecdir}/shping
+%config(noreplace) %{_confdir}/shping
 
 %files pmda-smart
 %{_pmdasdir}/smart
+%{_pmdasexecdir}/smart
 
 %files pmda-summary
 %{_pmdasdir}/summary
+%{_pmdasexecdir}/summary
+%config(noreplace) %{_confdir}/summary
 
 %if !%{disable_systemd}
 %files pmda-systemd
 %{_pmdasdir}/systemd
+%{_pmdasexecdir}/systemd
 %endif
 
 %files pmda-trace
 %{_pmdasdir}/trace
+%{_pmdasexecdir}/trace
 
 %files pmda-weblog
 %{_pmdasdir}/weblog
+%{_pmdasexecdir}/weblog
 
 %files -n perl-PCP-PMDA -f perl-pcp-pmda.list
 
@@ -3388,14 +3408,18 @@ chown -R pcp:pcp %{_logsdir}/pmproxy 2>/dev/null
 %{_localstatedir}/lib/pcp/config/pmchart
 %{_localstatedir}/lib/pcp/config/pmafm/pcp-gui
 %{_datadir}/applications/pmchart.desktop
+%dir %{_pixmapdir}
 %{_bashcompdir}/pmdumptext
 %endif
 
 %files doc -f pcp-doc.list
+%dir %{_booksdir}
+%dir %{_datadir}/pcp/demos/tutorials
 
 %if !%{disable_selinux}
 %files selinux -f pcp-selinux.list
 %dir %{_selinuxdir}
+%dir %{_selinuxexecdir}
 %endif
 
 %if !%{disable_python2} || !%{disable_python3}
@@ -3408,21 +3432,41 @@ chown -R pcp:pcp %{_logsdir}/pmproxy 2>/dev/null
 %endif
 
 %changelog
+* Tue Sep 29 2020 Nathan Scott <nathans@redhat.com> - 5.2.1-2
+- Fix rpm spec permissions for pmie and sa directories (BZ 1882615)
+- Add versioned rpm spec dependencies for pcp-zeroconf (BZ 1882664)
+
+* Fri Sep 25 2020 Nathan Scott <nathans@redhat.com> - 5.2.1-1
+- Add online help text for PCP derived metrics (BZ 1670029)
+- Iteractive mode in pcp-atop for time intervals (BZ 1806824)
+- Add branch-to-specific-time feature in pcp-atop (BZ 1806927)
+- Fix hostname written to archive folio by pmchart (BZ 1824403)
+- Allow pmafm to function with compressed archives (BZ 1839666)
+- Fix folio handling of archive files outside /tmp (BZ 1839698)
+- pcp-pmda-lio deals with a missing kernel module (BZ 1843792)
+- Windows authentication mode for SQL Server PMDA (BZ 1847808)
+- Fix previous sample selection within pcp-atop (BZ 1847925)
+- Make pcp-atopsar honour the -e (end time) option (BZ 1851838)
+- Fix pcp-pmda-zswap crashing with namespace error (BZ 1851887)
+- Rebase to a more recent upstream version of PCP (BZ 1854035)
+- Fix pcp-atopsar handling of -H (header) option (BZ 1857121)
+- Update pcp-free(1) to show 'shared' memory usage (BZ 1878768)
+
 * Tue Jun 23 2020 Mark Goodwin <mgoodwin@redhat.com> - 5.1.1-3
-- fix for missing runtime deps on perl Net::SNMP (BZ 1790433)
-- resolve covscan and other issues from upstream QA (BZ 1849511)
+- Fix for missing runtime deps on perl Net::SNMP (BZ 1790433)
+- Resolve covscan and other issues from upstream QA (BZ 1849511)
 - Possible memory leak detected in pcp-atop (BZ 1846705)
 - Installation of pcp-pmda-samba causes SELinux issues (BZ 1790452)
-- fix Intermittent pminfo crashes (BZ 1848995)
+- Fix Intermittent pminfo crashes (BZ 1848995)
 - Silence openmetrics PMDA warnings, add status metrics (BZ 1846711)
-- set PACKAGE_BUILD in VERSION.pcp so pmcd.build metric is correct
+- Set PACKAGE_BUILD in VERSION.pcp so pmcd.build metric is correct
 
 * Thu Jun 11 2020 Mark Goodwin <mgoodwin@redhat.com> - 5.1.1-2
-- activate pmlogger_rewrite on upgrades (BZ 1541406)
-- fix Coverity issues in pmdastatsd and pmlogconf (BZ 1792971)
+- Activate pmlogger_rewrite on upgrades (BZ 1541406)
+- Fix Coverity issues in pmdastatsd and pmlogconf (BZ 1792971)
 - libpcp_web: ensure context is freed only after timer is fully closed
-- services: pmlogger and pmie services Want pmcd on boot
-- fix intermittent pmlogconf core dumps (BZ 1845241)
+- Services: pmlogger and pmie services Want pmcd on boot
+- Fix intermittent pmlogconf core dumps (BZ 1845241)
 - pcp-atop: resolve potential null task pointer dereference
 - pmproxy: improve diagnostics, particularly relating to http requests
 - pmproxy: cleanup, remove unused flags and dead code in http encoding
@@ -3435,16 +3479,16 @@ chown -R pcp:pcp %{_logsdir}/pmproxy 2>/dev/null
 - PCP_NSSDB_DIR should not be mentioned in /etc/pcp.conf (BZ 1826020)
 - pmie_daily.service runs as pcp but tries to do root things (BZ 1832262)
 - pcp-testsuite-5.1.0-2.el8 package is missing pcpqa.pp file (BZ 1835422)
-- gfs2 kernel trace points turning on by themselves (BZ 1825386)
+- GFS2 kernel trace points turning on by themselves (BZ 1825386)
 - pcp-atop various fixes (BZ 1818710)
 - SELinux prevents pmlogger from secure connection to remote pmcd (BZ 1826047)
 - pmda-lustre fails to start since lustre 2.12 (BZ 1788937)
-- added labels support for pmrep and various pcp2xxx tools
+- Added labels support for pmrep and various pcp2xxx tools
 - Update to latest pcp-5.1.1 PCP sources.
 
 * Fri May 08 2020 Mark Goodwin <mgoodwin@redhat.com> - 5.1.0-2
-- replace pmdads389log dep on 389-ds-base with a softdep
-- fix install deps for pcp-testsuite on pcp-pmda-mssql
+- Replace pmdads389log dep on 389-ds-base with a softdep
+- Fix install deps for pcp-testsuite on pcp-pmda-mssql
 - Improve pmlogger and pmie system log messages (BZ 1806428)
 - Missing dep of pcp-pmda-dbping on perl-DBI (BZ 1790421)
 - Missing dep of pcp-pmda-ds389log on 389-ds-base (BZ 1790422)
@@ -3461,7 +3505,7 @@ chown -R pcp:pcp %{_logsdir}/pmproxy 2>/dev/null
 - pmieconf randomly fails (BZ 1800545)
 - collectl2pcp does not handle large collectl archives well (BZ 1785101)
 - Missing dep of pcp-pmda-redis on perl-autodie and perl-Time-HiRes (BZ 1788519)
-- Can not install pcp-pmda-activemq, wrong location of RESTClient (BZ 1788878)
+- Cannot install pcp-pmda-activemq, wrong location of RESTClient (BZ 1788878)
 - Missing dep of pcp-pmda-bind2 on various perl packages (BZ 1790415)
 - pmlogger_daily_report causing PCP upstream testsuite to fail (BZ 1805146)
 - Missing selinux rules preventing pcp-pmda-named runing rndc(BZ 1825663)
@@ -3469,15 +3513,14 @@ chown -R pcp:pcp %{_logsdir}/pmproxy 2>/dev/null
 - pmnewlog generates inaccessible config file (BZ 1810110)
 - pmnewlog is causing PCP testsuite to hang (BZ 1810118)
 - pmdakvm: debugfs access is restricted (BZ 1824297)
-- error starting pmlogger; pid file not owned by root (BZ 1761962)
-- Update to latest PCP 5.1.0-1 upstream sources.
+- Error starting pmlogger; pid file not owned by root (BZ 1761962)
 - Update to latest PCP sources.
 
 * Wed Mar 11 2020 Mark Goodwin <mgoodwin@redhat.com> - 5.0.3-3
 - Resolve pcp-selinux issues causing services failures - (BZ 1810458)
 
 * Mon Mar 02 2020 Mark Goodwin <mgoodwin@redhat.com> - 5.0.3-2
-- fix typo in Requires: perl-Time-HiRes affecting pcp-pmda-bind2
+- Fix typo in Requires: perl-Time-HiRes affecting pcp-pmda-bind2
 
 * Thu Feb 27 2020 Mark Goodwin <mgoodwin@redhat.com> - 5.0.3-1
 - Avoid python ctypes bitfield struct on-stack (BZ 1800685)
@@ -3497,8 +3540,8 @@ chown -R pcp:pcp %{_logsdir}/pmproxy 2>/dev/null
 - Update to latest PCP sources.
 
 * Sun Oct 20 2019 Mark Goodwin <mgoodwin@redhat.com> - 5.0.0-2
-- various spec fixes for pmdastatsd
-- add patch1 to fix pmdastatsd build on rawhide
+- Various spec fixes for pmdastatsd
+- Add patch1 to fix pmdastatsd build on rawhide
 
 * Fri Oct 11 2019 Mark Goodwin <mgoodwin@redhat.com> - 5.0.0-1
 - Update to latest PCP sources.