summaryrefslogtreecommitdiff
path: root/src/pmlogrewrite
diff options
context:
space:
mode:
authorIgor Pashev <pashev.igor@gmail.com>2014-10-26 12:33:50 +0400
committerIgor Pashev <pashev.igor@gmail.com>2014-10-26 12:33:50 +0400
commit47e6e7c84f008a53061e661f31ae96629bc694ef (patch)
tree648a07f3b5b9d67ce19b0fd72e8caa1175c98f1a /src/pmlogrewrite
downloadpcp-47e6e7c84f008a53061e661f31ae96629bc694ef.tar.gz
Debian 3.9.10debian/3.9.10debian
Diffstat (limited to 'src/pmlogrewrite')
-rw-r--r--src/pmlogrewrite/GNUlocaldefs.coverage2
-rw-r--r--src/pmlogrewrite/GNUmakefile48
-rw-r--r--src/pmlogrewrite/gram.y986
-rw-r--r--src/pmlogrewrite/indom.c381
-rw-r--r--src/pmlogrewrite/lex.l223
-rw-r--r--src/pmlogrewrite/logger.h178
-rw-r--r--src/pmlogrewrite/logio.c175
-rw-r--r--src/pmlogrewrite/metric.c230
-rw-r--r--src/pmlogrewrite/pmlogrewrite.c1318
-rw-r--r--src/pmlogrewrite/result.c730
-rw-r--r--src/pmlogrewrite/util.c302
11 files changed, 4573 insertions, 0 deletions
diff --git a/src/pmlogrewrite/GNUlocaldefs.coverage b/src/pmlogrewrite/GNUlocaldefs.coverage
new file mode 100644
index 0000000..00f5eb4
--- /dev/null
+++ b/src/pmlogrewrite/GNUlocaldefs.coverage
@@ -0,0 +1,2 @@
+CFLAGS += -fprofile-arcs -ftest-coverage
+LDIRT += *.gcda *.gcno *.gcov
diff --git a/src/pmlogrewrite/GNUmakefile b/src/pmlogrewrite/GNUmakefile
new file mode 100644
index 0000000..9a0c79a
--- /dev/null
+++ b/src/pmlogrewrite/GNUmakefile
@@ -0,0 +1,48 @@
+#
+# Copyright (c) 2000,2004 Silicon Graphics, Inc. All Rights Reserved.
+# Copyright (c) 2011 Ken McDonell. All Rights Reserved.
+# Copyright (c) 2012 Red Hat, Inc. All Rights Reserved.
+#
+# This program is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by the
+# Free Software Foundation; either version 2 of the License, or (at your
+# option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+# for more details.
+#
+
+TOPDIR = ../..
+include $(TOPDIR)/src/include/builddefs
+-include ./GNUlocaldefs
+
+CFILES = pmlogrewrite.c util.c logio.c metric.c indom.c result.c
+HFILES = logger.h
+LFILES = lex.l
+YFILES = gram.y
+
+CMDTARGET = pmlogrewrite$(EXECSUFFIX)
+LLDLIBS = $(PCPLIB) $(LIB_FOR_MATH)
+LDIRT += $(YFILES:%.y=%.tab.?)
+
+default: $(CMDTARGET)
+
+include $(BUILDRULES)
+
+pmlogextract: $(OBJECTS)
+
+install: $(CMDTARGET)
+ $(INSTALL) -m 755 -d $(PCP_VAR_DIR)/config/pmlogrewrite
+ $(INSTALL) -m 755 $(CMDTARGET) $(PCP_BINADM_DIR)/$(CMDTARGET)
+
+.NOTPARALLEL:
+gram.tab.h gram.tab.c: gram.y
+
+lex.o gram.tab.o: gram.tab.h logger.h
+indom.o metric.o result.o util.o pmlogrewrite.o: logger.h
+
+default_pcp: default
+
+install_pcp: install
diff --git a/src/pmlogrewrite/gram.y b/src/pmlogrewrite/gram.y
new file mode 100644
index 0000000..bdbb795
--- /dev/null
+++ b/src/pmlogrewrite/gram.y
@@ -0,0 +1,986 @@
+/*
+ * Copyright (c) 2013 Red Hat.
+ * Copyright (c) 2011 Ken McDonell. All Rights Reserved.
+ * Copyright (c) 1997-2002 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+%{
+/*
+ * pmlogrewrite parser
+ */
+#include "pmapi.h"
+#include "impl.h"
+#include "logger.h"
+#include <errno.h>
+#include <assert.h>
+
+static indomspec_t *current_indomspec;
+static int current_star_indom;
+static int do_walk_indom;
+static int star_domain;
+
+static metricspec_t *current_metricspec;
+static int current_star_metric;
+static int star_cluster;
+static int do_walk_metric;
+static int output = OUTPUT_ALL;
+static int one_inst;
+static char *one_name;
+
+indomspec_t *
+walk_indom(int mode)
+{
+ static indomspec_t *ip;
+
+ if (do_walk_indom) {
+ if (mode == W_START)
+ ip = indom_root;
+ else
+ ip = ip->i_next;
+ while (ip != NULL && pmInDom_domain(ip->old_indom) != star_domain)
+ ip = ip->i_next;
+ }
+ else {
+ if (mode == W_START)
+ ip = current_indomspec;
+ else
+ ip = NULL;
+ }
+
+ return ip;
+}
+
+metricspec_t *
+walk_metric(int mode, int flag, char *which)
+{
+ static metricspec_t *mp;
+
+ if (do_walk_metric) {
+ if (mode == W_START)
+ mp = metric_root;
+ else
+ mp = mp->m_next;
+ while (mp != NULL) {
+ if (pmid_domain(mp->old_desc.pmid) == star_domain &&
+ (star_cluster == PM_ID_NULL || star_cluster == pmid_cluster(mp->old_desc.pmid)))
+ break;
+ mp = mp->m_next;
+ }
+ }
+ else {
+ if (mode == W_START)
+ mp = current_metricspec;
+ else
+ mp = NULL;
+ }
+
+ if (mp != NULL) {
+ if (mp->flags & flag) {
+ snprintf(mess, sizeof(mess), "Duplicate %s clause for metric %s", which, mp->old_name);
+ yyerror(mess);
+ }
+ if (flag != METRIC_DELETE) {
+ if (mp->flags & METRIC_DELETE) {
+ snprintf(mess, sizeof(mess), "Conflicting %s clause for deleted metric %s", which, mp->old_name);
+ yyerror(mess);
+ }
+ }
+ else {
+ if (mp->flags & (~METRIC_DELETE)) {
+ snprintf(mess, sizeof(mess), "Conflicting delete and other clauses for metric %s", mp->old_name);
+ yyerror(mess);
+ }
+ }
+ }
+
+ return mp;
+}
+
+%}
+
+%union {
+ char *str;
+ int ival;
+ double dval;
+ pmInDom indom;
+ pmID pmid;
+}
+
+%token TOK_LBRACE
+ TOK_RBRACE
+ TOK_PLUS
+ TOK_MINUS
+ TOK_COLON
+ TOK_COMMA
+ TOK_ASSIGN
+ TOK_GLOBAL
+ TOK_INDOM
+ TOK_DUPLICATE
+ TOK_METRIC
+ TOK_HOSTNAME
+ TOK_TZ
+ TOK_TIME
+ TOK_NAME
+ TOK_INST
+ TOK_INAME
+ TOK_DELETE
+ TOK_PMID
+ TOK_NULL_INT
+ TOK_TYPE
+ TOK_SEM
+ TOK_UNITS
+ TOK_OUTPUT
+ TOK_RESCALE
+
+%token<str> TOK_GNAME TOK_NUMBER TOK_STRING TOK_HNAME TOK_FLOAT
+%token<str> TOK_INDOM_STAR TOK_PMID_INT TOK_PMID_STAR
+%token<ival> TOK_TYPE_NAME TOK_SEM_NAME TOK_SPACE_NAME TOK_TIME_NAME
+%token<ival> TOK_COUNT_NAME TOK_OUTPUT_TYPE
+
+%type<str> hname
+%type<indom> indom_int null_or_indom
+%type<pmid> pmid_int pmid_or_name
+%type<ival> signnumber number rescaleopt duplicateopt
+%type<dval> float
+
+%%
+
+config : speclist
+ ;
+
+speclist : spec
+ | spec speclist
+ ;
+
+spec : globalspec
+ | indomspec
+ | metricspec
+ ;
+
+globalspec : TOK_GLOBAL TOK_LBRACE globaloptlist TOK_RBRACE
+ | TOK_GLOBAL TOK_LBRACE TOK_RBRACE
+ ;
+
+globaloptlist : globalopt
+ | globalopt globaloptlist
+ ;
+
+globalopt : TOK_HOSTNAME TOK_ASSIGN hname
+ {
+ if (global.flags & GLOBAL_CHANGE_HOSTNAME) {
+ snprintf(mess, sizeof(mess), "Duplicate global hostname clause");
+ yyerror(mess);
+ }
+ if (strcmp(inarch.label.ll_hostname, $3) == 0) {
+ /* no change ... */
+ if (wflag) {
+ snprintf(mess, sizeof(mess), "Global hostname (%s): No change", inarch.label.ll_hostname);
+ yywarn(mess);
+ }
+ }
+ else {
+ strncpy(global.hostname, $3, sizeof(global.hostname));
+ global.flags |= GLOBAL_CHANGE_HOSTNAME;
+ }
+ free($3);
+ }
+ | TOK_TZ TOK_ASSIGN TOK_STRING
+ {
+ if (global.flags & GLOBAL_CHANGE_TZ) {
+ snprintf(mess, sizeof(mess), "Duplicate global tz clause");
+ yyerror(mess);
+ }
+ if (strcmp(inarch.label.ll_tz, $3) == 0) {
+ /* no change ... */
+ if (wflag) {
+ snprintf(mess, sizeof(mess), "Global timezone (%s): No change", inarch.label.ll_tz);
+ yywarn(mess);
+ }
+ }
+ else {
+ strncpy(global.tz, $3, sizeof(global.tz));
+ global.flags |= GLOBAL_CHANGE_TZ;
+ }
+ free($3);
+ }
+ | TOK_TIME TOK_ASSIGN signtime
+ {
+ if (global.flags & GLOBAL_CHANGE_TIME) {
+ snprintf(mess, sizeof(mess), "Duplicate global time clause");
+ yyerror(mess);
+ }
+ if (global.time.tv_sec == 0 && global.time.tv_usec == 0) {
+ /* no change ... */
+ if (wflag) {
+ snprintf(mess, sizeof(mess), "Global time: No change");
+ yywarn(mess);
+ }
+ }
+ else
+ global.flags |= GLOBAL_CHANGE_TIME;
+ }
+ | TOK_HOSTNAME TOK_ASSIGN
+ {
+ snprintf(mess, sizeof(mess), "Expecting hostname in hostname clause");
+ yyerror(mess);
+ }
+ | TOK_HOSTNAME
+ {
+ snprintf(mess, sizeof(mess), "Expecting -> in hostname clause");
+ yyerror(mess);
+ }
+ | TOK_TZ TOK_ASSIGN
+ {
+ snprintf(mess, sizeof(mess), "Expecting timezone string in tz clause");
+ yyerror(mess);
+ }
+ | TOK_TZ
+ {
+ snprintf(mess, sizeof(mess), "Expecting -> in tz clause");
+ yyerror(mess);
+ }
+ | TOK_TIME TOK_ASSIGN
+ {
+ snprintf(mess, sizeof(mess), "Expecting delta of the form [+-][HH:[MM:]]SS[.d...] in time clause");
+ yyerror(mess);
+ }
+ | TOK_TIME
+ {
+ snprintf(mess, sizeof(mess), "Expecting -> in time clause");
+ yyerror(mess);
+ }
+ ;
+
+ /*
+ * ambiguity in lexical scanner ... handle here
+ * abc.def - is TOK_HNAME or TOK_GNAME
+ * 123 - is TOK_HNAME or TOK_NUMBER
+ * 123.456 - is TOK_HNAME or TOK_FLOAT
+ */
+hname : TOK_HNAME
+ | TOK_GNAME
+ | TOK_NUMBER
+ | TOK_FLOAT
+ ;
+
+signnumber : TOK_PLUS TOK_NUMBER
+ {
+ $$ = atoi($2);
+ free($2);
+ }
+ | TOK_MINUS TOK_NUMBER
+ {
+ $$ = -atoi($2);
+ free($2);
+ }
+ | TOK_NUMBER
+ {
+ $$ = atoi($1);
+ free($1);
+ }
+ ;
+
+number : TOK_NUMBER
+ {
+ $$ = atoi($1);
+ free($1);
+ }
+ ;
+
+float : TOK_FLOAT
+ {
+ $$ = atof($1);
+ free($1);
+ }
+ ;
+
+signtime : TOK_PLUS time
+ | TOK_MINUS time { global.time.tv_sec = -global.time.tv_sec; }
+ | time
+ ;
+
+time : number TOK_COLON number TOK_COLON float /* HH:MM:SS.d.. format */
+ {
+ if ($3 > 59) {
+ snprintf(mess, sizeof(mess), "Minutes (%d) in time clause more than 59", $3);
+ yywarn(mess);
+ }
+ if ($5 > 59) {
+ snprintf(mess, sizeof(mess), "Seconds (%.6f) in time clause more than 59", $5);
+ yywarn(mess);
+ }
+ global.time.tv_sec = $1 * 3600 + $3 * 60 + (int)$5;
+ global.time.tv_usec = (int)(1000000*(($5 - (int)$5))+0.5);
+ }
+ | number TOK_COLON number TOK_COLON number /* HH:MM:SS format */
+ {
+ if ($3 > 59) {
+ snprintf(mess, sizeof(mess), "Minutes (%d) in time clause more than 59", $3);
+ yywarn(mess);
+ }
+ if ($5 > 59) {
+ snprintf(mess, sizeof(mess), "Seconds (%d) in time clause more than 59", $5);
+ yywarn(mess);
+ }
+ global.time.tv_sec = $1 * 3600 + $3 * 60 + $5;
+ }
+ | number TOK_COLON float /* MM:SS.d.. format */
+ {
+ if ($1 > 59) {
+ snprintf(mess, sizeof(mess), "Minutes (%d) in time clause more than 59", $1);
+ yywarn(mess);
+ }
+ if ($3 > 59) {
+ snprintf(mess, sizeof(mess), "Seconds (%.6f) in time clause more than 59", $3);
+ yywarn(mess);
+ }
+ global.time.tv_sec = $1 * 60 + (int)$3;
+ global.time.tv_usec = (int)(1000000*(($3 - (int)$3))+0.5);
+ }
+ | number TOK_COLON number /* MM:SS format */
+ {
+ if ($1 > 59) {
+ snprintf(mess, sizeof(mess), "Minutes (%d) in time clause more than 59", $1);
+ yywarn(mess);
+ }
+ if ($3 > 59) {
+ snprintf(mess, sizeof(mess), "Seconds (%d) in time clause more than 59", $3);
+ yywarn(mess);
+ }
+ global.time.tv_sec = $1 * 60 + $3;
+ }
+ | float /* SS.d.. format */
+ {
+ if ($1 > 59) {
+ snprintf(mess, sizeof(mess), "Seconds (%.6f) in time clause more than 59", $1);
+ yywarn(mess);
+ }
+ global.time.tv_sec = (int)$1;
+ global.time.tv_usec = (int)(1000000*(($1 - (int)$1))+0.5);
+ }
+ | number /* SS format */
+ {
+ if ($1 > 59) {
+ snprintf(mess, sizeof(mess), "Seconds (%d) in time clause more than 59", $1);
+ yywarn(mess);
+ }
+ global.time.tv_sec = $1;
+ global.time.tv_usec = 0;
+ }
+ ;
+
+indomspec : TOK_INDOM indom_int
+ {
+ if (current_star_indom) {
+ __pmContext *ctxp;
+ __pmHashCtl *hcp;
+ __pmHashNode *node;
+
+ ctxp = __pmHandleToPtr(pmWhichContext());
+ assert(ctxp != NULL);
+ hcp = &ctxp->c_archctl->ac_log->l_hashindom;
+ star_domain = pmInDom_domain($2);
+ for (node = __pmHashWalk(hcp, PM_HASH_WALK_START);
+ node != NULL;
+ node = __pmHashWalk(hcp, PM_HASH_WALK_NEXT)) {
+ if (pmInDom_domain((pmInDom)(node->key)) == star_domain)
+ current_indomspec = start_indom((pmInDom)(node->key));
+ }
+ do_walk_indom = 1;
+ }
+ else {
+ current_indomspec = start_indom($2);
+ do_walk_indom = 0;
+ }
+ }
+ TOK_LBRACE optindomopt TOK_RBRACE
+ | TOK_INDOM
+ {
+ snprintf(mess, sizeof(mess), "Expecting <domain>.<serial> or <domain>.* in indom rule");
+ yyerror(mess);
+ }
+ ;
+
+indom_int : TOK_FLOAT
+ {
+ int domain;
+ int serial;
+ int sts;
+ sts = sscanf($1, "%d.%d", &domain, &serial);
+ if (sts < 2) {
+ snprintf(mess, sizeof(mess), "Missing serial field for indom");
+ yyerror(mess);
+ }
+ if (domain < 1 || domain >= DYNAMIC_PMID) {
+ snprintf(mess, sizeof(mess), "Illegal domain field (%d) for indom", domain);
+ yyerror(mess);
+ }
+ if (serial < 0 || serial >= 4194304) {
+ snprintf(mess, sizeof(mess), "Illegal serial field (%d) for indom", serial);
+ yyerror(mess);
+ }
+ current_star_indom = 0;
+ free($1);
+ $$ = pmInDom_build(domain, serial);
+ }
+ | TOK_INDOM_STAR
+ {
+ int domain;
+ sscanf($1, "%d.", &domain);
+ if (domain < 1 || domain >= DYNAMIC_PMID) {
+ snprintf(mess, sizeof(mess), "Illegal domain field (%d) for indom", domain);
+ yyerror(mess);
+ }
+ current_star_indom = 1;
+ free($1);
+ $$ = pmInDom_build(domain, 0);
+ }
+ ;
+
+optindomopt : indomoptlist
+ |
+ ;
+
+indomoptlist : indomopt
+ | indomopt indomoptlist
+ ;
+
+indomopt : TOK_INDOM TOK_ASSIGN duplicateopt indom_int
+ {
+ indomspec_t *ip;
+ for (ip = walk_indom(W_START); ip != NULL; ip = walk_indom(W_NEXT)) {
+ pmInDom indom;
+ if (indom_root->new_indom != indom_root->old_indom) {
+ snprintf(mess, sizeof(mess), "Duplicate indom clause for indom %s", pmInDomStr(indom_root->old_indom));
+ yyerror(mess);
+ }
+ if (current_star_indom)
+ indom = pmInDom_build(pmInDom_domain($4), pmInDom_serial(ip->old_indom));
+ else
+ indom = $4;
+ if (indom != ip->old_indom)
+ ip->new_indom = indom;
+ else {
+ /* no change ... */
+ if (wflag) {
+ snprintf(mess, sizeof(mess), "Instance domain %s: indom: No change", pmInDomStr(ip->old_indom));
+ yywarn(mess);
+ }
+ }
+ ip->indom_flags |= $3;
+ }
+ }
+ | TOK_INAME TOK_STRING TOK_ASSIGN TOK_STRING
+ {
+ indomspec_t *ip;
+ for (ip = walk_indom(W_START); ip != NULL; ip = walk_indom(W_NEXT)) {
+ if (change_inst_by_name(ip->old_indom, $2, $4) < 0)
+ yyerror(mess);
+ }
+ free($2);
+ /* Note: $4 referenced from new_iname[] */
+ }
+ | TOK_INAME TOK_STRING TOK_ASSIGN TOK_DELETE
+ {
+ indomspec_t *ip;
+ for (ip = walk_indom(W_START); ip != NULL; ip = walk_indom(W_NEXT)) {
+ if (change_inst_by_name(ip->old_indom, $2, NULL) < 0)
+ yyerror(mess);
+ }
+ free($2);
+ }
+ | TOK_INST number TOK_ASSIGN number
+ {
+ indomspec_t *ip;
+ for (ip = walk_indom(W_START); ip != NULL; ip = walk_indom(W_NEXT)) {
+ if (change_inst_by_inst(ip->old_indom, $2, $4) < 0)
+ yyerror(mess);
+ }
+ }
+ | TOK_INST number TOK_ASSIGN TOK_DELETE
+ {
+ indomspec_t *ip;
+ for (ip = walk_indom(W_START); ip != NULL; ip = walk_indom(W_NEXT)) {
+ if (change_inst_by_inst(ip->old_indom, $2, PM_IN_NULL) < 0)
+ yyerror(mess);
+ }
+ }
+ | TOK_INDOM TOK_ASSIGN
+ {
+ snprintf(mess, sizeof(mess), "Expecting <domain>.<serial> or <domain>.* in indom clause");
+ yyerror(mess);
+ }
+ | TOK_INDOM
+ {
+ snprintf(mess, sizeof(mess), "Expecting -> in indom clause");
+ yyerror(mess);
+ }
+ | TOK_INAME TOK_STRING TOK_ASSIGN
+ {
+ snprintf(mess, sizeof(mess), "Expecting new external instance name string or DELETE in iname clause");
+ yyerror(mess);
+ }
+ | TOK_INAME TOK_STRING
+ {
+ snprintf(mess, sizeof(mess), "Expecting -> in iname clause");
+ yyerror(mess);
+ }
+ | TOK_INAME
+ {
+ snprintf(mess, sizeof(mess), "Expecting old external instance name string in iname clause");
+ yyerror(mess);
+ }
+ | TOK_INST number TOK_ASSIGN
+ {
+ snprintf(mess, sizeof(mess), "Expecting new internal instance identifier or DELETE in inst clause");
+ yyerror(mess);
+ }
+ | TOK_INST number
+ {
+ snprintf(mess, sizeof(mess), "Expecting -> in inst clause");
+ yyerror(mess);
+ }
+ | TOK_INST
+ {
+ snprintf(mess, sizeof(mess), "Expecting old internal instance identifier in inst clause");
+ yyerror(mess);
+ }
+ ;
+
+duplicateopt : TOK_DUPLICATE { $$ = INDOM_DUPLICATE; }
+ | { $$ = 0; }
+ ;
+
+metricspec : TOK_METRIC pmid_or_name
+ {
+ if (current_star_metric) {
+ __pmContext *ctxp;
+ __pmHashCtl *hcp;
+ __pmHashNode *node;
+
+ ctxp = __pmHandleToPtr(pmWhichContext());
+ assert(ctxp != NULL);
+ hcp = &ctxp->c_archctl->ac_log->l_hashpmid;
+ star_domain = pmid_domain($2);
+ if (current_star_metric == 1)
+ star_cluster = pmid_cluster($2);
+ else
+ star_cluster = PM_ID_NULL;
+ for (node = __pmHashWalk(hcp, PM_HASH_WALK_START);
+ node != NULL;
+ node = __pmHashWalk(hcp, PM_HASH_WALK_NEXT)) {
+ if (pmid_domain((pmID)(node->key)) == star_domain &&
+ (star_cluster == PM_ID_NULL ||
+ star_cluster == pmid_cluster((pmID)(node->key))))
+ current_metricspec = start_metric((pmID)(node->key));
+ }
+ do_walk_metric = 1;
+ }
+ else {
+ if ($2 == PM_ID_NULL)
+ /* metric not in archive */
+ current_metricspec = NULL;
+ else
+ current_metricspec = start_metric($2);
+ do_walk_metric = 0;
+ }
+ }
+ TOK_LBRACE optmetricoptlist TOK_RBRACE
+ | TOK_METRIC
+ {
+ snprintf(mess, sizeof(mess), "Expecting metric name or <domain>.<cluster>.<item> or <domain>.<cluster>.* or <domain>.*.* in metric rule");
+ yyerror(mess);
+ }
+ ;
+
+pmid_or_name : pmid_int
+ | TOK_GNAME
+ {
+ int sts;
+ pmID pmid;
+ sts = pmLookupName(1, &$1, &pmid);
+ if (sts < 0) {
+ if (wflag) {
+ snprintf(mess, sizeof(mess), "Metric: %s: %s", $1, pmErrStr(sts));
+ yywarn(mess);
+ }
+ pmid = PM_ID_NULL;
+ }
+ current_star_metric = 0;
+ free($1);
+ $$ = pmid;
+ }
+ ;
+
+pmid_int : TOK_PMID_INT
+ {
+ int domain;
+ int cluster;
+ int item;
+ int sts;
+ sts = sscanf($1, "%d.%d.%d", &domain, &cluster, &item);
+ assert(sts == 3);
+ if (domain < 1 || domain >= DYNAMIC_PMID) {
+ snprintf(mess, sizeof(mess), "Illegal domain field (%d) for pmid", domain);
+ yyerror(mess);
+ }
+ if (cluster < 0 || cluster >= 4096) {
+ snprintf(mess, sizeof(mess), "Illegal cluster field (%d) for pmid", cluster);
+ yyerror(mess);
+ }
+ if (item < 0 || item >= 1024) {
+ snprintf(mess, sizeof(mess), "Illegal item field (%d) for pmid", item);
+ yyerror(mess);
+ }
+ current_star_metric = 0;
+ free($1);
+ $$ = pmid_build(domain, cluster, item);
+ }
+ | TOK_PMID_STAR
+ {
+ int domain;
+ int cluster;
+ int sts;
+ sts = sscanf($1, "%d.%d.", &domain, &cluster);
+ if (domain < 1 || domain >= DYNAMIC_PMID) {
+ snprintf(mess, sizeof(mess), "Illegal domain field (%d) for pmid", domain);
+ yyerror(mess);
+ }
+ if (sts == 2) {
+ if (cluster >= 4096) {
+ snprintf(mess, sizeof(mess), "Illegal cluster field (%d) for pmid", cluster);
+ yyerror(mess);
+ }
+ current_star_metric = 1;
+ }
+ else {
+ cluster = 0;
+ current_star_metric = 2;
+ }
+ free($1);
+ $$ = pmid_build(domain, cluster, 0);
+ }
+ ;
+
+optmetricoptlist : metricoptlist
+ | /* nothing */
+ ;
+
+metricoptlist : metricopt
+ | metricopt metricoptlist
+ ;
+
+metricopt : TOK_PMID TOK_ASSIGN pmid_int
+ {
+ metricspec_t *mp;
+ pmID pmid;
+ for (mp = walk_metric(W_START, METRIC_CHANGE_PMID, "pmid"); mp != NULL; mp = walk_metric(W_NEXT, METRIC_CHANGE_PMID, "pmid")) {
+ if (current_star_metric == 1)
+ pmid = pmid_build(pmid_domain($3), pmid_cluster($3), pmid_item(mp->old_desc.pmid));
+ else if (current_star_metric == 2)
+ pmid = pmid_build(pmid_domain($3), pmid_cluster(mp->old_desc.pmid), pmid_item(mp->old_desc.pmid));
+ else
+ pmid = $3;
+ if (pmid == mp->old_desc.pmid) {
+ /* no change ... */
+ if (wflag) {
+ snprintf(mess, sizeof(mess), "Metric: %s (%s): pmid: No change", mp->old_name, pmIDStr(mp->old_desc.pmid));
+ yywarn(mess);
+ }
+ }
+ else {
+ mp->new_desc.pmid = pmid;
+ mp->flags |= METRIC_CHANGE_PMID;
+ }
+ }
+ }
+ | TOK_NAME TOK_ASSIGN TOK_GNAME
+ {
+ metricspec_t *mp;
+ for (mp = walk_metric(W_START, METRIC_CHANGE_NAME, "name"); mp != NULL; mp = walk_metric(W_NEXT, METRIC_CHANGE_NAME, "name")) {
+ if (strcmp($3, mp->old_name) == 0) {
+ /* no change ... */
+ if (wflag) {
+ snprintf(mess, sizeof(mess), "Metric: %s (%s): name: No change", mp->old_name, pmIDStr(mp->old_desc.pmid));
+ yywarn(mess);
+ }
+ }
+ else {
+ int sts;
+ pmID pmid;
+ sts = pmLookupName(1, &$3, &pmid);
+ if (sts >= 0) {
+ snprintf(mess, sizeof(mess), "Metric name %s already assigned for PMID %s", $3, pmIDStr(pmid));
+ yyerror(mess);
+ }
+ mp->new_name = $3;
+ mp->flags |= METRIC_CHANGE_NAME;
+ }
+ }
+ }
+ | TOK_TYPE TOK_ASSIGN TOK_TYPE_NAME
+ {
+ metricspec_t *mp;
+ for (mp = walk_metric(W_START, METRIC_CHANGE_TYPE, "type"); mp != NULL; mp = walk_metric(W_NEXT, METRIC_CHANGE_TYPE, "type")) {
+ if ($3 == mp->old_desc.type) {
+ /* no change ... */
+ if (wflag) {
+ snprintf(mess, sizeof(mess), "Metric: %s (%s): type: PM_TYPE_%s: No change", mp->old_name, pmIDStr(mp->old_desc.pmid), pmTypeStr(mp->old_desc.type));
+ yywarn(mess);
+ }
+ }
+ else {
+ if (mp->old_desc.type == PM_TYPE_32 ||
+ mp->old_desc.type == PM_TYPE_U32 ||
+ mp->old_desc.type == PM_TYPE_64 ||
+ mp->old_desc.type == PM_TYPE_U64 ||
+ mp->old_desc.type == PM_TYPE_FLOAT ||
+ mp->old_desc.type == PM_TYPE_DOUBLE) {
+ mp->new_desc.type = $3;
+ mp->flags |= METRIC_CHANGE_TYPE;
+ }
+ else {
+ snprintf(mess, sizeof(mess), "Old type (PM_TYPE_%s) must be numeric", pmTypeStr(mp->old_desc.type));
+ yyerror(mess);
+ }
+ }
+ }
+ }
+ | TOK_INDOM TOK_ASSIGN null_or_indom pick
+ {
+ metricspec_t *mp;
+ pmInDom indom;
+ for (mp = walk_metric(W_START, METRIC_CHANGE_INDOM, "indom"); mp != NULL; mp = walk_metric(W_NEXT, METRIC_CHANGE_INDOM, "indom")) {
+ if (current_star_indom)
+ indom = pmInDom_build(pmInDom_domain($3), pmInDom_serial(mp->old_desc.indom));
+ else
+ indom = $3;
+ if (indom == mp->old_desc.indom) {
+ /* no change ... */
+ if (wflag) {
+ snprintf(mess, sizeof(mess), "Metric: %s (%s): indom: %s: No change", mp->old_name, pmIDStr(mp->old_desc.pmid), pmInDomStr(mp->old_desc.indom));
+ yywarn(mess);
+ }
+ }
+ else {
+ if ((output == OUTPUT_MIN ||
+ output == OUTPUT_MAX ||
+ output == OUTPUT_SUM ||
+ output == OUTPUT_AVG) &&
+ mp->old_desc.type != PM_TYPE_32 &&
+ mp->old_desc.type != PM_TYPE_U32 &&
+ mp->old_desc.type != PM_TYPE_64 &&
+ mp->old_desc.type != PM_TYPE_U64 &&
+ mp->old_desc.type != PM_TYPE_FLOAT &&
+ mp->old_desc.type != PM_TYPE_DOUBLE) {
+ snprintf(mess, sizeof(mess), "OUTPUT option MIN, MAX, AVG or SUM requires type to be numeric, not PM_TYPE_%s", pmTypeStr(mp->old_desc.type));
+ yyerror(mess);
+ }
+ mp->new_desc.indom = indom;
+ mp->flags |= METRIC_CHANGE_INDOM;
+ mp->output = output;
+ if (output == OUTPUT_ONE) {
+ mp->one_name = one_name;
+ mp->one_inst = one_inst;
+ if (mp->old_desc.indom == PM_INDOM_NULL)
+ /*
+ * singular input, pick first (only)
+ * value, not one_inst matching ...
+ * one_inst used for output instance
+ * id
+ */
+ mp->output = OUTPUT_FIRST;
+ }
+ if (output == OUTPUT_ALL) {
+ /*
+ * No OUTPUT clause, set up the defaults
+ * based on indom types:
+ * non-NULL -> NULL
+ * OUTPUT_FIRST, inst PM_IN_NULL
+ * NULL -> non-NULL
+ * OUTPUT_FIRST, inst 0
+ * non-NULL -> non-NULL
+ * all instances selected
+ * (nothing to do for defaults)
+ * NULL -> NULL
+ * caught above in no change case
+ */
+ if (mp->old_desc.indom != PM_INDOM_NULL &&
+ mp->new_desc.indom == PM_INDOM_NULL) {
+ mp->output = OUTPUT_FIRST;
+ mp->one_inst = PM_IN_NULL;
+ }
+ else if (mp->old_desc.indom == PM_INDOM_NULL &&
+ mp->new_desc.indom != PM_INDOM_NULL) {
+ mp->output = OUTPUT_FIRST;
+ mp->one_inst = 0;
+ }
+ }
+ }
+ }
+ output = OUTPUT_ALL; /* for next time */
+ }
+ | TOK_SEM TOK_ASSIGN TOK_SEM_NAME
+ {
+ metricspec_t *mp;
+ for (mp = walk_metric(W_START, METRIC_CHANGE_SEM, "sem"); mp != NULL; mp = walk_metric(W_NEXT, METRIC_CHANGE_SEM, "sem")) {
+ if ($3 == mp->old_desc.sem) {
+ /* no change ... */
+ if (wflag) {
+ snprintf(mess, sizeof(mess), "Metric: %s (%s): sem: %s: No change", mp->old_name, pmIDStr(mp->old_desc.pmid), SemStr(mp->old_desc.sem));
+ yywarn(mess);
+ }
+ }
+ else {
+ mp->new_desc.sem = $3;
+ mp->flags |= METRIC_CHANGE_SEM;
+ }
+ }
+ }
+ | TOK_UNITS TOK_ASSIGN signnumber TOK_COMMA signnumber TOK_COMMA signnumber TOK_COMMA TOK_SPACE_NAME TOK_COMMA TOK_TIME_NAME TOK_COMMA TOK_COUNT_NAME rescaleopt
+ {
+ metricspec_t *mp;
+ for (mp = walk_metric(W_START, METRIC_CHANGE_UNITS, "units"); mp != NULL; mp = walk_metric(W_NEXT, METRIC_CHANGE_UNITS, "units")) {
+ if ($3 == mp->old_desc.units.dimSpace &&
+ $5 == mp->old_desc.units.dimTime &&
+ $7 == mp->old_desc.units.dimCount &&
+ $9 == mp->old_desc.units.scaleSpace &&
+ $11 == mp->old_desc.units.scaleTime &&
+ $13 == mp->old_desc.units.scaleCount) {
+ /* no change ... */
+ if (wflag) {
+ snprintf(mess, sizeof(mess), "Metric: %s (%s): units: %s: No change", mp->old_name, pmIDStr(mp->old_desc.pmid), pmUnitsStr(&mp->old_desc.units));
+ yywarn(mess);
+ }
+ }
+ else {
+ mp->new_desc.units.dimSpace = $3;
+ mp->new_desc.units.dimTime = $5;
+ mp->new_desc.units.dimCount = $7;
+ mp->new_desc.units.scaleSpace = $9;
+ mp->new_desc.units.scaleTime = $11;
+ mp->new_desc.units.scaleCount = $13;
+ mp->flags |= METRIC_CHANGE_UNITS;
+ if ($14 == 1) {
+ if ($3 == mp->old_desc.units.dimSpace &&
+ $5 == mp->old_desc.units.dimTime &&
+ $7 == mp->old_desc.units.dimCount)
+ /* OK, no dim changes */
+ mp->flags |= METRIC_RESCALE;
+ else {
+ if (wflag) {
+ snprintf(mess, sizeof(mess), "Metric: %s (%s): Dimension changed, cannot rescale", mp->old_name, pmIDStr(mp->old_desc.pmid));
+ yywarn(mess);
+ }
+ }
+ }
+ else if (sflag) {
+ if ($3 == mp->old_desc.units.dimSpace &&
+ $5 == mp->old_desc.units.dimTime &&
+ $7 == mp->old_desc.units.dimCount)
+ mp->flags |= METRIC_RESCALE;
+ }
+ }
+ }
+ }
+ | TOK_DELETE
+ {
+ metricspec_t *mp;
+ for (mp = walk_metric(W_START, METRIC_DELETE, "delete"); mp != NULL; mp = walk_metric(W_NEXT, METRIC_DELETE, "delete")) {
+ mp->flags |= METRIC_DELETE;
+ }
+ }
+ | TOK_PMID TOK_ASSIGN
+ {
+ snprintf(mess, sizeof(mess), "Expecting <domain>.<cluster>.<item> or <domain>.<cluster>.* or <domain>.*.* in pmid clause");
+ yyerror(mess);
+ }
+ | TOK_NAME TOK_ASSIGN
+ {
+ snprintf(mess, sizeof(mess), "Expecting metric name in iname clause");
+ yyerror(mess);
+ }
+ | TOK_TYPE TOK_ASSIGN
+ {
+ snprintf(mess, sizeof(mess), "Expecting XXX (from PM_TYPE_XXX) in type clause");
+ yyerror(mess);
+ }
+ | TOK_INDOM TOK_ASSIGN
+ {
+ snprintf(mess, sizeof(mess), "Expecting <domain>.<serial> or NULL in indom clause");
+ yyerror(mess);
+ }
+ | TOK_SEM TOK_ASSIGN
+ {
+ snprintf(mess, sizeof(mess), "Expecting XXX (from PM_SEM_XXX) in sem clause");
+ yyerror(mess);
+ }
+ | TOK_UNITS TOK_ASSIGN
+ {
+ snprintf(mess, sizeof(mess), "Expecting 3 numeric values for dim* fields of units");
+ yyerror(mess);
+ }
+ | TOK_UNITS TOK_ASSIGN signnumber TOK_COMMA signnumber TOK_COMMA signnumber TOK_COMMA
+ {
+ snprintf(mess, sizeof(mess), "Expecting 0 or XXX (from PM_SPACE_XXX) for scaleSpace field of units");
+ yyerror(mess);
+ }
+ | TOK_UNITS TOK_ASSIGN signnumber TOK_COMMA signnumber TOK_COMMA signnumber TOK_COMMA TOK_SPACE_NAME TOK_COMMA
+ {
+ snprintf(mess, sizeof(mess), "Expecting 0 or XXX (from PM_TIME_XXX) for scaleTime field of units");
+ yyerror(mess);
+ }
+ | TOK_UNITS TOK_ASSIGN signnumber TOK_COMMA signnumber TOK_COMMA signnumber TOK_COMMA TOK_SPACE_NAME TOK_COMMA TOK_TIME_NAME TOK_COMMA
+ {
+ snprintf(mess, sizeof(mess), "Expecting 0 or ONE for scaleCount field of units");
+ yyerror(mess);
+ }
+ ;
+
+null_or_indom : indom_int
+ | TOK_NULL_INT
+ {
+ $$ = PM_INDOM_NULL;
+ }
+ ;
+
+pick : TOK_OUTPUT TOK_INST number
+ {
+ output = OUTPUT_ONE;
+ one_inst = $3;
+ one_name = NULL;
+ }
+ | TOK_OUTPUT TOK_INAME TOK_STRING
+ {
+ output = OUTPUT_ONE;
+ one_inst = PM_IN_NULL;
+ one_name = $3;
+ }
+ | TOK_OUTPUT TOK_OUTPUT_TYPE
+ {
+ output = $2;
+ }
+ | TOK_OUTPUT
+ {
+ snprintf(mess, sizeof(mess), "Expecting FIRST or LAST or INST or INAME or MIN or MAX or AVG for OUTPUT instance option");
+ yyerror(mess);
+ }
+ | /* nothing */
+ ;
+
+rescaleopt : TOK_RESCALE { $$ = 1; }
+ | /* nothing */
+ { $$ = 0; }
+ ;
+
+%%
diff --git a/src/pmlogrewrite/indom.c b/src/pmlogrewrite/indom.c
new file mode 100644
index 0000000..25592cb
--- /dev/null
+++ b/src/pmlogrewrite/indom.c
@@ -0,0 +1,381 @@
+/*
+ * Indom metadata support for pmlogrewrite
+ *
+ * Copyright (c) 2011 Ken McDonell. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#include "pmapi.h"
+#include "impl.h"
+#include "logger.h"
+#include <assert.h>
+
+/*
+ * Find or create a new indomspec_t
+ */
+indomspec_t *
+start_indom(pmInDom indom)
+{
+ indomspec_t *ip;
+ int i;
+
+ for (ip = indom_root; ip != NULL; ip = ip->i_next) {
+ if (indom == ip->old_indom)
+ break;
+ }
+ if (ip == NULL) {
+ int numinst;
+ int *instlist;
+ char **namelist;
+
+ numinst = pmGetInDomArchive(indom, &instlist, &namelist);
+ if (numinst < 0) {
+ if (wflag) {
+ snprintf(mess, sizeof(mess), "Instance domain %s: %s", pmInDomStr(indom), pmErrStr(numinst));
+ yywarn(mess);
+ }
+ return NULL;
+ }
+
+ ip = (indomspec_t *)malloc(sizeof(indomspec_t));
+ if (ip == NULL) {
+ fprintf(stderr, "indomspec malloc(%d) failed: %s\n", (int)sizeof(indomspec_t), strerror(errno));
+ abandon();
+ /*NOTREACHED*/
+ }
+ ip->i_next = indom_root;
+ indom_root = ip;
+ ip->indom_flags = 0;
+ ip->inst_flags = (int *)malloc(numinst*sizeof(int));
+ if (ip->inst_flags == NULL) {
+ fprintf(stderr, "indomspec flags malloc(%d) failed: %s\n", (int)(numinst*sizeof(int)), strerror(errno));
+ abandon();
+ /*NOTREACHED*/
+ }
+ for (i = 0; i < numinst; i++)
+ ip->inst_flags[i] = 0;
+ ip->old_indom = indom;
+ ip->new_indom = indom;
+ ip->numinst = numinst;
+ ip->old_inst = instlist;
+ ip->new_inst = (int *)malloc(numinst*sizeof(int));
+ if (ip->new_inst == NULL) {
+ fprintf(stderr, "new_inst malloc(%d) failed: %s\n", (int)(numinst*sizeof(int)), strerror(errno));
+ abandon();
+ /*NOTREACHED*/
+ }
+ ip->old_iname = namelist;
+ ip->new_iname = (char **)malloc(numinst*sizeof(char *));
+ if (ip->new_iname == NULL) {
+ fprintf(stderr, "new_iname malloc(%d) failed: %s\n", (int)(numinst*sizeof(char *)), strerror(errno));
+ abandon();
+ /*NOTREACHED*/
+ }
+ }
+
+ return ip;
+}
+
+int
+change_inst_by_name(pmInDom indom, char *old, char *new)
+{
+ int i;
+ indomspec_t *ip;
+
+ for (ip = indom_root; ip != NULL; ip = ip->i_next) {
+ if (indom == ip->old_indom)
+ break;
+ }
+ assert(ip != NULL);
+
+ for (i = 0; i < ip->numinst; i++) {
+ if (inst_name_eq(ip->old_iname[i], old) > 0) {
+ if ((new == NULL && ip->inst_flags[i]) ||
+ (ip->inst_flags[i] & (INST_CHANGE_INAME|INST_DELETE))) {
+ snprintf(mess, sizeof(mess), "Duplicate or conflicting clauses for instance [%d] \"%s\" of indom %s",
+ ip->old_inst[i], ip->old_iname[i], pmInDomStr(indom));
+ return -1;
+ }
+ break;
+ }
+ }
+ if (i == ip->numinst) {
+ if (wflag) {
+ snprintf(mess, sizeof(mess), "Unknown instance \"%s\" in iname clause for indom %s", old, pmInDomStr(indom));
+ yywarn(mess);
+ }
+ return 0;
+ }
+
+ if (new == NULL) {
+ ip->inst_flags[i] |= INST_DELETE;
+ ip->new_iname[i] = NULL;
+ return 0;
+ }
+
+ if (strcmp(ip->old_iname[i], new) == 0) {
+ /* no change ... */
+ if (wflag) {
+ snprintf(mess, sizeof(mess), "Instance domain %s: Instance: \"%s\": No change", pmInDomStr(indom), ip->old_iname[i]);
+ yywarn(mess);
+ }
+ }
+ else {
+ ip->inst_flags[i] |= INST_CHANGE_INAME;
+ ip->new_iname[i] = new;
+ }
+
+ return 0;
+}
+
+int
+change_inst_by_inst(pmInDom indom, int old, int new)
+{
+ int i;
+ indomspec_t *ip;
+
+ for (ip = indom_root; ip != NULL; ip = ip->i_next) {
+ if (indom == ip->old_indom)
+ break;
+ }
+ assert(ip != NULL);
+
+ for (i = 0; i < ip->numinst; i++) {
+ if (ip->old_inst[i] == old) {
+ if ((new == PM_IN_NULL && ip->inst_flags[i]) ||
+ (ip->inst_flags[i] & (INST_CHANGE_INST|INST_DELETE))) {
+ snprintf(mess, sizeof(mess), "Duplicate or conflicting clauses for instance [%d] \"%s\" of indom %s",
+ ip->old_inst[i], ip->old_iname[i], pmInDomStr(indom));
+ return -1;
+ }
+ break;
+ }
+ }
+ if (i == ip->numinst) {
+ if (wflag) {
+ snprintf(mess, sizeof(mess), "Unknown instance %d in inst clause for indom %s", old, pmInDomStr(indom));
+ yywarn(mess);
+ }
+ return 0;
+ }
+
+ if (new == PM_IN_NULL) {
+ ip->inst_flags[i] |= INST_DELETE;
+ ip->new_inst[i] = PM_IN_NULL;
+ return 0;
+ }
+
+ if (ip->old_inst[i] == new) {
+ /* no change ... */
+ if (wflag) {
+ snprintf(mess, sizeof(mess), "Instance domain %s: Instance: %d: No change", pmInDomStr(indom), ip->old_inst[i]);
+ yywarn(mess);
+ }
+ }
+ else {
+ ip->new_inst[i] = new;
+ ip->inst_flags[i] |= INST_CHANGE_INST;
+ }
+
+ return 0;
+}
+
+typedef struct {
+ __pmLogHdr hdr;
+ __pmTimeval stamp;
+ pmInDom indom;
+ int numinst;
+ char other[1];
+} indom_t;
+
+/*
+ * reverse the logic of __pmLogPutInDom()
+ */
+static void
+_pmUnpackInDom(__pmPDU *pdubuf, pmInDom *indom, __pmTimeval *tp, int *numinst, int **instlist, char ***inamelist)
+{
+ indom_t *idp;
+ int i;
+ int *ip;
+ char *strbuf;
+
+ idp = (indom_t *)pdubuf;
+
+ tp->tv_sec = ntohl(idp->stamp.tv_sec);
+ tp->tv_usec = ntohl(idp->stamp.tv_usec);
+ *indom = ntoh_pmInDom(idp->indom);
+ *numinst = ntohl(idp->numinst);
+ *instlist = (int *)malloc(*numinst * sizeof(int));
+ if (*instlist == NULL) {
+ fprintf(stderr, "_pmUnpackInDom instlist malloc(%d) failed: %s\n", (int)(*numinst * sizeof(int)), strerror(errno));
+ abandon();
+ /*NOTREACHED*/
+ }
+ ip = (int *)idp->other;
+ for (i = 0; i < *numinst; i++)
+ (*instlist)[i] = ntohl(*ip++);
+ *inamelist = (char **)malloc(*numinst * sizeof(char *));
+ if (*inamelist == NULL) {
+ fprintf(stderr, "_pmUnpackInDom inamelist malloc(%d) failed: %s\n", (int)(*numinst * sizeof(char *)), strerror(errno));
+ abandon();
+ /*NOTREACHED*/
+ }
+ /*
+ * ip[i] is stridx[i], which is offset into strbuf[]
+ */
+ strbuf = (char *)&ip[*numinst];
+ for (i = 0; i < *numinst; i++) {
+ (*inamelist)[i] = &strbuf[ntohl(ip[i])];
+ }
+}
+
+/*
+ * Note:
+ * We unpack the indom metadata record _again_ (was already done when
+ * the input archive was opened), but the data structure behind
+ * __pmLogCtl has differences for 32-bit and 64-bit pointers and
+ * modifying it as part of the rewrite could make badness break
+ * out later. It is safer to do it again, populate local copies
+ * of instlist[] and inamelist[], dink with 'em and then toss them
+ * away.
+ */
+void
+do_indom(void)
+{
+ long out_offset;
+ pmInDom indom;
+ __pmTimeval stamp;
+ int numinst;
+ int *instlist;
+ char **inamelist;
+ indomspec_t *ip;
+ int sts;
+ int i;
+ int j;
+ int need_alloc = 0;
+
+ out_offset = ftell(outarch.logctl.l_mdfp);
+ _pmUnpackInDom(inarch.metarec, &indom, &stamp, &numinst, &instlist, &inamelist);
+
+ /*
+ * global time stamp adjustment (if any has already been done in the
+ * PDU buffer, so this is reflected in the unpacked value of stamp.
+ */
+ for (ip = indom_root; ip != NULL; ip = ip->i_next) {
+ if (ip->old_indom != indom)
+ continue;
+ if (ip->indom_flags & INDOM_DUPLICATE) {
+ /*
+ * save the old indom without changes, then operate on the
+ * duplicate
+ */
+ if ((sts = __pmLogPutInDom(&outarch.logctl, indom, &stamp, numinst, instlist, inamelist)) < 0) {
+ fprintf(stderr, "%s: Error: __pmLogPutInDom: %s: %s\n",
+ pmProgname, pmInDomStr(indom), pmErrStr(sts));
+ abandon();
+ /*NOTREACHED*/
+ }
+#if PCP_DEBUG
+ if (pmDebug & DBG_TRACE_APPL0) {
+ fprintf(stderr, "Metadata: write pre-duplicate InDom %s @ offset=%ld\n", pmInDomStr(indom), out_offset);
+ }
+#endif
+ out_offset = ftell(outarch.logctl.l_mdfp);
+ }
+ if (ip->new_indom != ip->old_indom)
+ indom = ip->new_indom;
+ for (i = 0; i < ip->numinst; i++) {
+ for (j = 0; j < numinst; j++) {
+ if (ip->old_inst[i] == instlist[j])
+ break;
+ }
+ if (j == numinst)
+ continue;
+ if (ip->inst_flags[i] & INST_DELETE) {
+#if PCP_DEBUG
+ if (pmDebug & DBG_TRACE_APPL1)
+ fprintf(stderr, "Delete: instance %s (%d) for indom %s\n", ip->old_iname[i], ip->old_inst[i], pmInDomStr(ip->old_indom));
+#endif
+ j++;
+ while (j < numinst) {
+ instlist[j-1] = instlist[j];
+ inamelist[j-1] = inamelist[j];
+ j++;
+ }
+ need_alloc = 1;
+ numinst--;
+ }
+ else {
+ if (ip->inst_flags[i] & INST_CHANGE_INST)
+ instlist[j] = ip->new_inst[i];
+ if (ip->inst_flags[i] & INST_CHANGE_INAME) {
+ inamelist[j] = ip->new_iname[i];
+ need_alloc = 1;
+ }
+#if PCP_DEBUG
+ if ((ip->inst_flags[i] & (INST_CHANGE_INST | INST_CHANGE_INAME)) && (pmDebug & DBG_TRACE_APPL1)) {
+ if ((ip->inst_flags[i] & (INST_CHANGE_INST | INST_CHANGE_INAME)) == (INST_CHANGE_INST | INST_CHANGE_INAME))
+ fprintf(stderr, "Rewrite: instance %s (%d) -> %s (%d) for indom %s\n", ip->old_iname[i], ip->old_inst[i], ip->new_iname[i], ip->new_inst[i], pmInDomStr(ip->old_indom));
+ else if ((ip->inst_flags[i] & (INST_CHANGE_INST | INST_CHANGE_INAME)) == INST_CHANGE_INST)
+ fprintf(stderr, "Rewrite: instance %s (%d) -> %s (%d) for indom %s\n", ip->old_iname[i], ip->old_inst[i], ip->old_iname[i], ip->new_inst[i], pmInDomStr(ip->old_indom));
+ else
+ fprintf(stderr, "Rewrite: instance %s (%d) -> %s (%d) for indom %s\n", ip->old_iname[i], ip->old_inst[i], ip->new_iname[i], ip->old_inst[i], pmInDomStr(ip->old_indom));
+ }
+#endif
+ }
+ }
+ }
+
+ if (need_alloc) {
+ /*
+ * __pmLogPutInDom assumes the elements of inamelist[] point into
+ * of a contiguous allocation starting at inamelist[0] ... if we've
+ * changed an instance name or moved instance names about, then we
+ * need to reallocate the strings for inamelist[]
+ */
+ int need = 0;
+ char *new;
+ char *p;
+
+ for (j = 0; j < numinst; j++)
+ need += strlen(inamelist[j]) + 1;
+ new = (char *)malloc(need);
+ if (new == NULL) {
+ fprintf(stderr, "inamelist[] malloc(%d) failed: %s\n", need, strerror(errno));
+ abandon();
+ /*NOTREACHED*/
+ }
+ p = new;
+ for (j = 0; j < numinst; j++) {
+ strcpy(p, inamelist[j]);
+ inamelist[j] = p;
+ p += strlen(p) + 1;
+ }
+ }
+
+ if ((sts = __pmLogPutInDom(&outarch.logctl, indom, &stamp, numinst, instlist, inamelist)) < 0) {
+ fprintf(stderr, "%s: Error: __pmLogPutInDom: %s: %s\n",
+ pmProgname, pmInDomStr(indom), pmErrStr(sts));
+ abandon();
+ /*NOTREACHED*/
+ }
+#if PCP_DEBUG
+ if (pmDebug & DBG_TRACE_APPL0) {
+ fprintf(stderr, "Metadata: write InDom %s @ offset=%ld\n", pmInDomStr(indom), out_offset);
+ }
+#endif
+
+ free(instlist);
+ if (need_alloc)
+ free(inamelist[0]);
+ free(inamelist);
+}
diff --git a/src/pmlogrewrite/lex.l b/src/pmlogrewrite/lex.l
new file mode 100644
index 0000000..4f7e6d7
--- /dev/null
+++ b/src/pmlogrewrite/lex.l
@@ -0,0 +1,223 @@
+/*
+ * Copyright (c) 1997-2000 Silicon Graphics, Inc. All Rights Reserved.
+ * Copyright (c) 2011 Ken McDonell. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+%{
+/*
+ * pmlogrewrite configfile lexical scanner
+ */
+#include "pmapi.h"
+#include "impl.h"
+#include "logger.h"
+#include <errno.h>
+
+char mess[256];
+
+#define LEX_NONE 0
+#define LEX_GLOBAL 1
+#define LEX_INDOM 2
+#define LEX_METRIC 3
+
+int mystate = LEX_NONE;
+
+static int comma_count;
+
+#include "gram.tab.h"
+
+static char *
+dupstr(char *s, int strip_quotes)
+{
+ char *p;
+ if (strip_quotes)
+ p = strdup(&s[1]);
+ else
+ p = strdup(s);
+ if (p == NULL) {
+ fprintf(stderr, "Failed strdup(\"%s\") in lexer: %s\n", s, strerror(errno));
+ abandon();
+ /*NOTREACHED*/
+ }
+ if (strip_quotes) {
+ char *pend = p;
+ while (*pend != '\0')
+ pend++;
+ *--pend = '\0';
+ }
+ return p;
+}
+
+%}
+
+%option noinput
+%option nounput
+
+%{
+#ifdef FLEX_SCANNER
+#ifndef YY_NO_UNPUT
+#define YY_NO_UNPUT
+#endif
+#endif
+%}
+
+%s none glob host indom metric type sem units space time count output
+%option case-insensitive
+
+%%
+
+<INITIAL>"global" { mystate= LEX_GLOBAL; return TOK_GLOBAL; }
+<INITIAL>"metric" { mystate = LEX_METRIC; return TOK_METRIC; }
+<INITIAL>"indom" { mystate = LEX_INDOM; return TOK_INDOM; }
+
+<glob>"hostname" { BEGIN(host); return TOK_HOSTNAME; }
+<glob>"time" { return TOK_TIME; }
+<glob>"tz" { return TOK_TZ; }
+ /* Hostname */
+<host>[A-Za-z0-9][A-Za-z0-9.-]* { yylval.str = dupstr(yytext, 0); BEGIN(glob); return TOK_HNAME; }
+
+<indom,metric>"delete" { return TOK_DELETE; }
+<indom>"indom" { return TOK_INDOM; }
+<indom>"duplicate" { return TOK_DUPLICATE; }
+<indom>"iname" { return TOK_INAME; }
+<indom>"inst" { return TOK_INST; }
+
+<metric>"name" { return TOK_NAME; }
+<metric>"pmid" { return TOK_PMID; }
+<metric>"type" { BEGIN(type); return TOK_TYPE; }
+<metric>"indom" { return TOK_INDOM; }
+<metric>"NULL" { return TOK_NULL_INT; }
+<metric>"output" { BEGIN(output); return TOK_OUTPUT; }
+<metric>"sem" { BEGIN(sem); return TOK_SEM; }
+<metric>"units" { BEGIN(units); comma_count = 0; return TOK_UNITS; }
+
+<type>"32" { yylval.ival = PM_TYPE_32; BEGIN(metric); return TOK_TYPE_NAME; }
+<type>"U32" { yylval.ival = PM_TYPE_U32; BEGIN(metric); return TOK_TYPE_NAME; }
+<type>"64" { yylval.ival = PM_TYPE_64; BEGIN(metric); return TOK_TYPE_NAME; }
+<type>"U64" { yylval.ival = PM_TYPE_U64; BEGIN(metric); return TOK_TYPE_NAME; }
+<type>"FLOAT" { yylval.ival = PM_TYPE_FLOAT; BEGIN(metric); return TOK_TYPE_NAME; }
+<type>"DOUBLE" { yylval.ival = PM_TYPE_DOUBLE; BEGIN(metric); return TOK_TYPE_NAME; }
+
+<output>"first" { yylval.ival = OUTPUT_FIRST; BEGIN(metric); return TOK_OUTPUT_TYPE; }
+<output>"last" { yylval.ival = OUTPUT_LAST; BEGIN(metric); return TOK_OUTPUT_TYPE; }
+<output>"min" { yylval.ival = OUTPUT_MIN; BEGIN(metric); return TOK_OUTPUT_TYPE; }
+<output>"max" { yylval.ival = OUTPUT_MAX; BEGIN(metric); return TOK_OUTPUT_TYPE; }
+<output>"sum" { yylval.ival = OUTPUT_SUM; BEGIN(metric); return TOK_OUTPUT_TYPE; }
+<output>"avg" { yylval.ival = OUTPUT_AVG; BEGIN(metric); return TOK_OUTPUT_TYPE; }
+<output>"inst" { BEGIN(metric); return TOK_INST; }
+<output>"iname" { BEGIN(metric); return TOK_INAME; }
+
+<sem>"COUNTER" { yylval.ival = PM_SEM_COUNTER; BEGIN(metric); return TOK_SEM_NAME; }
+<sem>"INSTANT" { yylval.ival = PM_SEM_INSTANT; BEGIN(metric); return TOK_SEM_NAME; }
+<sem>"DISCRETE" { yylval.ival = PM_SEM_DISCRETE; BEGIN(metric); return TOK_SEM_NAME; }
+
+<units>"," {
+ ++comma_count;
+ switch (comma_count) {
+ case 1:
+ case 2:
+ break;
+ case 3:
+ BEGIN(space);
+ break;
+ case 4:
+ BEGIN(time);
+ break;
+ case 5:
+ BEGIN(count);
+ break;
+ }
+ return TOK_COMMA;
+ }
+<metric>"rescale" { return TOK_RESCALE; }
+
+<space>"BYTE" { yylval.ival = PM_SPACE_BYTE; BEGIN(units); return TOK_SPACE_NAME; }
+<space>"KBYTE" { yylval.ival = PM_SPACE_KBYTE; BEGIN(units); return TOK_SPACE_NAME; }
+<space>"MBYTE" { yylval.ival = PM_SPACE_MBYTE; BEGIN(units); return TOK_SPACE_NAME; }
+<space>"GBYTE" { yylval.ival = PM_SPACE_GBYTE; BEGIN(units); return TOK_SPACE_NAME; }
+<space>"TBYTE" { yylval.ival = PM_SPACE_TBYTE; BEGIN(units); return TOK_SPACE_NAME; }
+<space>"PBYTE" { yylval.ival = PM_SPACE_PBYTE; BEGIN(units); return TOK_SPACE_NAME; }
+<space>"EBYTE" { yylval.ival = PM_SPACE_EBYTE; BEGIN(units); return TOK_SPACE_NAME; }
+<space>"0" { yylval.ival = 0; BEGIN(units); return TOK_SPACE_NAME; }
+
+<time>"NSEC" { yylval.ival = PM_TIME_NSEC; BEGIN(units); return TOK_TIME_NAME; }
+<time>"USEC" { yylval.ival = PM_TIME_USEC; BEGIN(units); return TOK_TIME_NAME; }
+<time>"MSEC" { yylval.ival = PM_TIME_MSEC; BEGIN(units); return TOK_TIME_NAME; }
+<time>"SEC" { yylval.ival = PM_TIME_SEC; BEGIN(units); return TOK_TIME_NAME; }
+<time>"MIN" { yylval.ival = PM_TIME_MIN; BEGIN(units); return TOK_TIME_NAME; }
+<time>"HOUR" { yylval.ival = PM_TIME_HOUR; BEGIN(units); return TOK_TIME_NAME; }
+<time>"0" { yylval.ival = 0; BEGIN(units); return TOK_TIME_NAME; }
+
+<count>"ONE" { yylval.ival = PM_COUNT_ONE; BEGIN(metric); return TOK_COUNT_NAME; }
+<count>[0-9]+ { yylval.ival = atoi(yytext); BEGIN(metric); return TOK_COUNT_NAME; }
+
+\"[^\"\n][^\"\n]*\" { yylval.str = dupstr(yytext, 1); return TOK_STRING; }
+
+[0-9]+ { yylval.str = dupstr(yytext, 0); return TOK_NUMBER; }
+
+[0-9]+\.[0-9]* { yylval.str = dupstr(yytext, 0); return TOK_FLOAT; }
+
+[0-9]+\.\* { yylval.str = dupstr(yytext, 0); return TOK_INDOM_STAR; }
+
+[0-9]+\.[0-9]+\.[0-9]+ { yylval.str = dupstr(yytext, 0); return TOK_PMID_INT; }
+
+[0-9]+\.[0-9]+\.\* { yylval.str = dupstr(yytext, 0); return TOK_PMID_STAR; }
+[0-9]+\.\*\.\* { yylval.str = dupstr(yytext, 0); return TOK_PMID_STAR; }
+
+ /* Generic name, e.g. for identifier or metric or hostname */
+[A-Za-z][A-Za-z0-9_.]* { yylval.str = dupstr(yytext, 0); return TOK_GNAME; }
+
+\#.* { }
+
+[ \t\r]+ { }
+
+"->" { return TOK_ASSIGN; }
+"{" {
+ if (mystate == LEX_GLOBAL) BEGIN(glob);
+ if (mystate == LEX_INDOM) BEGIN(indom);
+ if (mystate == LEX_METRIC) BEGIN(metric);
+#if PCP_DEBUG
+ if ((pmDebug & (DBG_TRACE_APPL0 | DBG_TRACE_APPL1)) == (DBG_TRACE_APPL0 | DBG_TRACE_APPL1))
+ fprintf(stderr, "lex: [%d] { begin state=%d\n", lineno, mystate);
+#endif
+ return TOK_LBRACE;
+ }
+"}" {
+#if PCP_DEBUG
+ if ((pmDebug & (DBG_TRACE_APPL0 | DBG_TRACE_APPL1)) == (DBG_TRACE_APPL0 | DBG_TRACE_APPL1))
+ fprintf(stderr, "lex: [%d] } end state=%d\n", lineno, mystate);
+#endif
+ mystate = LEX_NONE;
+ BEGIN(INITIAL); return TOK_RBRACE;
+ }
+"+" { return TOK_PLUS; }
+"-" { return TOK_MINUS; }
+":" { return TOK_COLON; }
+"," { return TOK_COMMA; }
+
+\n { lineno++; }
+. {
+ snprintf(mess, sizeof(mess), "Unexpected character '%c'",
+ yytext[0]);
+ yyerror(mess);
+ }
+%%
+
+int
+yywrap(void)
+{
+ return(1);
+}
diff --git a/src/pmlogrewrite/logger.h b/src/pmlogrewrite/logger.h
new file mode 100644
index 0000000..36c9712
--- /dev/null
+++ b/src/pmlogrewrite/logger.h
@@ -0,0 +1,178 @@
+/*
+ * Copyright (c) 2013 Red Hat.
+ * Copyright (c) 2004 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * common data structures for pmlogextract
+ */
+
+#ifndef _LOGGER_H
+#define _LOGGER_H
+
+extern int sflag; /* -s from command line */
+extern int vflag; /* -v from command line */
+extern int wflag; /* -w from command line */
+
+/*
+ * Global rewrite specifications
+ */
+typedef struct {
+ int flags; /* GLOBAL_* flags */
+ struct timeval time; /* timestamp shift */
+ char hostname[PM_LOG_MAXHOSTLEN];
+ char tz[PM_TZ_MAXLEN];
+} global_t;
+
+/* values for global_t flags */
+#define GLOBAL_CHANGE_TIME 1
+#define GLOBAL_CHANGE_HOSTNAME 2
+#define GLOBAL_CHANGE_TZ 4
+
+extern global_t global;
+
+/*
+ * Rewrite specifications for an instance domain
+ */
+typedef struct indomspec {
+ struct indomspec *i_next;
+ int indom_flags; /* INDOM_* flags */
+ int *inst_flags; /* INST_* flags */
+ pmInDom old_indom;
+ pmInDom new_indom; /* old_indom if no change */
+ int numinst;
+ int *old_inst; /* filled from pmGetInDomArchive() */
+ char **old_iname; /* filled from pmGetInDomArchive() */
+ int *new_inst;
+ char **new_iname;
+} indomspec_t;
+
+/* values for indomspec_t indom_flags */
+#define INDOM_ADD 1
+#define INDOM_DELETE 2
+#define INDOM_DUPLICATE 4
+/* values for indomspec_t inst_flags[] */
+#define INST_CHANGE_INST 16
+#define INST_CHANGE_INAME 32
+#define INST_DELETE 64
+
+extern indomspec_t *indom_root;
+
+/*
+ * Rewrite specifications for a metric
+ */
+typedef struct metricspec {
+ struct metricspec *m_next;
+ int flags; /* METRIC_* flags */
+ int output; /* OUTPUT_* values */
+ int one_inst; /* for OUTPUT_ONE INST */
+ char *one_name; /* for OUTPUT_ONE NAME */
+ char *old_name;
+ char *new_name;
+ pmDesc old_desc;
+ pmDesc new_desc;
+ indomspec_t *ip; /* for instance id changes */
+} metricspec_t;
+
+/* values for metricspec_t flags[] */
+#define METRIC_CHANGE_PMID 1
+#define METRIC_CHANGE_NAME 2
+#define METRIC_CHANGE_TYPE 4
+#define METRIC_CHANGE_INDOM 8
+#define METRIC_CHANGE_SEM 16
+#define METRIC_CHANGE_UNITS 32
+#define METRIC_DELETE 64
+#define METRIC_RESCALE 128
+
+/* values for output when indom (numval >= 1) => PM_INDOM_NULL (numval = 1) */
+#define OUTPUT_ALL 0
+#define OUTPUT_FIRST 1
+#define OUTPUT_LAST 2
+#define OUTPUT_ONE 3
+#define OUTPUT_MIN 4
+#define OUTPUT_MAX 5
+#define OUTPUT_SUM 6
+#define OUTPUT_AVG 7
+
+extern metricspec_t *metric_root;
+
+/*
+ * Input archive control
+ */
+typedef struct {
+ int ctx;
+ __pmContext *ctxp;
+ char *name;
+ pmLogLabel label;
+ __pmPDU *metarec;
+ __pmPDU *logrec;
+ pmResult *rp;
+ int mark; /* need EOL marker */
+} inarch_t;
+
+extern inarch_t inarch; /* input archive */
+
+/*
+ * Output archive control
+ */
+typedef struct {
+ char *name; /* base name of output archive */
+ __pmLogCtl logctl; /* libpcp control */
+} outarch_t;
+
+extern outarch_t outarch; /* output archive */
+
+/* size of a string length field */
+#define LENSIZE 4
+
+/* generic error message buffer */
+extern char mess[256];
+
+/* yylex() gets intput from here ... */
+extern char *configfile;
+extern FILE *fconfig;
+extern int lineno;
+extern FILE *yyin;
+
+extern void yyerror(char *);
+extern void yywarn(char *);
+extern void yysemantic(char *);
+extern int yylex(void);
+extern int yyparse(void);
+
+#define W_START 1
+#define W_NEXT 2
+#define W_NONE 3
+
+extern int _pmLogGet(__pmLogCtl *, int, __pmPDU **);
+extern int _pmLogPut(FILE *, __pmPDU *);
+extern int _pmLogRename(const char *, const char *);
+extern int _pmLogRemove(const char *);
+extern pmUnits ntoh_pmUnits(pmUnits);
+#define ntoh_pmInDom(indom) ntohl(indom)
+#define ntoh_pmID(pmid) ntohl(pmid)
+
+extern metricspec_t *start_metric(pmID);
+extern indomspec_t *start_indom(pmInDom);
+extern int change_inst_by_inst(pmInDom, int, int);
+extern int change_inst_by_name(pmInDom, char *, char *);
+extern int inst_name_eq(const char *, const char *);
+
+extern char *SemStr(int);
+extern void newvolume(int);
+
+extern void do_desc(void);
+extern void do_indom(void);
+extern void do_result(void);
+
+extern void abandon(void);
+
+#endif /* _LOGGER_H */
diff --git a/src/pmlogrewrite/logio.c b/src/pmlogrewrite/logio.c
new file mode 100644
index 0000000..010e0e3
--- /dev/null
+++ b/src/pmlogrewrite/logio.c
@@ -0,0 +1,175 @@
+/*
+ * utils for pmlogextract
+ *
+ * Copyright (c) 1997-2002 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#include <assert.h>
+#include "pmapi.h"
+#include "impl.h"
+
+/*
+ * raw read of next log record - largely stolen from __pmLogRead in libpcp
+ */
+int
+_pmLogGet(__pmLogCtl *lcp, int vol, __pmPDU **pb)
+{
+ int head;
+ int tail;
+ int sts;
+ long offset;
+ char *p;
+ __pmPDU *lpb;
+ FILE *f;
+
+ if (vol == PM_LOG_VOL_META)
+ f = lcp->l_mdfp;
+ else
+ f = lcp->l_mfp;
+
+ offset = ftell(f);
+ assert(offset >= 0);
+#ifdef PCP_DEBUG
+ if (pmDebug & DBG_TRACE_LOG) {
+ fprintf(stderr, "_pmLogGet: fd=%d vol=%d posn=%ld ",
+ fileno(f), vol, offset);
+ }
+#endif
+
+again:
+ sts = (int)fread(&head, 1, sizeof(head), f);
+ if (sts != sizeof(head)) {
+ if (sts == 0) {
+#ifdef PCP_DEBUG
+ if (pmDebug & DBG_TRACE_LOG)
+ fprintf(stderr, "AFTER end\n");
+#endif
+ fseek(f, offset, SEEK_SET);
+ if (vol != PM_LOG_VOL_META) {
+ if (lcp->l_curvol < lcp->l_maxvol) {
+ if (__pmLogChangeVol(lcp, lcp->l_curvol+1) == 0) {
+ f = lcp->l_mfp;
+ goto again;
+ }
+ }
+ }
+ return PM_ERR_EOL;
+ }
+#ifdef PCP_DEBUG
+ if (pmDebug & DBG_TRACE_LOG)
+ fprintf(stderr, "Error: hdr fread=%d %s\n", sts, osstrerror());
+#endif
+ if (sts > 0)
+ return PM_ERR_LOGREC;
+ else
+ return -oserror();
+ }
+
+ if ((lpb = (__pmPDU *)malloc(ntohl(head))) == NULL) {
+#ifdef PCP_DEBUG
+ if (pmDebug & DBG_TRACE_LOG)
+ fprintf(stderr, "Error: __pmFindPDUBuf(%d) %s\n",
+ (int)ntohl(head), osstrerror());
+#endif
+ fseek(f, offset, SEEK_SET);
+ return -oserror();
+ }
+
+ lpb[0] = head;
+ if ((sts = (int)fread(&lpb[1], 1, ntohl(head) - sizeof(head), f)) != ntohl(head) - sizeof(head)) {
+#ifdef PCP_DEBUG
+ if (pmDebug & DBG_TRACE_LOG)
+ fprintf(stderr, "Error: data fread=%d %s\n", sts, osstrerror());
+#endif
+ free(lpb);
+ if (sts == 0) {
+ fseek(f, offset, SEEK_SET);
+ return PM_ERR_EOL;
+ }
+ else if (sts > 0)
+ return PM_ERR_LOGREC;
+ else
+ return -oserror();
+ }
+
+
+ p = (char *)lpb;
+ memcpy(&tail, &p[ntohl(head) - sizeof(head)], sizeof(head));
+ if (head != tail) {
+#ifdef PCP_DEBUG
+ if (pmDebug & DBG_TRACE_LOG)
+ fprintf(stderr, "Error: head-tail mismatch (%d-%d)\n",
+ (int)ntohl(head), (int)ntohl(tail));
+#endif
+ free(lpb);
+ return PM_ERR_LOGREC;
+ }
+
+#ifdef PCP_DEBUG
+ if (pmDebug & DBG_TRACE_LOG) {
+ if (vol != PM_LOG_VOL_META || ntohl(lpb[1]) == TYPE_INDOM) {
+ fprintf(stderr, "@");
+ if (sts >= 0) {
+ struct timeval stamp;
+ __pmTimeval *tvp = (__pmTimeval *)&lpb[vol == PM_LOG_VOL_META ? 2 : 1];
+ stamp.tv_sec = ntohl(tvp->tv_sec);
+ stamp.tv_usec = ntohl(tvp->tv_usec);
+ __pmPrintStamp(stderr, &stamp);
+ }
+ else
+ fprintf(stderr, "unknown time");
+ }
+ fprintf(stderr, " len=%d (incl head+tail)\n", (int)ntohl(head));
+ }
+#endif
+
+#ifdef PCP_DEBUG
+ if (pmDebug & DBG_TRACE_PDU) {
+ int i, j;
+ struct timeval stamp;
+ __pmTimeval *tvp = (__pmTimeval *)&lpb[vol == PM_LOG_VOL_META ? 2 : 1];
+ fprintf(stderr, "_pmLogGet");
+ if (vol != PM_LOG_VOL_META || ntohl(lpb[1]) == TYPE_INDOM) {
+ fprintf(stderr, " timestamp=");
+ stamp.tv_sec = ntohl(tvp->tv_sec);
+ stamp.tv_usec = ntohl(tvp->tv_usec);
+ __pmPrintStamp(stderr, &stamp);
+ }
+ fprintf(stderr, " " PRINTF_P_PFX "%p ... " PRINTF_P_PFX "%p", lpb, &lpb[ntohl(head)/sizeof(__pmPDU) - 1]);
+ fputc('\n', stderr);
+ fprintf(stderr, "%03d: ", 0);
+ for (j = 0, i = 0; j < ntohl(head)/sizeof(__pmPDU); j++) {
+ if (i == 8) {
+ fprintf(stderr, "\n%03d: ", j);
+ i = 0;
+ }
+ fprintf(stderr, "%08x ", lpb[j]);
+ i++;
+ }
+ fputc('\n', stderr);
+ }
+#endif
+
+ *pb = lpb;
+ return 0;
+}
+
+pmUnits
+ntoh_pmUnits(pmUnits units)
+{
+ unsigned int x;
+
+ x = ntohl(*(unsigned int *)&units);
+ units = *(pmUnits *)&x;
+ return units;
+}
diff --git a/src/pmlogrewrite/metric.c b/src/pmlogrewrite/metric.c
new file mode 100644
index 0000000..31da308
--- /dev/null
+++ b/src/pmlogrewrite/metric.c
@@ -0,0 +1,230 @@
+/*
+ * Metric metadata support for pmlogrewrite
+ *
+ * Copyright (c) 2011 Ken McDonell. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#include "pmapi.h"
+#include "impl.h"
+#include "logger.h"
+#include <assert.h>
+
+/*
+ * Find or create a new metricspec_t
+ */
+metricspec_t *
+start_metric(pmID pmid)
+{
+ metricspec_t *mp;
+ int sts;
+
+#if PCP_DEBUG
+ if ((pmDebug & (DBG_TRACE_APPL0 | DBG_TRACE_APPL1)) == (DBG_TRACE_APPL0 | DBG_TRACE_APPL1))
+ fprintf(stderr, "start_metric(%s)", pmIDStr(pmid));
+#endif
+
+ for (mp = metric_root; mp != NULL; mp = mp->m_next) {
+ if (pmid == mp->old_desc.pmid) {
+#if PCP_DEBUG
+ if ((pmDebug & (DBG_TRACE_APPL0 | DBG_TRACE_APPL1)) == (DBG_TRACE_APPL0 | DBG_TRACE_APPL1))
+ fprintf(stderr, " -> %s\n", mp->old_name);
+#endif
+ break;
+ }
+ }
+ if (mp == NULL) {
+ char *name;
+ pmDesc desc;
+
+ sts = pmNameID(pmid, &name);
+ if (sts < 0) {
+ if (wflag) {
+ snprintf(mess, sizeof(mess), "Metric %s pmNameID: %s", pmIDStr(pmid), pmErrStr(sts));
+ yywarn(mess);
+ }
+ return NULL;
+ }
+ sts = pmLookupDesc(pmid, &desc);
+ if (sts < 0) {
+ if (wflag) {
+ snprintf(mess, sizeof(mess), "Metric %s: pmLookupDesc: %s", pmIDStr(pmid), pmErrStr(sts));
+ yywarn(mess);
+ }
+ free(name);
+ return NULL;
+ }
+
+ mp = (metricspec_t *)malloc(sizeof(metricspec_t));
+ if (mp == NULL) {
+ fprintf(stderr, "metricspec malloc(%d) failed: %s\n", (int)sizeof(metricspec_t), strerror(errno));
+ abandon();
+ /*NOTREACHED*/
+ }
+ mp->m_next = metric_root;
+ metric_root = mp;
+ mp->output = OUTPUT_ALL;
+ mp->one_inst = 0;
+ mp->one_name = NULL;
+ mp->old_name = name;
+ mp->old_desc = desc;
+ mp->new_desc = mp->old_desc;
+ mp->flags = 0;
+ mp->ip = NULL;
+#if PCP_DEBUG
+ if ((pmDebug & (DBG_TRACE_APPL0 | DBG_TRACE_APPL1)) == (DBG_TRACE_APPL0 | DBG_TRACE_APPL1))
+ fprintf(stderr, " -> %s [new entry]\n", mp->old_name);
+#endif
+ }
+
+ return mp;
+}
+
+typedef struct {
+ __pmLogHdr hdr;
+ pmDesc desc;
+ int numnames;
+ char strbuf[1];
+} desc_t;
+
+/*
+ * reverse the logic of __pmLogPutDesc()
+ */
+static void
+_pmUnpackDesc(__pmPDU *pdubuf, pmDesc *desc, int *numnames, char ***names)
+{
+ desc_t *pp;
+ int i;
+ char *p;
+ int slen;
+
+ pp = (desc_t *)pdubuf;
+ desc->type = ntohl(pp->desc.type);
+ desc->sem = ntohl(pp->desc.sem);
+ desc->indom = ntoh_pmInDom(pp->desc.indom);
+ desc->units = ntoh_pmUnits(pp->desc.units);
+ desc->pmid = ntoh_pmID(pp->desc.pmid);
+ *numnames = ntohl(pp->numnames);
+ *names = (char **)malloc(*numnames * sizeof(*names[1]));
+ if (*names == NULL) {
+ fprintf(stderr, "_pmUnpackDesc malloc(%d) failed: %s\n", (int)(*numnames * sizeof(*names[1])), strerror(errno));
+ abandon();
+ /*NOTREACHED*/
+ }
+
+ p = pp->strbuf;
+ for (i = 0; i < *numnames; i++) {
+ memcpy(&slen, p, LENSIZE);
+ slen = ntohl(slen);
+ p += LENSIZE;
+ (*names)[i] = malloc(slen+1);
+ if ((*names)[i] == NULL) {
+ fprintf(stderr, "_pmUnpackDesc malloc(%d) failed: %s\n", slen+1, strerror(errno));
+ abandon();
+ /*NOTREACHED*/
+ }
+ strncpy((*names)[i], p, slen);
+ (*names)[i][slen] = '\0';
+ p += slen;
+ }
+
+ return;
+}
+
+/*
+ * rewrite pmDesc from metadata, returns
+ * -1 delete this pmDesc
+ * 0 no change
+ * 1 changed
+ */
+void
+do_desc(void)
+{
+ metricspec_t *mp;
+ pmDesc desc;
+ int i;
+ int sts;
+ int numnames;
+ char **names;
+ long out_offset;
+
+ out_offset = ftell(outarch.logctl.l_mdfp);
+ _pmUnpackDesc(inarch.metarec, &desc, &numnames, &names);
+
+ for (mp = metric_root; mp != NULL; mp = mp->m_next) {
+ if (desc.pmid != mp->old_desc.pmid || mp->flags == 0)
+ continue;
+ if (mp->flags & METRIC_DELETE) {
+#if PCP_DEBUG
+ if (pmDebug & DBG_TRACE_APPL1)
+ fprintf(stderr, "Delete: pmDesc for %s\n", pmIDStr(desc.pmid));
+#endif
+ goto done;
+ }
+#if PCP_DEBUG
+ if (pmDebug & DBG_TRACE_APPL1)
+ fprintf(stderr, "Rewrite: pmDesc for %s\n", pmIDStr(desc.pmid));
+#endif
+ if (mp->flags & METRIC_CHANGE_PMID)
+ desc.pmid = mp->new_desc.pmid;
+ if (mp->flags & METRIC_CHANGE_NAME) {
+ for (i = 0; i < numnames; i++) {
+ if (strcmp(names[i], mp->old_name) == 0) {
+ free(names[i]);
+ names[i] = strdup(mp->new_name);
+ if (names[i] == NULL) {
+ fprintf(stderr, "do_desc strdup(%s) failed: %s\n", mp->new_name, strerror(errno));
+ abandon();
+ /*NOTREACHED*/
+ }
+ break;
+ }
+ }
+ if (i == numnames) {
+ fprintf(stderr, "%s: Botch: old name %s not found in list of %d names for pmid %s ...",
+ pmProgname, mp->old_name, numnames, pmIDStr(mp->old_desc.pmid));
+ for (i = 0; i < numnames; i++) {
+ if (i > 0) fputc(',', stderr);
+ fprintf(stderr, " %s", names[i]);
+ }
+ fputc('\n', stderr);
+ abandon();
+ /*NOTREACHED*/
+ }
+ }
+ if (mp->flags & METRIC_CHANGE_TYPE)
+ desc.type = mp->new_desc.type;
+ if (mp->flags & METRIC_CHANGE_INDOM)
+ desc.indom = mp->new_desc.indom;
+ if (mp->flags & METRIC_CHANGE_SEM)
+ desc.sem = mp->new_desc.sem;
+ if (mp->flags & METRIC_CHANGE_UNITS)
+ desc.units = mp->new_desc.units; /* struct assignment */
+ break;
+ }
+ if ((sts = __pmLogPutDesc(&outarch.logctl, &desc, numnames, names)) < 0) {
+ fprintf(stderr, "%s: Error: __pmLogPutDesc: %s (%s): %s\n",
+ pmProgname, names[0], pmIDStr(desc.pmid), pmErrStr(sts));
+ abandon();
+ /*NOTREACHED*/
+ }
+#if PCP_DEBUG
+ if (pmDebug & DBG_TRACE_APPL0)
+ fprintf(stderr, "Metadata: write PMID %s @ offset=%ld\n", pmIDStr(desc.pmid), out_offset);
+#endif
+
+done:
+ for (i = 0; i < numnames; i++)
+ free(names[i]);
+ free(names);
+ return;
+}
diff --git a/src/pmlogrewrite/pmlogrewrite.c b/src/pmlogrewrite/pmlogrewrite.c
new file mode 100644
index 0000000..625366d
--- /dev/null
+++ b/src/pmlogrewrite/pmlogrewrite.c
@@ -0,0 +1,1318 @@
+/*
+ * pmlogrewrite - config-driven stream editor for PCP archives
+ *
+ * Copyright (c) 2013-2014 Red Hat.
+ * Copyright (c) 2011 Ken McDonell. All Rights Reserved.
+ * Copyright (c) 1997-2002 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#include <math.h>
+#include <ctype.h>
+#include <sys/stat.h>
+#include <assert.h>
+#include "pmapi.h"
+#include "impl.h"
+#include "logger.h"
+#include <assert.h>
+
+global_t global;
+indomspec_t *indom_root;
+metricspec_t *metric_root;
+int lineno;
+
+static pmLongOptions longopts[] = {
+ PMAPI_OPTIONS_HEADER("Options"),
+ PMOPT_DEBUG,
+ { "config", 1, 'c', "PATH", "file or directory to load rules from" },
+ { "check", 0, 'C', 0, "parse config file(s) and quit (verbose warnings also)" },
+ { "desperate", 0, 'd', 0, "desperate, save output archive even after error" },
+ { "", 0, 'i', 0, "rewrite in place, input-archive will be over-written" },
+ { "quick", 0, 'q', 0, "quick mode, no output if no change" },
+ { "scale", 0, 's', 0, "do scale conversion" },
+ { "verbose", 0, 'v', 0, "increased diagnostic verbosity" },
+ { "warnings", 0, 'w', 0, "emit warnings [default is silence]" },
+ PMAPI_OPTIONS_TEXT(""),
+ PMAPI_OPTIONS_TEXT("output-archive is required unless -i is specified"),
+ PMAPI_OPTIONS_END
+};
+
+static pmOptions opts = {
+ .short_options = "c:CdD:iqsvw?",
+ .long_options = longopts,
+ .short_usage = "[options] input-archive [output-archive]",
+};
+
+/*
+ * Global variables
+ */
+static int first_datarec = 1; /* first record flag */
+static char bak_base[MAXPATHLEN+1]; /* basename for backup with -i */
+
+off_t new_log_offset; /* new log offset */
+off_t new_meta_offset; /* new meta offset */
+
+
+/* archive control stuff */
+inarch_t inarch; /* input archive control */
+outarch_t outarch; /* output archive control */
+
+/* command line args */
+int nconf; /* number of config files */
+char **conf; /* list of config files */
+char *configfile; /* current config file */
+int Cflag; /* -C parse config and quit */
+int dflag; /* -d desperate */
+int iflag; /* -i in-place */
+int qflag; /* -q quick or quiet */
+int sflag; /* -s scale values */
+int vflag; /* -v verbosity */
+int wflag; /* -w emit warnings */
+
+/*
+ * report that archive is corrupted
+ */
+static void
+_report(FILE *fp)
+{
+ off_t here;
+ struct stat sbuf;
+
+ here = lseek(fileno(fp), 0L, SEEK_CUR);
+ fprintf(stderr, "%s: Error occurred at byte offset %ld into a file of",
+ pmProgname, (long)here);
+ if (fstat(fileno(fp), &sbuf) < 0)
+ fprintf(stderr, ": stat: %s\n", osstrerror());
+ else
+ fprintf(stderr, " %ld bytes.\n", (long)sbuf.st_size);
+ if (dflag)
+ fprintf(stderr, "The last record, and the remainder of this file will not be processed.\n");
+ abandon();
+ /*NOTREACHED*/
+}
+
+/*
+ * switch output volumes
+ */
+void
+newvolume(int vol)
+{
+ FILE *newfp;
+
+ if ((newfp = __pmLogNewFile(outarch.name, vol)) != NULL) {
+ fclose(outarch.logctl.l_mfp);
+ outarch.logctl.l_mfp = newfp;
+ outarch.logctl.l_label.ill_vol = outarch.logctl.l_curvol = vol;
+ __pmLogWriteLabel(outarch.logctl.l_mfp, &outarch.logctl.l_label);
+ fflush(outarch.logctl.l_mfp);
+ }
+ else {
+ fprintf(stderr, "%s: __pmLogNewFile(%s,%d) Error: %s\n",
+ pmProgname, outarch.name, vol, pmErrStr(-oserror()));
+ abandon();
+ /*NOTREACHED*/
+ }
+}
+
+/* construct new archive label */
+static void
+newlabel(void)
+{
+ __pmLogLabel *lp = &outarch.logctl.l_label;
+
+ /* copy magic number, pid, host and timezone */
+ lp->ill_magic = inarch.label.ll_magic;
+ lp->ill_pid = inarch.label.ll_pid;
+ if (global.flags & GLOBAL_CHANGE_HOSTNAME)
+ strncpy(lp->ill_hostname, global.hostname, PM_LOG_MAXHOSTLEN);
+ else
+ strncpy(lp->ill_hostname, inarch.label.ll_hostname, PM_LOG_MAXHOSTLEN);
+ lp->ill_hostname[PM_LOG_MAXHOSTLEN-1] = '\0';
+ if (global.flags & GLOBAL_CHANGE_TZ)
+ strncpy(lp->ill_tz, global.tz, PM_TZ_MAXLEN);
+ else
+ strncpy(lp->ill_tz, inarch.label.ll_tz, PM_TZ_MAXLEN);
+ lp->ill_tz[PM_TZ_MAXLEN-1] = '\0';
+}
+
+/*
+ * write label records at the start of each physical file
+ */
+void
+writelabel(int do_rewind)
+{
+ off_t old_offset;
+
+ if (do_rewind) {
+ old_offset = ftell(outarch.logctl.l_tifp);
+ assert(old_offset >= 0);
+ rewind(outarch.logctl.l_tifp);
+ }
+ outarch.logctl.l_label.ill_vol = PM_LOG_VOL_TI;
+ __pmLogWriteLabel(outarch.logctl.l_tifp, &outarch.logctl.l_label);
+ if (do_rewind)
+ fseek(outarch.logctl.l_tifp, (long)old_offset, SEEK_SET);
+
+ if (do_rewind) {
+ old_offset = ftell(outarch.logctl.l_mdfp);
+ assert(old_offset >= 0);
+ rewind(outarch.logctl.l_mdfp);
+ }
+ outarch.logctl.l_label.ill_vol = PM_LOG_VOL_META;
+ __pmLogWriteLabel(outarch.logctl.l_mdfp, &outarch.logctl.l_label);
+ if (do_rewind)
+ fseek(outarch.logctl.l_mdfp, (long)old_offset, SEEK_SET);
+
+ if (do_rewind) {
+ old_offset = ftell(outarch.logctl.l_mfp);
+ assert(old_offset >= 0);
+ rewind(outarch.logctl.l_mfp);
+ }
+ outarch.logctl.l_label.ill_vol = 0;
+ __pmLogWriteLabel(outarch.logctl.l_mfp, &outarch.logctl.l_label);
+ if (do_rewind)
+ fseek(outarch.logctl.l_mfp, (long)old_offset, SEEK_SET);
+}
+
+/*
+ * read next metadata record
+ */
+static int
+nextmeta()
+{
+ int sts;
+ __pmLogCtl *lcp;
+
+ lcp = inarch.ctxp->c_archctl->ac_log;
+ if ((sts = _pmLogGet(lcp, PM_LOG_VOL_META, &inarch.metarec)) < 0) {
+ if (sts != PM_ERR_EOL) {
+ fprintf(stderr, "%s: Error: _pmLogGet[meta %s]: %s\n",
+ pmProgname, inarch.name, pmErrStr(sts));
+ _report(lcp->l_mdfp);
+ }
+ return -1;
+ }
+
+ return ntohl(inarch.metarec[1]);
+}
+
+
+/*
+ * read next log record
+ *
+ * return status is
+ * 0 ok
+ * 1 ok, but volume switched
+ * PM_ERR_EOL end of file
+ * -1 fatal error
+ */
+static int
+nextlog(void)
+{
+ int sts;
+ __pmLogCtl *lcp;
+ int old_vol;
+
+
+ lcp = inarch.ctxp->c_archctl->ac_log;
+ old_vol = inarch.ctxp->c_archctl->ac_log->l_curvol;
+
+ if ((sts = __pmLogRead(lcp, PM_MODE_FORW, NULL, &inarch.rp, PMLOGREAD_NEXT)) < 0) {
+ if (sts != PM_ERR_EOL) {
+ fprintf(stderr, "%s: Error: __pmLogRead[log %s]: %s\n",
+ pmProgname, inarch.name, pmErrStr(sts));
+ _report(lcp->l_mfp);
+ }
+ return -1;
+ }
+
+ return old_vol == inarch.ctxp->c_archctl->ac_log->l_curvol ? 0 : 1;
+}
+
+#ifdef IS_MINGW
+#define S_ISLINK(mode) 0 /* no symlink support */
+#else
+#ifndef S_ISLINK
+#define S_ISLINK(mode) ((mode & S_IFMT) == S_IFLNK)
+#endif
+#endif
+
+/*
+ * parse command line arguments
+ */
+int
+parseargs(int argc, char *argv[])
+{
+ int c;
+ int sts;
+ int sep = __pmPathSeparator();
+ struct stat sbuf;
+
+ while ((c = pmgetopt_r(argc, argv, &opts)) != EOF) {
+ switch (c) {
+
+ case 'c': /* config file */
+ if (stat(opts.optarg, &sbuf) < 0) {
+ pmprintf("%s: stat(%s) failed: %s\n",
+ pmProgname, opts.optarg, osstrerror());
+ opts.errors++;
+ break;
+ }
+ if (S_ISREG(sbuf.st_mode) || S_ISLINK(sbuf.st_mode)) {
+ nconf++;
+ if ((conf = (char **)realloc(conf, nconf*sizeof(conf[0]))) != NULL)
+ conf[nconf-1] = opts.optarg;
+ }
+ else if (S_ISDIR(sbuf.st_mode)) {
+ DIR *dirp;
+ struct dirent *dp;
+ char path[MAXPATHLEN+1];
+
+ if ((dirp = opendir(opts.optarg)) == NULL) {
+ pmprintf("%s: opendir(%s) failed: %s\n", pmProgname, opts.optarg, osstrerror());
+ opts.errors++;
+ }
+ else while ((dp = readdir(dirp)) != NULL) {
+ /* skip ., .. and "hidden" files */
+ if (dp->d_name[0] == '.') continue;
+ snprintf(path, sizeof(path), "%s%c%s", opts.optarg, sep, dp->d_name);
+ if (stat(path, &sbuf) < 0) {
+ pmprintf("%s: %s: %s\n", pmProgname, path, osstrerror());
+ opts.errors++;
+ }
+ else if (S_ISREG(sbuf.st_mode) || S_ISLINK(sbuf.st_mode)) {
+ nconf++;
+ if ((conf = (char **)realloc(conf, nconf*sizeof(conf[0]))) == NULL)
+ break;
+ if ((conf[nconf-1] = strdup(path)) == NULL) {
+ fprintf(stderr, "conf[%d] strdup(%s) failed: %s\n", nconf-1, path, strerror(errno));
+ abandon();
+ /*NOTREACHED*/
+ }
+ }
+ }
+ if (dirp != NULL)
+ closedir(dirp);
+ }
+ else {
+ pmprintf("%s: Error: -c config %s is not a file or directory\n", pmProgname, opts.optarg);
+ opts.errors++;
+ }
+ if (nconf > 0 && conf == NULL) {
+ fprintf(stderr, "%s: Error: conf[%d] realloc(%d) failed: %s\n", pmProgname, nconf, (int)(nconf*sizeof(conf[0])), strerror(errno));
+ abandon();
+ /*NOTREACHED*/
+ }
+ break;
+
+ case 'C': /* parse configs and quit */
+ Cflag = 1;
+ vflag = 1;
+ wflag = 1;
+ break;
+
+ case 'd': /* desperate */
+ dflag = 1;
+ break;
+
+ case 'D': /* debug flag */
+ sts = __pmParseDebug(opts.optarg);
+ if (sts < 0) {
+ pmprintf("%s: unrecognized debug flag specification (%s)\n",
+ pmProgname, opts.optarg);
+ opts.errors++;
+ }
+ else
+ pmDebug |= sts;
+ break;
+
+ case 'i': /* in-place, over-write input archive */
+ iflag = 1;
+ break;
+
+ case 'q': /* quick or quiet */
+ qflag = 1;
+ break;
+
+ case 's': /* do scale conversions */
+ sflag = 1;
+ break;
+
+ case 'v': /* verbosity */
+ vflag++;
+ break;
+
+ case 'w': /* print warnings */
+ wflag = 1;
+ break;
+
+ case '?':
+ default:
+ opts.errors++;
+ break;
+ }
+ }
+
+ if (opts.errors == 0) {
+ if ((iflag == 0 && opts.optind != argc-2) ||
+ (iflag == 1 && opts.optind != argc-1))
+ opts.errors++;
+ }
+
+ return -opts.errors;
+}
+
+static void
+parseconfig(char *file)
+{
+ configfile = file;
+ if ((yyin = fopen(configfile, "r")) == NULL) {
+ fprintf(stderr, "%s: Cannot open config file \"%s\": %s\n",
+ pmProgname, configfile, osstrerror());
+ exit(1);
+ }
+ if (vflag > 1)
+ fprintf(stderr, "Start configfile: %s\n", file);
+ lineno = 1;
+
+ if (yyparse() != 0)
+ exit(1);
+
+ fclose(yyin);
+ yyin = NULL;
+
+ return;
+}
+
+char *
+SemStr(int sem)
+{
+ static char buf[20];
+
+ if (sem == PM_SEM_COUNTER) snprintf(buf, sizeof(buf), "counter");
+ else if (sem == PM_SEM_INSTANT) snprintf(buf, sizeof(buf), "instant");
+ else if (sem == PM_SEM_DISCRETE) snprintf(buf, sizeof(buf), "discrete");
+ else snprintf(buf, sizeof(buf), "bad sem? %d", sem);
+
+ return buf;
+}
+
+static void
+reportconfig(void)
+{
+ indomspec_t *ip;
+ metricspec_t *mp;
+ int i;
+ int change = 0;
+
+ printf("PCP Archive Log Rewrite Specifications Summary\n");
+ change |= (global.flags != 0);
+ if (global.flags & GLOBAL_CHANGE_HOSTNAME)
+ printf("Hostname:\t%s -> %s\n", inarch.label.ll_hostname, global.hostname);
+ if (global.flags & GLOBAL_CHANGE_TZ)
+ printf("Timezone:\t%s -> %s\n", inarch.label.ll_tz, global.tz);
+ if (global.flags & GLOBAL_CHANGE_TIME) {
+ static struct tm *tmp;
+ char *sign = "";
+ time_t time;
+ if (global.time.tv_sec < 0) {
+ time = (time_t)(-global.time.tv_sec);
+ sign = "-";
+ }
+ else
+ time = (time_t)global.time.tv_sec;
+ tmp = gmtime(&time);
+ tmp->tm_hour += 24 * tmp->tm_yday;
+ if (tmp->tm_hour < 10)
+ printf("Delta:\t\t-> %s%02d:%02d:%02d.%06d\n", sign, tmp->tm_hour, tmp->tm_min, tmp->tm_sec, (int)global.time.tv_usec);
+ else
+ printf("Delta:\t\t-> %s%d:%02d:%02d.%06d\n", sign, tmp->tm_hour, tmp->tm_min, tmp->tm_sec, (int)global.time.tv_usec);
+ }
+ for (ip = indom_root; ip != NULL; ip = ip->i_next) {
+ int hdr_done = 0;
+ if (ip->new_indom != ip->old_indom) {
+ printf("\nInstance Domain: %s\n", pmInDomStr(ip->old_indom));
+ hdr_done = 1;
+ printf("pmInDom:\t-> %s\n", pmInDomStr(ip->new_indom));
+ change |= 1;
+ }
+ for (i = 0; i < ip->numinst; i++) {
+ change |= (ip->inst_flags[i] != 0);
+ if (ip->inst_flags[i]) {
+ if (hdr_done == 0) {
+ printf("\nInstance Domain: %s\n", pmInDomStr(ip->old_indom));
+ hdr_done = 1;
+ }
+ printf("Instance:\t\[%d] \"%s\" -> ", ip->old_inst[i], ip->old_iname[i]);
+ if (ip->inst_flags[i] & INST_DELETE)
+ printf("DELETE\n");
+ else {
+ if (ip->inst_flags[i] & INST_CHANGE_INST)
+ printf("[%d] ", ip->new_inst[i]);
+ else
+ printf("[%d] ", ip->old_inst[i]);
+ if (ip->inst_flags[i] & INST_CHANGE_INAME)
+ printf("\"%s\"\n", ip->new_iname[i]);
+ else
+ printf("\"%s\"\n", ip->old_iname[i]);
+ }
+ }
+ }
+ }
+ for (mp = metric_root; mp != NULL; mp = mp->m_next) {
+ if (mp->flags != 0 || mp->ip != NULL) {
+ change |= 1;
+ printf("\nMetric: %s (%s)\n", mp->old_name, pmIDStr(mp->old_desc.pmid));
+ }
+ if (mp->flags & METRIC_CHANGE_PMID) {
+ printf("pmID:\t\t%s ->", pmIDStr(mp->old_desc.pmid));
+ printf(" %s\n", pmIDStr(mp->new_desc.pmid));
+ }
+ if (mp->flags & METRIC_CHANGE_NAME)
+ printf("Name:\t\t%s -> %s\n", mp->old_name, mp->new_name);
+ if (mp->flags & METRIC_CHANGE_TYPE) {
+ printf("Type:\t\t%s ->", pmTypeStr(mp->old_desc.type));
+ printf(" %s\n", pmTypeStr(mp->new_desc.type));
+ }
+ if (mp->flags & METRIC_CHANGE_INDOM) {
+ printf("InDom:\t\t%s ->", pmInDomStr(mp->old_desc.indom));
+ printf(" %s\n", pmInDomStr(mp->new_desc.indom));
+ if (mp->output != OUTPUT_ALL) {
+ printf("Output:\t\t");
+ switch (mp->output) {
+ case OUTPUT_ONE:
+ if (mp->old_desc.indom != PM_INDOM_NULL) {
+ printf("value for instance");
+ if (mp->one_inst != PM_IN_NULL)
+ printf(" %d", mp->one_inst);
+ if (mp->one_name != NULL)
+ printf(" \"%s\"", mp->one_name);
+ putchar('\n');
+ }
+ else
+ printf("the only value (output instance %d)\n", mp->one_inst);
+ break;
+ case OUTPUT_FIRST:
+ if (mp->old_desc.indom != PM_INDOM_NULL)
+ printf("first value\n");
+ else {
+ if (mp->one_inst != PM_IN_NULL)
+ printf("first and only value (output instance %d)\n", mp->one_inst);
+ else
+ printf("first and only value (output instance \"%s\")\n", mp->one_name);
+ }
+ break;
+ case OUTPUT_LAST:
+ if (mp->old_desc.indom != PM_INDOM_NULL)
+ printf("last value\n");
+ else
+ printf("last and only value (output instance %d)\n", mp->one_inst);
+ break;
+ case OUTPUT_MIN:
+ if (mp->old_desc.indom != PM_INDOM_NULL)
+ printf("smallest value\n");
+ else
+ printf("smallest and only value (output instance %d)\n", mp->one_inst);
+ break;
+ case OUTPUT_MAX:
+ if (mp->old_desc.indom != PM_INDOM_NULL)
+ printf("largest value\n");
+ else
+ printf("largest and only value (output instance %d)\n", mp->one_inst);
+ break;
+ case OUTPUT_SUM:
+ if (mp->old_desc.indom != PM_INDOM_NULL)
+ printf("sum value (output instance %d)\n", mp->one_inst);
+ else
+ printf("sum and only value (output instance %d)\n", mp->one_inst);
+ break;
+ case OUTPUT_AVG:
+ if (mp->old_desc.indom != PM_INDOM_NULL)
+ printf("average value (output instance %d)\n", mp->one_inst);
+ else
+ printf("average and only value (output instance %d)\n", mp->one_inst);
+ break;
+ }
+ }
+ }
+ if (mp->ip != NULL)
+ printf("Inst Changes:\t<- InDom %s\n", pmInDomStr(mp->ip->old_indom));
+ if (mp->flags & METRIC_CHANGE_SEM) {
+ printf("Semantics:\t%s ->", SemStr(mp->old_desc.sem));
+ printf(" %s\n", SemStr(mp->new_desc.sem));
+ }
+ if (mp->flags & METRIC_CHANGE_UNITS) {
+ printf("Units:\t\t%s ->", pmUnitsStr(&mp->old_desc.units));
+ printf(" %s", pmUnitsStr(&mp->new_desc.units));
+ if (mp->flags & METRIC_RESCALE)
+ printf(" (rescale)");
+ putchar('\n');
+ }
+ if (mp->flags & METRIC_DELETE)
+ printf("DELETE\n");
+ }
+ if (change == 0)
+ printf("No changes\n");
+}
+
+static int
+anychange(void)
+{
+ indomspec_t *ip;
+ metricspec_t *mp;
+ int i;
+
+ if (global.flags != 0)
+ return 1;
+ for (ip = indom_root; ip != NULL; ip = ip->i_next) {
+ if (ip->new_indom != ip->old_indom)
+ return 1;
+ for (i = 0; i < ip->numinst; i++) {
+ if (ip->inst_flags[i])
+ return 1;
+ }
+ }
+ for (mp = metric_root; mp != NULL; mp = mp->m_next) {
+ if (mp->flags != 0 || mp->ip != NULL)
+ return 1;
+ }
+
+ return 0;
+}
+
+static int
+fixstamp(struct timeval *tvp)
+{
+ if (global.flags & GLOBAL_CHANGE_TIME) {
+ if (global.time.tv_sec > 0) {
+ tvp->tv_sec += global.time.tv_sec;
+ tvp->tv_usec += global.time.tv_usec;
+ if (tvp->tv_usec > 1000000) {
+ tvp->tv_sec++;
+ tvp->tv_usec -= 1000000;
+ }
+ return 1;
+ }
+ else if (global.time.tv_sec < 0) {
+ /* parser makes tv_sec < 0 and tv_usec >= 0 */
+ tvp->tv_sec += global.time.tv_sec;
+ tvp->tv_usec -= global.time.tv_usec;
+ if (tvp->tv_usec < 0) {
+ tvp->tv_sec--;
+ tvp->tv_usec += 1000000;
+ }
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/*
+ * Link metricspec_t entries to corresponding indom_t entry if there
+ * are changes to instance identifiers or instance names (includes
+ * instance deletion)
+ */
+static void
+link_entries(void)
+{
+ indomspec_t *ip;
+ metricspec_t *mp;
+ __pmHashCtl *hcp;
+ __pmHashNode *node;
+ int i;
+ int change;
+
+ hcp = &inarch.ctxp->c_archctl->ac_log->l_hashpmid;
+ for (ip = indom_root; ip != NULL; ip = ip->i_next) {
+ change = 0;
+ for (i = 0; i < ip->numinst; i++)
+ change |= (ip->inst_flags[i] != 0);
+ if (change == 0 && ip->new_indom == ip->old_indom)
+ continue;
+
+ for (node = __pmHashWalk(hcp, PM_HASH_WALK_START);
+ node != NULL;
+ node = __pmHashWalk(hcp, PM_HASH_WALK_NEXT)) {
+ mp = start_metric((pmID)(node->key));
+ if (mp->old_desc.indom == ip->old_indom) {
+ if (change)
+ mp->ip = ip;
+ if (ip->new_indom != ip->old_indom) {
+ if (mp->flags & METRIC_CHANGE_INDOM) {
+ /* indom already changed via metric clause */
+ if (mp->new_desc.indom != ip->new_indom) {
+ char strbuf[80];
+ snprintf(strbuf, sizeof(strbuf), "%s", pmInDomStr(mp->new_desc.indom));
+ snprintf(mess, sizeof(mess), "Conflicting indom change for metric %s (%s from metric clause, %s from indom clause)", mp->old_name, strbuf, pmInDomStr(ip->new_indom));
+ yysemantic(mess);
+ }
+ }
+ else {
+ mp->flags |= METRIC_CHANGE_INDOM;
+ mp->new_desc.indom = ip->new_indom;
+ }
+ }
+ }
+ }
+ }
+}
+
+static void
+check_indoms()
+{
+ /*
+ * For each metric, make sure the output instance domain will be in
+ * the output archive.
+ * Called after link_entries(), so if an input metric is associated
+ * with an instance domain that has any instance rewriting, we're OK.
+ * The case to be checked here is a rewritten metric with an indom
+ * clause and no associated indomspec_t (so no instance domain changes,
+ * but the new indom may not match any indom in the archive.
+ */
+ metricspec_t *mp;
+ indomspec_t *ip;
+ __pmHashCtl *hcp;
+ __pmHashNode *node;
+
+
+ hcp = &inarch.ctxp->c_archctl->ac_log->l_hashindom;
+
+ for (mp = metric_root; mp != NULL; mp = mp->m_next) {
+ if (mp->ip != NULL)
+ /* associated indom has instance changes, we're OK */
+ continue;
+ if ((mp->flags & METRIC_CHANGE_INDOM) && mp->new_desc.indom != PM_INDOM_NULL) {
+ for (node = __pmHashWalk(hcp, PM_HASH_WALK_START);
+ node != NULL;
+ node = __pmHashWalk(hcp, PM_HASH_WALK_NEXT)) {
+ /*
+ * if this indom has an indomspec_t, check that, else
+ * this indom will go to the archive without change
+ */
+ for (ip = indom_root; ip != NULL; ip = ip->i_next) {
+ if (ip->old_indom == mp->old_desc.indom)
+ break;
+ }
+ if (ip == NULL) {
+ if ((pmInDom)(node->key) == mp->new_desc.indom)
+ /* we're OK */
+ break;
+ }
+ else {
+ if (ip->new_indom != ip->old_indom &&
+ ip->new_indom == mp->new_desc.indom)
+ /* we're OK */
+ break;
+ }
+ }
+ if (node == NULL) {
+ snprintf(mess, sizeof(mess), "New indom (%s) for metric %s is not in the output archive", pmInDomStr(mp->new_desc.indom), mp->old_name);
+ yysemantic(mess);
+ }
+ }
+ }
+
+ /*
+ * For each modified instance domain, make sure instances are
+ * still unique and instance names are unique to the first
+ * space.
+ */
+ for (ip = indom_root; ip != NULL; ip = ip->i_next) {
+ int i;
+ for (i = 0; i < ip->numinst; i++) {
+ int insti;
+ char *namei;
+ int j;
+ if (ip->inst_flags[i] & INST_CHANGE_INST)
+ insti = ip->new_inst[i];
+ else
+ insti = ip->old_inst[i];
+ if (ip->inst_flags[i] & INST_CHANGE_INAME)
+ namei = ip->new_iname[i];
+ else
+ namei = ip->old_iname[i];
+ for (j = 0; j < ip->numinst; j++) {
+ int instj;
+ char *namej;
+ if (i == j)
+ continue;
+ if (ip->inst_flags[j] & INST_CHANGE_INST)
+ instj = ip->new_inst[j];
+ else
+ instj = ip->old_inst[j];
+ if (ip->inst_flags[j] & INST_CHANGE_INAME)
+ namej = ip->new_iname[j];
+ else
+ namej = ip->old_iname[j];
+ if (insti == instj) {
+ snprintf(mess, sizeof(mess), "Duplicate instance id %d (\"%s\" and \"%s\") for indom %s", insti, namei, namej, pmInDomStr(ip->old_indom));
+ yysemantic(mess);
+ }
+ if (inst_name_eq(namei, namej) > 0) {
+ snprintf(mess, sizeof(mess), "Duplicate instance name \"%s\" (%d) and \"%s\" (%d) for indom %s", namei, insti, namej, instj, pmInDomStr(ip->old_indom));
+ yysemantic(mess);
+ }
+ }
+ }
+ }
+}
+
+static void
+check_output()
+{
+ /*
+ * For each metric, if there is an INDOM clause, perform some
+ * additional semantic checks and perhaps a name -> instance id
+ * mapping.
+ *
+ * Note instance renumbering happens _after_ value selction from
+ * the INDOM -> ,,,, OUTPUT clause, so all references to
+ * instance names and instance ids are relative to the
+ * "old" set.
+ */
+ metricspec_t *mp;
+ indomspec_t *ip;
+
+ for (mp = metric_root; mp != NULL; mp = mp->m_next) {
+ if ((mp->flags & METRIC_CHANGE_INDOM)) {
+ if (mp->output == OUTPUT_ONE || mp->output == OUTPUT_FIRST) {
+ /*
+ * cases here are
+ * INAME "name"
+ * => one_name == "name" and one_inst == PM_IN_NULL
+ * INST id
+ * => one_name == NULL and one_inst = id
+ */
+ if (mp->old_desc.indom != PM_INDOM_NULL && mp->output == OUTPUT_ONE) {
+ /*
+ * old metric is not singular, so one_name and one_inst
+ * are used to pick the value
+ * also map one_name -> one_inst
+ */
+ int i;
+ ip = start_indom(mp->old_desc.indom);
+ for (i = 0; i < ip->numinst; i++) {
+ if (mp->one_name != NULL) {
+ if (inst_name_eq(ip->old_iname[i], mp->one_name) > 0) {
+ mp->one_name = NULL;
+ mp->one_inst = ip->old_inst[i];
+ break;
+ }
+ }
+ else if (ip->old_inst[i] == mp->one_inst)
+ break;
+ }
+ if (i == ip->numinst) {
+ if (wflag) {
+ if (mp->one_name != NULL)
+ snprintf(mess, sizeof(mess), "Instance \"%s\" from OUTPUT clause not found in old indom %s", mp->one_name, pmInDomStr(mp->old_desc.indom));
+ else
+ snprintf(mess, sizeof(mess), "Instance %d from OUTPUT clause not found in old indom %s", mp->one_inst, pmInDomStr(mp->old_desc.indom));
+ yywarn(mess);
+ }
+ }
+ }
+ if (mp->new_desc.indom != PM_INDOM_NULL) {
+ /*
+ * new metric is not singular, so one_inst should be
+ * found in the new instance domain ... ignore one_name
+ * other than to map one_name -> one_inst if one_inst
+ * is not already known
+ */
+ int i;
+ ip = start_indom(mp->new_desc.indom);
+ for (i = 0; i < ip->numinst; i++) {
+ if (mp->one_name != NULL) {
+ if (inst_name_eq(ip->old_iname[i], mp->one_name) > 0) {
+ mp->one_name = NULL;
+ mp->one_inst = ip->old_inst[i];
+ break;
+ }
+ }
+ else if (ip->old_inst[i] == mp->one_inst)
+ break;
+ }
+ if (i == ip->numinst) {
+ if (wflag) {
+ if (mp->one_name != NULL)
+ snprintf(mess, sizeof(mess), "Instance \"%s\" from OUTPUT clause not found in new indom %s", mp->one_name, pmInDomStr(mp->new_desc.indom));
+ else
+ snprintf(mess, sizeof(mess), "Instance %d from OUTPUT clause not found in new indom %s", mp->one_inst, pmInDomStr(mp->new_desc.indom));
+ yywarn(mess);
+ }
+ }
+ /*
+ * use default rule (id 0) if INAME not found and
+ * and instance id is needed for output value
+ */
+ if (mp->old_desc.indom == PM_INDOM_NULL && mp->one_inst == PM_IN_NULL)
+ mp->one_inst = 0;
+ }
+ }
+ }
+ }
+}
+
+int
+main(int argc, char **argv)
+{
+ int sts;
+ int stslog; /* sts from nextlog() */
+ int stsmeta = 0; /* sts from nextmeta() */
+ int i;
+ int ti_idx; /* next slot for input temporal index */
+ int dir_fd = -1; /* poinless initialization to humour gcc */
+ int needti = 0;
+ int doneti = 0;
+ __pmTimeval tstamp = { 0 }; /* for last log record */
+ off_t old_log_offset = 0; /* log offset before last log record */
+ off_t old_meta_offset;
+
+ /* process cmd line args */
+ if (parseargs(argc, argv) < 0) {
+ pmUsageMessage(&opts);
+ exit(1);
+ }
+
+ /* input archive */
+ if (iflag == 0)
+ inarch.name = argv[argc-2];
+ else
+ inarch.name = argv[argc-1];
+ inarch.logrec = inarch.metarec = NULL;
+ inarch.mark = 0;
+ inarch.rp = NULL;
+
+ if ((inarch.ctx = pmNewContext(PM_CONTEXT_ARCHIVE, inarch.name)) < 0) {
+ fprintf(stderr, "%s: Error: cannot open archive \"%s\": %s\n",
+ pmProgname, inarch.name, pmErrStr(inarch.ctx));
+ exit(1);
+ }
+ inarch.ctxp = __pmHandleToPtr(inarch.ctx);
+ assert(inarch.ctxp != NULL);
+
+ if ((sts = pmGetArchiveLabel(&inarch.label)) < 0) {
+ fprintf(stderr, "%s: Error: cannot get archive label record (%s): %s\n",
+ pmProgname, inarch.name, pmErrStr(sts));
+ exit(1);
+ }
+
+ if ((inarch.label.ll_magic & 0xff) != PM_LOG_VERS02) {
+ fprintf(stderr,"%s: Error: illegal version number %d in archive (%s)\n",
+ pmProgname, inarch.label.ll_magic & 0xff, inarch.name);
+ exit(1);
+ }
+
+ /* output archive */
+ if (iflag && Cflag == 0) {
+ /*
+ * -i (in place) method outline
+ *
+ * + create one temporary base filename in the same directory is
+ * the input archive, keep a copy of this name this accessed
+ * via outarch.name
+ * + create a second (and different) temporary base file name
+ * in the same directory, keep this name in bak_base[]
+ * + close the temporary file descriptors and unlink the basename
+ * files
+ * + create the output as per normal in outarch.name
+ * + fsync() all the output files and the container directory
+ * + rename the _input_ archive files using the _second_ temporary
+ * basename
+ * + rename the output archive files to the basename of the input
+ * archive ... if this step fails for any reason, restore the
+ * original input files
+ * + unlink all the (old) input archive files
+ */
+ char path[MAXPATHLEN+1];
+ char dname[MAXPATHLEN+1];
+ mode_t cur_umask;
+ int tmp_f1; /* fd for first temp basename */
+ int tmp_f2; /* fd for second temp basename */
+
+#if HAVE_MKSTEMP
+ strncpy(path, argv[argc-1], sizeof(path));
+ path[sizeof(path)-1] = '\0';
+ strncpy(dname, dirname(path), sizeof(dname));
+ dname[sizeof(dname)-1] = '\0';
+ if ((dir_fd = open(dname, O_RDONLY)) < 0) {
+ fprintf(stderr, "%s: Error: cannot open directory \"%s\" for reading: %s\n", pmProgname, dname, strerror(errno));
+ abandon();
+ /*NOTREACHED*/
+ }
+ sprintf(path, "%s%cXXXXXX", dname, __pmPathSeparator());
+ cur_umask = umask(S_IXUSR | S_IRWXG | S_IRWXO);
+ tmp_f1 = mkstemp(path);
+ umask(cur_umask);
+ outarch.name = strdup(path);
+ if (outarch.name == NULL) {
+ fprintf(stderr, "%s: Error: temp file strdup(%s) failed: %s\n", pmProgname, path, strerror(errno));
+ abandon();
+ /*NOTREACHED*/
+ }
+ sprintf(bak_base, "%s%cXXXXXX", dname, __pmPathSeparator());
+ cur_umask = umask(S_IXUSR | S_IRWXG | S_IRWXO);
+ tmp_f2 = mkstemp(bak_base);
+ umask(cur_umask);
+#else
+ char fname[MAXPATHLEN+1];
+ char *s;
+
+ strncpy(path, argv[argc-1], sizeof(path));
+ path[sizeof(path)-1] = '\0';
+ strncpy(fname, basename(path), sizeof(fname));
+ fname[sizeof(fname)-1] = '\0';
+ strncpy(dname, dirname(path), sizeof(dname));
+ dname[sizeof(dname)-1] = '\0';
+
+ if ((s = tempnam(dname, fname)) == NULL) {
+ fprintf(stderr, "%s: Error: first tempnam() failed: %s\n", pmProgname, strerror(errno));
+ abandon();
+ /*NOTREACHED*/
+ }
+ else {
+ outarch.name = strdup(s);
+ if (outarch.name == NULL) {
+ fprintf(stderr, "%s: Error: temp file strdup(%s) failed: %s\n", pmProgname, s, strerror(errno));
+ abandon();
+ /*NOTREACHED*/
+ }
+ cur_umask = umask(S_IXUSR | S_IRWXG | S_IRWXO);
+ tmp_f1 = open(outarch.name, O_WRONLY|O_CREAT|O_EXCL, 0600);
+ umask(cur_umask);
+ }
+ if ((s = tempnam(dname, fname)) == NULL) {
+ fprintf(stderr, "%s: Error: second tempnam() failed: %s\n", pmProgname, strerror(errno));
+ abandon();
+ /*NOTREACHED*/
+ }
+ else {
+ strcpy(bak_base, s);
+ cur_umask = umask(S_IXUSR | S_IRWXG | S_IRWXO);
+ tmp_f2 = open(bak_base, O_WRONLY|O_CREAT|O_EXCL, 0600);
+ umask(cur_umask);
+ }
+#endif
+ if (tmp_f1 < 0) {
+ fprintf(stderr, "%s: Error: create first temp (%s) failed: %s\n", pmProgname, outarch.name, strerror(errno));
+ abandon();
+ /*NOTREACHED*/
+ }
+ if (tmp_f2 < 0) {
+ fprintf(stderr, "%s: Error: create second temp (%s) failed: %s\n", pmProgname, bak_base, strerror(errno));
+ abandon();
+ /*NOTREACHED*/
+ }
+ close(tmp_f1);
+ close(tmp_f2);
+ unlink(outarch.name);
+ unlink(bak_base);
+ }
+ else
+ outarch.name = argv[argc-1];
+
+ /*
+ * process config file(s)
+ */
+ for (i = 0; i < nconf; i++) {
+ parseconfig(conf[i]);
+ }
+
+ /*
+ * cross-specification dependencies and semantic checks once all
+ * config files have been processed
+ */
+ link_entries();
+ check_indoms();
+ check_output();
+
+ if (vflag)
+ reportconfig();
+
+ if (Cflag)
+ exit(0);
+
+ if (qflag && anychange() == 0)
+ exit(0);
+
+ /* create output log - must be done before writing label */
+ if ((sts = __pmLogCreate("", outarch.name, PM_LOG_VERS02, &outarch.logctl)) < 0) {
+ fprintf(stderr, "%s: Error: __pmLogCreate(%s): %s\n",
+ pmProgname, outarch.name, pmErrStr(sts));
+ abandon();
+ /*NOTREACHED*/
+ }
+
+ /* initialize and write label records */
+ newlabel();
+ outarch.logctl.l_state = PM_LOG_STATE_INIT;
+ writelabel(0);
+
+ first_datarec = 1;
+ ti_idx = 0;
+
+ /*
+ * loop
+ * - get next log record
+ * - write out new/changed meta data required by this log record
+ * - write out log
+ * - do ti update if necessary
+ */
+ while (1) {
+ static long in_offset; /* for -Dappl0 */
+
+ fflush(outarch.logctl.l_mdfp);
+ old_meta_offset = ftell(outarch.logctl.l_mdfp);
+ assert(old_meta_offset >= 0);
+
+ in_offset = ftell(inarch.ctxp->c_archctl->ac_log->l_mfp);
+ stslog = nextlog();
+ if (stslog < 0) {
+#if PCP_DEBUG
+ if (pmDebug & DBG_TRACE_APPL0)
+ fprintf(stderr, "Log: read EOF @ offset=%ld\n", in_offset);
+#endif
+ break;
+ }
+ if (stslog == 1) {
+ /* volume change */
+ if (inarch.ctxp->c_archctl->ac_log->l_curvol >= outarch.logctl.l_curvol+1)
+ /* track input volume numbering */
+ newvolume(inarch.ctxp->c_archctl->ac_log->l_curvol);
+ else
+ /*
+ * output archive volume number is ahead, probably because
+ * rewriting has forced an earlier volume change
+ */
+ newvolume(outarch.logctl.l_curvol+1);
+ }
+#if PCP_DEBUG
+ if (pmDebug & DBG_TRACE_APPL0) {
+ struct timeval stamp;
+ fprintf(stderr, "Log: read ");
+ stamp.tv_sec = inarch.rp->timestamp.tv_sec;
+ stamp.tv_usec = inarch.rp->timestamp.tv_usec;
+ __pmPrintStamp(stderr, &stamp);
+ fprintf(stderr, " numpmid=%d @ offset=%ld\n", inarch.rp->numpmid, in_offset);
+ }
+#endif
+
+ if (ti_idx < inarch.ctxp->c_archctl->ac_log->l_numti) {
+ __pmLogTI *tip = &inarch.ctxp->c_archctl->ac_log->l_ti[ti_idx];
+ if (tip->ti_stamp.tv_sec == inarch.rp->timestamp.tv_sec &&
+ tip->ti_stamp.tv_usec == inarch.rp->timestamp.tv_usec) {
+ /*
+ * timestamp on input pmResult matches next temporal index
+ * entry for input archive ... make sure matching temporal
+ * index entry added to output archive
+ */
+ needti = 1;
+ ti_idx++;
+ }
+ }
+
+ /*
+ * optionally rewrite timestamp in pmResult for global time
+ * adjustment ... flows to output pmResult, indom entries in
+ * metadata, temporal index entries and label records
+ * */
+ fixstamp(&inarch.rp->timestamp);
+
+ /*
+ * process metadata until find an indom record with timestamp
+ * after the current log record, or a metric record for a pmid
+ * that is not in the current log record
+ */
+ for ( ; ; ) {
+ pmID pmid; /* pmid for TYPE_DESC */
+ pmInDom indom; /* indom for TYPE_INDOM */
+
+ if (stsmeta == 0) {
+ in_offset = ftell(inarch.ctxp->c_archctl->ac_log->l_mdfp);
+ stsmeta = nextmeta();
+#if PCP_DEBUG
+ if (stsmeta < 0 && pmDebug & DBG_TRACE_APPL0)
+ fprintf(stderr, "Metadata: read EOF @ offset=%ld\n", in_offset);
+#endif
+ }
+ if (stsmeta < 0) {
+ break;
+ }
+ if (stsmeta == TYPE_DESC) {
+ int i;
+ pmid = ntoh_pmID(inarch.metarec[2]);
+#if PCP_DEBUG
+ if (pmDebug & DBG_TRACE_APPL0)
+ fprintf(stderr, "Metadata: read PMID %s @ offset=%ld\n", pmIDStr(pmid), in_offset);
+#endif
+ /*
+ * if pmid not in next pmResult, we're done ...
+ */
+ for (i = 0; i < inarch.rp->numpmid; i++) {
+ if (pmid == inarch.rp->vset[i]->pmid)
+ break;
+ }
+ if (i == inarch.rp->numpmid)
+ break;
+ /*
+ * rewrite if needed, delete if needed else output
+ */
+ do_desc();
+ }
+ else if (stsmeta == TYPE_INDOM) {
+ struct timeval stamp;
+ __pmTimeval *tvp = (__pmTimeval *)&inarch.metarec[2];
+ indom = ntoh_pmInDom((unsigned int)inarch.metarec[4]);
+#if PCP_DEBUG
+ if (pmDebug & DBG_TRACE_APPL0)
+ fprintf(stderr, "Metadata: read InDom %s @ offset=%ld\n", pmInDomStr(indom), in_offset);
+#endif
+ stamp.tv_sec = ntohl(tvp->tv_sec);
+ stamp.tv_usec = ntohl(tvp->tv_usec);
+ if (fixstamp(&stamp)) {
+ /* global time adjustment specified */
+ tvp->tv_sec = htonl(stamp.tv_sec);
+ tvp->tv_usec = htonl(stamp.tv_usec);
+ }
+ /* if time of indom > next pmResult stop processing metadata */
+ if (stamp.tv_sec > inarch.rp->timestamp.tv_sec)
+ break;
+ if (stamp.tv_sec == inarch.rp->timestamp.tv_sec &&
+ stamp.tv_usec > inarch.rp->timestamp.tv_usec)
+ break;
+ needti = 1;
+ do_indom();
+ }
+ else {
+ fprintf(stderr, "%s: Error: unrecognised meta data type: %d\n",
+ pmProgname, stsmeta);
+ abandon();
+ /*NOTREACHED*/
+ }
+ free(inarch.metarec);
+ stsmeta = 0;
+ }
+
+ if (first_datarec) {
+ first_datarec = 0;
+ /* any global time adjustment done after nextlog() above */
+ outarch.logctl.l_label.ill_start.tv_sec = inarch.rp->timestamp.tv_sec;
+ outarch.logctl.l_label.ill_start.tv_usec = inarch.rp->timestamp.tv_usec;
+ /* need to fix start-time in label records */
+ writelabel(1);
+ needti = 1;
+ }
+
+ tstamp.tv_sec = inarch.rp->timestamp.tv_sec;
+ tstamp.tv_usec = inarch.rp->timestamp.tv_usec;
+
+ if (needti) {
+ fflush(outarch.logctl.l_mdfp);
+ fflush(outarch.logctl.l_mfp);
+ new_meta_offset = ftell(outarch.logctl.l_mdfp);
+ assert(new_meta_offset >= 0);
+ fseek(outarch.logctl.l_mdfp, (long)old_meta_offset, SEEK_SET);
+ __pmLogPutIndex(&outarch.logctl, &tstamp);
+ fseek(outarch.logctl.l_mdfp, (long)new_meta_offset, SEEK_SET);
+ needti = 0;
+ doneti = 1;
+ }
+ else
+ doneti = 0;
+
+ old_log_offset = ftell(outarch.logctl.l_mfp);
+ assert(old_log_offset >= 0);
+
+ if (inarch.rp->numpmid == 0)
+ /* mark record, need index entry @ next log record */
+ needti = 1;
+
+ do_result();
+ }
+
+ if (!doneti) {
+ /* Final temporal index entry */
+ fflush(outarch.logctl.l_mfp);
+ fseek(outarch.logctl.l_mfp, (long)old_log_offset, SEEK_SET);
+ __pmLogPutIndex(&outarch.logctl, &tstamp);
+ }
+
+ if (iflag) {
+ /*
+ * fsync() to make sure new archive is safe before we start
+ * renaming ...
+ */
+ if (fsync(fileno(outarch.logctl.l_mdfp)) < 0) {
+ fprintf(stderr, "%s: Error: fsync(%d) failed for output metadata file: %s\n",
+ pmProgname, fileno(outarch.logctl.l_mdfp), strerror(errno));
+ abandon();
+ /*NOTREACHED*/
+ }
+ if (fsync(fileno(outarch.logctl.l_mfp)) < 0) {
+ fprintf(stderr, "%s: Error: fsync(%d) failed for output data file: %s\n",
+ pmProgname, fileno(outarch.logctl.l_mfp), strerror(errno));
+ abandon();
+ /*NOTREACHED*/
+ }
+ if (fsync(fileno(outarch.logctl.l_tifp)) < 0) {
+ fprintf(stderr, "%s: Error: fsync(%d) failed for output index file: %s\n",
+ pmProgname, fileno(outarch.logctl.l_tifp), strerror(errno));
+ abandon();
+ /*NOTREACHED*/
+ }
+ if (fsync(dir_fd) < 0) {
+ fprintf(stderr, "%s: Error: fsync(%d) failed for output directory: %s\n",
+ pmProgname, dir_fd, strerror(errno));
+ abandon();
+ /*NOTREACHED*/
+ }
+ close(dir_fd);
+ if (_pmLogRename(inarch.name, bak_base) < 0) {
+ abandon();
+ /*NOTREACHED*/
+ }
+ if (_pmLogRename(outarch.name, inarch.name) < 0) {
+ abandon();
+ /*NOTREACHED*/
+ }
+ _pmLogRemove(bak_base);
+ }
+
+ exit(0);
+}
+
+void
+abandon(void)
+{
+ char path[MAXNAMELEN+1];
+ if (dflag == 0) {
+ if (Cflag == 0 && iflag == 0)
+ fprintf(stderr, "Archive \"%s\" not created.\n", outarch.name);
+
+ _pmLogRemove(outarch.name);
+ if (iflag)
+ _pmLogRename(bak_base, inarch.name);
+ while (outarch.logctl.l_curvol >= 0) {
+ snprintf(path, sizeof(path), "%s.%d", outarch.name, outarch.logctl.l_curvol);
+ unlink(path);
+ outarch.logctl.l_curvol--;
+ }
+ snprintf(path, sizeof(path), "%s.meta", outarch.name);
+ unlink(path);
+ snprintf(path, sizeof(path), "%s.index", outarch.name);
+ unlink(path);
+ }
+ else
+ fprintf(stderr, "Archive \"%s\" creation truncated.\n", outarch.name);
+
+ exit(1);
+}
diff --git a/src/pmlogrewrite/result.c b/src/pmlogrewrite/result.c
new file mode 100644
index 0000000..715b9d5
--- /dev/null
+++ b/src/pmlogrewrite/result.c
@@ -0,0 +1,730 @@
+/*
+ * pmResult rewrite methods for pmlogrewrite
+ *
+ * Copyright (c) 2011 Ken McDonell. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * pmResult rewriting is complicated because ...
+ *
+ * - deleting a metric involves moving all of the following rp->vset[]
+ * entries "up" one position and decrementing rp->numpmid
+ * - deleting an instance involves moving all of the following
+ * rp->vset[i]->vlist[] enties "up" one position and then decrementing
+ * rp->vset[i]->numval
+ * - rescaling values involves calling __pmStuffValue which modifies
+ * rp->vset[i]->vlist[j].value, and in the case of all types other than
+ * U32 or 32 this will involve an allocation for a new pmValueBlock
+ * ... we need to keep track of the previous pmValueBlock (if any) to
+ * avoid memory leaks
+ * - changing type has the same implications as rescaling
+ * - a single metric within a pmResult may have both rescaling and type
+ * change
+ * - the initial pmResult contains pointers into a PDU buffer so the fast
+ * track case in pmFreeresult() may not release any of the pmValueSet
+ * or pmValue or pmValueBlock allocations if pmFreeResult is given a
+ * rewritten pmResult, so we modify the pmResult in place but use save[]
+ * to remember the original pmValueSet when retyping or rescaling, use
+ * orig_numval[] to remember how many pmValue instances we had had for
+ * each pmValueSet, and use orig_numpmid to remember the original numpmid
+ * value
+ */
+
+#include "pmapi.h"
+#include "impl.h"
+#include "logger.h"
+#include <assert.h>
+
+/*
+ * Keep track of pmValueSets that have been moved aside to allow for
+ * new values from __pmStuffValue() during rewriting.
+ */
+static pmValueSet **save = NULL;
+static int len_save = 0;
+
+/*
+ * Save rp->vset[idx] in save[idx], and build a new rp->vset[idx]
+ * for the number of values expected for this metric
+ */
+static int
+save_vset(pmResult *rp, int idx)
+{
+ pmValueSet *vsp;
+ int need;
+ int j;
+
+ if (save[idx] != NULL)
+ /* already done */
+ return 1;
+
+ vsp = save[idx] = rp->vset[idx];
+
+ if (vsp->numval > 0)
+ need = sizeof(pmValueSet) + (vsp->numval-1)*sizeof(pmValue);
+ else
+ need = sizeof(pmValueSet);
+ rp->vset[idx] = (pmValueSet *)malloc(need);
+ if (rp->vset[idx] == NULL) {
+ fprintf(stderr, "save_vset: malloc(%d) failed: %s\n", need, strerror(errno));
+ abandon();
+ /*NOTREACHED*/
+ }
+ rp->vset[idx]->pmid = vsp->pmid;
+ rp->vset[idx]->numval = vsp->numval;
+ rp->vset[idx]->valfmt = vsp->valfmt;
+ for (j = 0; j < vsp->numval; j++)
+ rp->vset[idx]->vlist[j].inst = vsp->vlist[j].inst;
+#ifdef PCP_DEBUG
+ if (pmDebug & DBG_TRACE_APPL2) {
+ fprintf(stderr, "save_vset: vset[%d] -> " PRINTF_P_PFX "%p (was " PRINTF_P_PFX "%p) pmid=%s numval=%d\n",
+ idx, rp->vset[idx], save[idx], pmIDStr(rp->vset[idx]->pmid), rp->vset[idx]->numval);
+ }
+#endif
+ return 0;
+}
+
+/*
+ * free the pval for the jth instance of the ith metric
+ */
+static void
+free_pval(pmResult *rp, int i, int j)
+{
+#ifdef PCP_DEBUG
+ if (pmDebug & DBG_TRACE_APPL2) {
+ fprintf(stderr, "free_pval: free(" PRINTF_P_PFX "%p) pmid=%s inst=%d\n",
+ rp->vset[i]->vlist[j].value.pval, pmIDStr(rp->vset[i]->pmid), rp->vset[i]->vlist[j].inst);
+ }
+#endif
+ free(rp->vset[i]->vlist[j].value.pval);
+}
+
+/*
+ * if a pmValueSet was saved via save_vset(), then free the newly build
+ * pmValueSet and put the old one back in place
+ */
+static void
+clean_vset(pmResult *rp)
+{
+ int i;
+ int j;
+
+ for (i = 0; i < rp->numpmid; i++) {
+ if (save[i] != NULL) {
+ if (rp->vset[i]->valfmt == PM_VAL_DPTR) {
+ /* values hanging off the pval */
+ for (j = 0; j < rp->vset[i]->numval; j++)
+ free_pval(rp, i, j);
+ }
+ /*
+ * we did the vset[i] allocation in save_vset(), so malloc()
+ */
+#ifdef PCP_DEBUG
+ if (pmDebug & DBG_TRACE_APPL2) {
+ fprintf(stderr, "clean_vset: free(" PRINTF_P_PFX "%p) pmValueSet pmid=%s\n",
+ rp->vset[i], pmIDStr(rp->vset[i]->pmid));
+ }
+#endif
+ free(rp->vset[i]);
+ rp->vset[i] = save[i];
+ save[i] = NULL;
+ }
+ }
+}
+
+/*
+ * pick/calculate one value from multiple values for the ith vset[] ...
+ *
+ * Note: pval->vbuf may not have correct alignment for all data types,
+ * so memcpy() rather than assign.
+ */
+static int
+pick_val(int i, metricspec_t *mp)
+{
+ int j;
+ int pick = -1;
+ pmAtomValue jval;
+ pmAtomValue pickval;
+
+ assert(inarch.rp->vset[i]->numval > 0);
+
+ for (j = 0; j < inarch.rp->vset[i]->numval; j++) {
+ if (mp->output == OUTPUT_ONE) {
+ if (inarch.rp->vset[i]->vlist[j].inst == mp->one_inst) {
+ pick = j;
+ break;
+ }
+ continue;
+ }
+ if (j == 0) {
+ pick = 0;
+ switch (mp->old_desc.type) {
+ case PM_TYPE_64:
+ memcpy(&pickval.ll, &inarch.rp->vset[i]->vlist[0].value.pval->vbuf, sizeof(__int64_t));
+ break;
+ case PM_TYPE_U64:
+ memcpy(&pickval.ull, &inarch.rp->vset[i]->vlist[0].value.pval->vbuf, sizeof(__uint64_t));
+ break;
+ case PM_TYPE_FLOAT:
+ memcpy(&pickval.f, &inarch.rp->vset[i]->vlist[0].value.pval->vbuf, sizeof(float));
+ break;
+ case PM_TYPE_DOUBLE:
+ memcpy(&pickval.d, &inarch.rp->vset[i]->vlist[0].value.pval->vbuf, sizeof(double));
+ break;
+ }
+ if (mp->output == OUTPUT_MIN || mp->output == OUTPUT_MAX ||
+ mp->output == OUTPUT_SUM || mp->output == OUTPUT_AVG)
+ continue;
+ }
+ switch (mp->old_desc.type) {
+ case PM_TYPE_32:
+ switch (mp->output) {
+ case OUTPUT_MIN:
+ if (inarch.rp->vset[i]->vlist[j].value.lval < inarch.rp->vset[i]->vlist[pick].value.lval)
+ pick = j;
+ break;
+ case OUTPUT_MAX:
+ if (inarch.rp->vset[i]->vlist[j].value.lval > inarch.rp->vset[i]->vlist[pick].value.lval)
+ pick = j;
+ break;
+ case OUTPUT_SUM:
+ case OUTPUT_AVG:
+ inarch.rp->vset[i]->vlist[0].value.lval += inarch.rp->vset[i]->vlist[j].value.lval;
+ break;
+ }
+ break;
+ case PM_TYPE_U32:
+ switch (mp->output) {
+ case OUTPUT_MIN:
+ if ((__uint32_t)inarch.rp->vset[i]->vlist[j].value.lval < (__uint32_t)inarch.rp->vset[i]->vlist[pick].value.lval)
+ pick = j;
+ break;
+ case OUTPUT_MAX:
+ if ((__uint32_t)inarch.rp->vset[i]->vlist[j].value.lval > (__uint32_t)inarch.rp->vset[i]->vlist[pick].value.lval)
+ pick = j;
+ break;
+ case OUTPUT_SUM:
+ case OUTPUT_AVG:
+ *(__uint32_t *)&inarch.rp->vset[i]->vlist[0].value.lval += (__uint32_t)inarch.rp->vset[i]->vlist[j].value.lval;
+ break;
+ }
+ break;
+ case PM_TYPE_64:
+ memcpy(&jval.ll, &inarch.rp->vset[i]->vlist[j].value.pval->vbuf, sizeof(__int64_t));
+ switch (mp->output) {
+ case OUTPUT_MIN:
+ if (jval.ll < pickval.ll) {
+ pickval.ll = jval.ll;
+ pick = j;
+ }
+ break;
+ case OUTPUT_MAX:
+ if (jval.ll > pickval.ll) {
+ pickval.ll = jval.ll;
+ pick = j;
+ }
+ break;
+ case OUTPUT_SUM:
+ case OUTPUT_AVG:
+ pickval.ll += jval.ll;
+ break;
+ }
+ break;
+ case PM_TYPE_U64:
+ memcpy(&jval.ull, &inarch.rp->vset[i]->vlist[j].value.pval->vbuf, sizeof(__int64_t));
+ switch (mp->output) {
+ case OUTPUT_MIN:
+ if (jval.ull < pickval.ull) {
+ pickval.ull = jval.ull;
+ pick = j;
+ }
+ break;
+ case OUTPUT_MAX:
+ if (jval.ull > pickval.ull) {
+ pickval.ull = jval.ull;
+ pick = j;
+ }
+ break;
+ case OUTPUT_SUM:
+ case OUTPUT_AVG:
+ pickval.ull += jval.ull;
+ break;
+ }
+ break;
+ case PM_TYPE_FLOAT:
+ memcpy(&jval.f, &inarch.rp->vset[i]->vlist[j].value.pval->vbuf, sizeof(float));
+ switch (mp->output) {
+ case OUTPUT_MIN:
+ if (jval.f < pickval.f) {
+ pickval.f = jval.f;
+ pick = j;
+ }
+ break;
+ case OUTPUT_MAX:
+ if (jval.f > pickval.f) {
+ pickval.f = jval.f;
+ pick = j;
+ }
+ break;
+ case OUTPUT_SUM:
+ case OUTPUT_AVG:
+ pickval.f += jval.f;
+ break;
+ }
+ break;
+ case PM_TYPE_DOUBLE:
+ memcpy(&jval.d, &inarch.rp->vset[i]->vlist[j].value.pval->vbuf, sizeof(double));
+ switch (mp->output) {
+ case OUTPUT_MIN:
+ if (jval.d < pickval.d) {
+ pickval.d = jval.d;
+ pick = j;
+ }
+ break;
+ case OUTPUT_MAX:
+ if (jval.d > pickval.d) {
+ pickval.d = jval.d;
+ pick = j;
+ }
+ break;
+ case OUTPUT_SUM:
+ case OUTPUT_AVG:
+ pickval.d += jval.d;
+ break;
+ }
+ break;
+ }
+ }
+
+ if (mp->output == OUTPUT_AVG) {
+ switch (mp->old_desc.type) {
+ case PM_TYPE_32:
+ inarch.rp->vset[i]->vlist[0].value.lval = (int)(0.5 + inarch.rp->vset[i]->vlist[0].value.lval / (double)inarch.rp->vset[i]->numval);
+ break;
+ case PM_TYPE_U32:
+ *(__uint32_t *)&inarch.rp->vset[i]->vlist[0].value.lval = (__uint32_t)(0.5 + *(__uint32_t *)&inarch.rp->vset[i]->vlist[0].value.lval / (double)inarch.rp->vset[i]->numval);
+ break;
+ case PM_TYPE_64:
+ pickval.ll = 0.5 + pickval.ll / (double)inarch.rp->vset[i]->numval;
+ break;
+ case PM_TYPE_U64:
+ pickval.ull = 0.5 + pickval.ull / (double)inarch.rp->vset[i]->numval;
+ break;
+ case PM_TYPE_FLOAT:
+ pickval.f = pickval.f / (float)inarch.rp->vset[i]->numval;
+ break;
+ case PM_TYPE_DOUBLE:
+ pickval.d = pickval.d / (double)inarch.rp->vset[i]->numval;
+ break;
+ }
+ }
+ if (mp->output == OUTPUT_AVG || mp->output == OUTPUT_SUM) {
+ switch (mp->old_desc.type) {
+ case PM_TYPE_64:
+ memcpy(&inarch.rp->vset[i]->vlist[0].value.pval->vbuf, &pickval.ll, sizeof(__int64_t));
+ break;
+ case PM_TYPE_U64:
+ memcpy(&inarch.rp->vset[i]->vlist[0].value.pval->vbuf, &pickval.ull, sizeof(__uint64_t));
+ break;
+ case PM_TYPE_FLOAT:
+ memcpy(&inarch.rp->vset[i]->vlist[0].value.pval->vbuf, &pickval.f, sizeof(float));
+ break;
+ case PM_TYPE_DOUBLE:
+ memcpy(&inarch.rp->vset[i]->vlist[0].value.pval->vbuf, &pickval.d, sizeof(double));
+ break;
+ }
+ }
+
+ return pick;
+}
+
+/*
+ * rescale values for the ith vset[]
+ */
+static void
+rescale(int i, metricspec_t *mp)
+{
+ int sts;
+ int j;
+ pmAtomValue ival;
+ pmAtomValue oval;
+ int old_valfmt = inarch.rp->vset[i]->valfmt;
+ int already_saved;
+ pmValueSet *vsp;
+
+ sts = old_valfmt;
+ already_saved = save_vset(inarch.rp, i);
+ if (already_saved)
+ vsp = inarch.rp->vset[i];
+ else
+ vsp = save[i];
+ for (j = 0; j < inarch.rp->vset[i]->numval; j++) {
+ sts = pmExtractValue(old_valfmt, &vsp->vlist[j], mp->old_desc.type, &ival, mp->old_desc.type);
+ if (sts < 0) {
+ /*
+ * No type conversion here, so error not expected
+ */
+ fprintf(stderr, "%s: Botch: %s (%s): extracting value: %s\n",
+ pmProgname, mp->old_name, pmIDStr(mp->old_desc.pmid), pmErrStr(sts));
+ inarch.rp->vset[i]->numval = j;
+ __pmDumpResult(stderr, inarch.rp);
+ abandon();
+ /*NOTREACHED*/
+ }
+ sts = pmConvScale(mp->old_desc.type, &ival, &mp->old_desc.units, &oval, &mp->new_desc.units);
+ if (sts < 0) {
+ /*
+ * unless the "units" are bad (and the parser is supposed to
+ * make sure this does not happen) we do not expect errors
+ * from pmConvScale()
+ */
+ fprintf(stderr, "%s: Botch: %s (%s): scale conversion from %s",
+ pmProgname, mp->old_name, pmIDStr(mp->old_desc.pmid), pmUnitsStr(&mp->old_desc.units));
+ fprintf(stderr, " to %s failed: %s\n", pmUnitsStr(&mp->new_desc.units), pmErrStr(sts));
+ inarch.rp->vset[i]->numval = j;
+ __pmDumpResult(stderr, inarch.rp);
+ abandon();
+ /*NOTREACHED*/
+ }
+ if (already_saved && old_valfmt == PM_VAL_DPTR) {
+ /*
+ * current value uses pval that is from a previous call to
+ * __pmStuffValue() during rewriting, not a pointer into a
+ * PDU buffer
+ */
+#ifdef PCP_DEBUG
+ if (pmDebug & DBG_TRACE_APPL2) {
+ fprintf(stderr, "rescale free(" PRINTF_P_PFX "%p) pval pmid=%s inst=%d\n",
+ inarch.rp->vset[i]->vlist[j].value.pval,
+ pmIDStr(inarch.rp->vset[i]->pmid),
+ inarch.rp->vset[i]->vlist[j].inst);
+ }
+#endif
+ free(inarch.rp->vset[i]->vlist[j].value.pval);
+ }
+ sts = __pmStuffValue(&oval, &inarch.rp->vset[i]->vlist[j], mp->old_desc.type);
+ if (sts < 0) {
+ /*
+ * unless "type" is bad (which the parser is supposed to
+ * prevent) or malloc() failed, we do not expect errors from
+ * __pmStuffValue()
+ */
+ fprintf(stderr, "%s: Botch: %s (%s): stuffing value %s (type=%s) into rewritten pmResult: %s\n",
+ pmProgname, mp->old_name, pmIDStr(mp->old_desc.pmid), pmAtomStr(&oval, mp->old_desc.type), pmTypeStr(mp->old_desc.type), pmErrStr(sts));
+ inarch.rp->vset[i]->numval = j;
+ __pmDumpResult(stderr, inarch.rp);
+ abandon();
+ /*NOTREACHED*/
+ }
+ }
+ inarch.rp->vset[i]->valfmt = sts;
+}
+
+/*
+ * change type of values for the ith vset[] ... real chance that this will
+ * fail, as some failure modes depend on the sign or size of the data
+ * values found in the pmResult
+ */
+static void
+retype(int i, metricspec_t *mp)
+{
+ int sts;
+ int j;
+ pmAtomValue val;
+ int old_valfmt = inarch.rp->vset[i]->valfmt;
+ int already_saved;
+ pmValueSet *vsp;
+
+ sts = old_valfmt;
+ already_saved = save_vset(inarch.rp, i);
+ if (already_saved)
+ vsp = inarch.rp->vset[i];
+ else
+ vsp = save[i];
+ for (j = 0; j < inarch.rp->vset[i]->numval; j++) {
+ sts = pmExtractValue(old_valfmt, &vsp->vlist[j], mp->old_desc.type, &val, mp->new_desc.type);
+ if (sts < 0) {
+ fprintf(stderr, "%s: Error: %s (%s): extracting value from type %s",
+ pmProgname, mp->old_name, pmIDStr(mp->old_desc.pmid), pmTypeStr(mp->old_desc.type));
+ fprintf(stderr, " to %s: %s\n", pmTypeStr(mp->new_desc.type), pmErrStr(sts));
+ inarch.rp->vset[i]->numval = j;
+ __pmDumpResult(stderr, inarch.rp);
+ abandon();
+ /*NOTREACHED*/
+ }
+ if (already_saved && old_valfmt == PM_VAL_DPTR) {
+ /*
+ * current value uses pval that is from a previous call to
+ * __pmStuffValue() during rewriting, not a pointer into a
+ * PDU buffer
+ */
+#ifdef PCP_DEBUG
+ if (pmDebug & DBG_TRACE_APPL2) {
+ fprintf(stderr, "retype free(" PRINTF_P_PFX "%p) pval pmid=%s inst=%d\n",
+ inarch.rp->vset[i]->vlist[j].value.pval,
+ pmIDStr(inarch.rp->vset[i]->pmid),
+ inarch.rp->vset[i]->vlist[j].inst);
+ }
+#endif
+ free(inarch.rp->vset[i]->vlist[j].value.pval);
+ }
+ sts = __pmStuffValue(&val, &inarch.rp->vset[i]->vlist[j], mp->new_desc.type);
+ if (sts < 0) {
+ /*
+ * unless "type" is bad (which the parser is supposed to
+ * prevent) or malloc() failed, we do not expect errors from
+ * __pmStuffValue()
+ */
+ fprintf(stderr, "%s: Botch: %s (%s): stuffing value %s (type=%s) into rewritten pmResult: %s\n",
+ pmProgname, mp->old_name, pmIDStr(mp->old_desc.pmid), pmAtomStr(&val, mp->new_desc.type), pmTypeStr(mp->new_desc.type), pmErrStr(sts));
+ inarch.rp->vset[i]->numval = j;
+ __pmDumpResult(stderr, inarch.rp);
+ abandon();
+ /*NOTREACHED*/
+ }
+ }
+ inarch.rp->vset[i]->valfmt = sts;
+}
+
+void
+do_result(void)
+{
+ metricspec_t *mp;
+ int i;
+ int j;
+ int sts;
+ int orig_numpmid;
+ int *orig_numval = NULL;
+
+ orig_numpmid = inarch.rp->numpmid;
+
+ if (inarch.rp->numpmid > len_save) {
+ /* expand save[] */
+ save = (pmValueSet **)realloc(save, inarch.rp->numpmid * sizeof(save[0]));
+ if (save == NULL) {
+ fprintf(stderr, "save_vset: save realloc(...,%d) failed: %s\n", (int)(inarch.rp->numpmid * sizeof(save[0])), strerror(errno));
+ abandon();
+ /*NOTREACHED*/
+ }
+ for (i = len_save; i < inarch.rp->numpmid; i++)
+ save[i] = NULL;
+ len_save = inarch.rp->numpmid;
+ }
+ orig_numval = (int *)malloc(orig_numpmid * sizeof(int));
+ if (orig_numval == NULL) {
+ fprintf(stderr, "orig_numval malloc(%d) failed: %s\n", (int)(orig_numpmid * sizeof(int)), strerror(errno));
+ abandon();
+ /*NOTREACHED*/
+ }
+ for (i = 0; i < orig_numpmid; i++)
+ orig_numval[i] = inarch.rp->vset[i]->numval;
+
+ for (i = 0; i < inarch.rp->numpmid; i++) {
+ for (mp = metric_root; mp != NULL; mp = mp->m_next) {
+ if (inarch.rp->vset[i]->pmid != mp->old_desc.pmid)
+ continue;
+ if (mp->flags == 0 && mp->ip == NULL)
+ break;
+ if (mp->flags & METRIC_DELETE) {
+ /* move vset[i] to end of list, shuffle lower ones up */
+ pmValueSet *vsp = inarch.rp->vset[i];
+ pmValueSet *save_vsp = save[i];
+ int save_numval;
+ save_numval = orig_numval[i];
+#if PCP_DEBUG
+ if (pmDebug & DBG_TRACE_APPL1)
+ fprintf(stderr, "Delete: vset[%d] for %s\n", i, pmIDStr(inarch.rp->vset[i]->pmid));
+#endif
+ for (j = i+1; j < inarch.rp->numpmid; j++) {
+ inarch.rp->vset[j-1] = inarch.rp->vset[j];
+ save[j-1] = save[j];
+ orig_numval[j-1] = orig_numval[j];
+ }
+ inarch.rp->vset[j-1] = vsp;
+ save[j-1] = save_vsp;
+ orig_numval[j-1] = save_numval;
+ /* one less metric to write out, process vset[i] again */
+ inarch.rp->numpmid--;
+ i--;
+ break;
+ }
+#if PCP_DEBUG
+ /*
+ * mflags that will not force any pmResult rewrite ...
+ * METRIC_CHANGE_NAME
+ * METRIC_CHANGE_SEM
+ */
+ if (pmDebug & DBG_TRACE_APPL1)
+ fprintf(stderr, "Rewrite: vset[%d] for %s\n", i, pmIDStr(inarch.rp->vset[i]->pmid));
+
+#endif
+ if (mp->flags & METRIC_CHANGE_PMID)
+ inarch.rp->vset[i]->pmid = mp->new_desc.pmid;
+ if ((mp->flags & METRIC_CHANGE_INDOM) && inarch.rp->vset[i]->numval > 0) {
+ if (mp->output != OUTPUT_ALL) {
+ /*
+ * Output only one value ...
+ * Some instance selection to be done for the following
+ * indom cases:
+ * non-NULL -> NULL
+ * pick one input value, singular output
+ * NULL -> non-NULL
+ * only one input value, magic-up instance id for
+ * output value from mp->one_inst
+ * non-NULL -> non-NULL
+ * pick one input value base in mp->one_inst,
+ * copy to output
+ */
+ int pick = 0;
+ switch (mp->output) {
+ case OUTPUT_FIRST:
+ break;
+ case OUTPUT_LAST:
+ pick = inarch.rp->vset[i]->numval-1;
+ break;
+ case OUTPUT_ONE:
+ case OUTPUT_MIN:
+ case OUTPUT_MAX:
+ case OUTPUT_SUM:
+ case OUTPUT_AVG:
+ pick = pick_val(i, mp);
+ break;
+ }
+ if (pick >= 0) {
+ if (pick > 0) {
+ /* swap vlist[0] and vlist[pick] */
+ pmValue save;
+ save = inarch.rp->vset[i]->vlist[0];
+ inarch.rp->vset[i]->vlist[0] = inarch.rp->vset[i]->vlist[pick];
+ inarch.rp->vset[i]->vlist[0].inst = inarch.rp->vset[i]->vlist[pick].inst;
+ inarch.rp->vset[i]->vlist[pick] = save;
+ }
+ if (mp->new_desc.indom == PM_INDOM_NULL)
+ inarch.rp->vset[i]->vlist[0].inst = PM_IN_NULL;
+ else if (mp->old_desc.indom == PM_INDOM_NULL)
+ inarch.rp->vset[i]->vlist[0].inst = mp->one_inst;
+ inarch.rp->vset[i]->numval = 1;
+ }
+ else
+ inarch.rp->vset[i]->numval = 0;
+ }
+ }
+ /*
+ * order below is deliberate ...
+ * - cull/renumber instances if needed
+ * - rescale if needed
+ * - fix type if needed
+ */
+ if (mp->ip != NULL) {
+ /* rewrite/delete instance ids from the indom map */
+ int k;
+ for (k = 0; k < mp->ip->numinst; k++) {
+ if (mp->ip->inst_flags[k] & INST_CHANGE_INST) {
+ for (j = 0; j < inarch.rp->vset[i]->numval; j++) {
+ if (inarch.rp->vset[i]->vlist[j].inst == mp->ip->old_inst[k]) {
+ inarch.rp->vset[i]->vlist[j].inst = mp->ip->new_inst[k];
+ }
+ }
+ }
+ if (mp->ip->inst_flags[k] & INST_DELETE) {
+ for (j = 0; j < inarch.rp->vset[i]->numval; j++) {
+ if (inarch.rp->vset[i]->vlist[j].inst == mp->ip->old_inst[k]) {
+ j++;
+ while (j < inarch.rp->vset[i]->numval) {
+ inarch.rp->vset[i]->vlist[j-1] = inarch.rp->vset[i]->vlist[j];
+ j++;
+ }
+ if (save[i] != NULL &&
+ inarch.rp->vset[i]->valfmt == PM_VAL_DPTR) {
+ /*
+ * messy case ... last instance pval is
+ * from calling __pmStuffValue() in
+ * rewriting not a pointer into the PDU
+ * buffer, so free here because
+ * clean_vset() won't find it
+ */
+ free_pval(inarch.rp, i, j-1);
+ }
+ inarch.rp->vset[i]->numval--;
+ }
+ }
+ }
+ }
+ }
+ if (mp->flags & METRIC_RESCALE) {
+ /*
+ * parser already checked that dimension is unchanged,
+ * scale is different and -s on command line or RESCALE
+ * in UNITS clause of metricspec => rescale values
+ */
+ rescale(i, mp);
+ }
+ if (mp->flags & METRIC_CHANGE_TYPE)
+ retype(i, mp);
+ break;
+ }
+ }
+
+ /*
+ * only output numpmid == 0 case if input was a mark record
+ */
+ if (orig_numpmid == 0 || inarch.rp->numpmid > 0) {
+ unsigned long out_offset;
+ unsigned long peek_offset;
+ peek_offset = ftell(outarch.logctl.l_mfp);
+ sts = __pmEncodeResult(PDU_OVERRIDE2, inarch.rp, &inarch.logrec);
+ if (sts < 0) {
+ fprintf(stderr, "%s: Error: __pmEncodeResult: %s\n",
+ pmProgname, pmErrStr(sts));
+ abandon();
+ /*NOTREACHED*/
+ }
+ peek_offset += ((__pmPDUHdr *)inarch.logrec)->len - sizeof(__pmPDUHdr) + 2*sizeof(int);
+ if (peek_offset > 0x7fffffff) {
+ /*
+ * data file size will exceed 2^31-1 bytes, so force
+ * volume switch
+ */
+ newvolume(outarch.logctl.l_curvol+1);
+ }
+ out_offset = ftell(outarch.logctl.l_mfp);
+ if ((sts = __pmLogPutResult2(&outarch.logctl, inarch.logrec)) < 0) {
+ fprintf(stderr, "%s: Error: __pmLogPutResult2: log data: %s\n",
+ pmProgname, pmErrStr(sts));
+ abandon();
+ /*NOTREACHED*/
+ }
+ /* do not free inarch.logrec ... this is a libpcp PDU buffer */
+#if PCP_DEBUG
+ if (pmDebug & DBG_TRACE_APPL0) {
+ struct timeval stamp;
+ fprintf(stderr, "Log: write ");
+ stamp.tv_sec = inarch.rp->timestamp.tv_sec;
+ stamp.tv_usec = inarch.rp->timestamp.tv_usec;
+ __pmPrintStamp(stderr, &stamp);
+ fprintf(stderr, " numpmid=%d @ offset=%ld\n", inarch.rp->numpmid, out_offset);
+ }
+#endif
+ }
+
+ /* restore numpmid up so all vset[]s are freed */
+ inarch.rp->numpmid = orig_numpmid;
+ /*
+ * put pmResult back the way it was (so pmFreeResult works correctly)
+ * and release any allocated memory used in the rewriting
+ */
+ clean_vset(inarch.rp);
+ /* restore numval up so all vlist[]s are freed */
+ for (i = 0; i < orig_numpmid; i++)
+ inarch.rp->vset[i]->numval = orig_numval[i];
+ free(orig_numval);
+
+ pmFreeResult(inarch.rp);
+}
diff --git a/src/pmlogrewrite/util.c b/src/pmlogrewrite/util.c
new file mode 100644
index 0000000..360ac10
--- /dev/null
+++ b/src/pmlogrewrite/util.c
@@ -0,0 +1,302 @@
+/*
+ * Utiility routines for pmlogrewrite
+ *
+ * Copyright (c) 2013 Red Hat.
+ * Copyright (c) 2011 Ken McDonell. All Rights Reserved.
+ * Copyright (c) 1997-2000 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#include "pmapi.h"
+#include "impl.h"
+#include "logger.h"
+#include <assert.h>
+#include <ctype.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+void
+yywarn(char *s)
+{
+ fprintf(stderr, "Warning [%s, line %d]\n%s\n", configfile, lineno, s);
+}
+
+void
+yyerror(char *s)
+{
+ fprintf(stderr, "Specification error in configuration file (%s)\n",
+ configfile);
+ fprintf(stderr, "[line %d] %s\n", lineno, s);
+ exit(1);
+}
+
+void
+yysemantic(char *s)
+{
+ fprintf(stderr, "Semantic error in configuration file (%s)\n",
+ configfile);
+ fprintf(stderr, "%s\n", s);
+ exit(1);
+}
+
+/*
+ * instance name matching ... return
+ * 0 for no match
+ * 1 for match to first space
+ * 2 for complete match
+ * -1 if either name is empty or NULL
+ */
+int
+inst_name_eq(const char *p, const char *q)
+{
+ if (p == NULL || *p == '\0')
+ return -1;
+ if (q == NULL || *q == '\0')
+ return -1;
+
+ for ( ; ; p++, q++) {
+ if (*p == '\0' && *q == '\0')
+ return 2;
+ if (*p == '\0' || *p == ' ') {
+ if (*q == '\0' || *q == ' ')
+ return 1;
+ break;
+ }
+ if (*q == '\0' || *q == ' ') {
+ if (*p == '\0' || *p == ' ')
+ return 1;
+ break;
+ }
+ if (*p != *q)
+ break;
+ }
+ return 0;
+}
+
+/*
+ * Rename all the physical archive files with basename of old to
+ * a basename of new.
+ *
+ * If _any_ error occurs, don't make any changes.
+ *
+ * Note: does not handle compressed versions of files.
+ *
+ * TODO - need global locking for PCP 3.6 version if this is promoted
+ * to libpcp
+ */
+int
+_pmLogRename(const char *old, const char *new)
+{
+ int sts;
+ int nfound = 0;
+ char **found = NULL;
+ char *dname;
+ char *obase;
+ char path[MAXPATHLEN+1];
+ char opath[MAXPATHLEN+1];
+ char npath[MAXPATHLEN+1];
+ DIR *dirp;
+ struct dirent *dp;
+
+ strncpy(path, old, sizeof(path));
+ path[sizeof(path)-1] = '\0';
+ dname = dirname(path);
+
+ if ((dirp = opendir(dname)) == NULL)
+ return -oserror();
+
+ strncpy(path, old, sizeof(path));
+ path[sizeof(path)-1] = '\0';
+ obase = basename(path);
+
+ for ( ; ; ) {
+ setoserror(0);
+ if ((dp = readdir(dirp)) == NULL)
+ break;
+ if (strcmp(dp->d_name, ".") == 0 || strcmp(dp->d_name, "..") == 0)
+ continue;
+ if (strncmp(obase, dp->d_name, strlen(obase)) == 0) {
+ /*
+ * match the base part of the old archive name, now check
+ * for meta or index or a valid volume number
+ */
+ char *p = &dp->d_name[strlen(obase)];
+ int want = 0;
+ if (strcmp(p, ".meta") == 0)
+ want = 1;
+ else if (strcmp(p, ".index") == 0)
+ want = 1;
+ else if (*p == '.' && isdigit((int)p[1])) {
+ char *endp;
+ long vol;
+ vol = strtol(&p[1], &endp, 10);
+ if (vol >= 0 && *endp == '\0')
+ want = 1;
+ }
+ if (want) {
+ struct stat stbuf;
+ snprintf(opath, sizeof(opath), "%s%s", old, p);
+ snprintf(npath, sizeof(npath), "%s%s", new, p);
+ if (stat(npath, &stbuf) == 0) {
+ fprintf(stderr, "__pmLogRename: destination file %s already exists\n", npath);
+ goto revert;
+ }
+ if (rename(opath, npath) == -1) {
+ fprintf(stderr, "__pmLogRename: rename %s -> %s failed: %s\n", opath, npath, pmErrStr(-oserror()));
+ goto revert;
+ }
+ nfound++;
+ found = (char **)realloc(found, nfound*sizeof(found[0]));
+ if (found == NULL) {
+ __pmNoMem("__pmLogRename: realloc", nfound*sizeof(found[0]), PM_RECOV_ERR);
+ abandon();
+ /*NOTREACHED*/
+ }
+ if ((found[nfound-1] = strdup(p)) == NULL) {
+ __pmNoMem("__pmLogRename: strdup", strlen(p)+1, PM_RECOV_ERR);
+ abandon();
+ /*NOTREACHED*/
+ }
+#ifdef PCP_DEBUG
+ if (pmDebug & DBG_TRACE_LOG)
+ fprintf(stderr, "__pmLogRename: %s -> %s\n", opath, npath);
+#endif
+ }
+ }
+ }
+
+ if ((sts = oserror()) != 0) {
+ fprintf(stderr, "__pmLogRename: readdir for %s failed: %s\n", dname, pmErrStr(-sts));
+ goto revert;
+ }
+
+ sts = 0;
+ goto cleanup;
+
+revert:
+ while (nfound > 0) {
+ snprintf(opath, sizeof(opath), "%s%s", old, found[nfound-1]);
+ snprintf(npath, sizeof(npath), "%s%s", new, found[nfound-1]);
+ if (rename(npath, opath) == -1) {
+ fprintf(stderr, "__pmLogRename: arrgh trying to revert rename %s -> %s failed: %s\n", npath, opath, pmErrStr(-oserror()));
+ }
+#ifdef PCP_DEBUG
+ if (pmDebug & DBG_TRACE_LOG)
+ fprintf(stderr, "__pmLogRename: revert %s <- %s\n", opath, npath);
+#endif
+ nfound--;
+ }
+ sts = PM_ERR_GENERIC;
+
+cleanup:
+ closedir(dirp);
+ while (nfound > 0) {
+ free(found[nfound-1]);
+ nfound--;
+ }
+ if (found != NULL)
+ free(found);
+
+ return sts;
+}
+
+/*
+ * Remove all the physical archive files with basename of base.
+ *
+ * Note: does not handle compressed versions of files.
+ *
+ * TODO - need global locking for PCP 3.6 version if this is promoted
+ * to libpcp
+ */
+int
+_pmLogRemove(const char *name)
+{
+ int sts;
+ int nfound = 0;
+ char *dname;
+ char *base;
+ char path[MAXPATHLEN+1];
+ DIR *dirp;
+ struct dirent *dp;
+
+ strncpy(path, name, sizeof(path));
+ path[sizeof(path)-1] = '\0';
+ dname = strdup(dirname(path));
+ if (dname == NULL) {
+ __pmNoMem("__pmLogRemove: dirname strdup", strlen(dirname(path))+1, PM_RECOV_ERR);
+ abandon();
+ /*NOTREACHED*/
+ }
+
+ if ((dirp = opendir(dname)) == NULL) {
+ free(dname);
+ return -oserror();
+ }
+
+ strncpy(path, name, sizeof(path));
+ path[sizeof(path)-1] = '\0';
+ base = strdup(basename(path));
+ if (base == NULL) {
+ __pmNoMem("__pmLogRemove: basename strdup", strlen(basename(path))+1, PM_RECOV_ERR);
+ abandon();
+ /*NOTREACHED*/
+ }
+
+ for ( ; ; ) {
+ setoserror(0);
+ if ((dp = readdir(dirp)) == NULL)
+ break;
+ if (strcmp(dp->d_name, ".") == 0 || strcmp(dp->d_name, "..") == 0)
+ continue;
+ if (strncmp(base, dp->d_name, strlen(base)) == 0) {
+ /*
+ * match the base part of the old archive name, now check
+ * for meta or index or a valid volume number
+ */
+ char *p = &dp->d_name[strlen(base)];
+ int want = 0;
+ if (strcmp(p, ".meta") == 0)
+ want = 1;
+ else if (strcmp(p, ".index") == 0)
+ want = 1;
+ else if (*p == '.' && isdigit((int)p[1])) {
+ char *endp;
+ long vol;
+ vol = strtol(&p[1], &endp, 10);
+ if (vol >= 0 && *endp == '\0')
+ want = 1;
+ }
+ if (want) {
+ snprintf(path, sizeof(path), "%s%s", name, p);
+ unlink(path);
+ nfound++;
+#ifdef PCP_DEBUG
+ if (pmDebug & DBG_TRACE_LOG)
+ fprintf(stderr, "__pmLogRemove: %s\n", path);
+#endif
+ }
+ }
+ }
+
+ if ((sts = oserror()) != 0) {
+ fprintf(stderr, "__pmLogRemove: readdir for %s failed: %s\n", dname, pmErrStr(-sts));
+ sts = -sts;
+ }
+ else
+ sts = nfound;
+
+ closedir(dirp);
+ free(dname);
+ free(base);
+
+ return sts;
+}