summaryrefslogtreecommitdiff
path: root/src/libpcp_pmda
diff options
context:
space:
mode:
Diffstat (limited to 'src/libpcp_pmda')
-rw-r--r--src/libpcp_pmda/GNUlocaldefs.324
-rw-r--r--src/libpcp_pmda/GNUmakefile31
-rw-r--r--src/libpcp_pmda/src/GNUmakefile102
-rw-r--r--src/libpcp_pmda/src/cache.c1543
-rw-r--r--src/libpcp_pmda/src/callback.c753
-rw-r--r--src/libpcp_pmda/src/context.c31
-rw-r--r--src/libpcp_pmda/src/dynamic.c217
-rw-r--r--src/libpcp_pmda/src/events.c391
-rw-r--r--src/libpcp_pmda/src/exports115
-rw-r--r--src/libpcp_pmda/src/help.c217
-rw-r--r--src/libpcp_pmda/src/libdefs.h36
-rw-r--r--src/libpcp_pmda/src/mainloop.c491
-rw-r--r--src/libpcp_pmda/src/open.c987
-rw-r--r--src/libpcp_pmda/src/queues.c610
-rw-r--r--src/libpcp_pmda/src/queues.h172
-rw-r--r--src/libpcp_pmda/src/tree.c313
16 files changed, 6013 insertions, 0 deletions
diff --git a/src/libpcp_pmda/GNUlocaldefs.32 b/src/libpcp_pmda/GNUlocaldefs.32
new file mode 100644
index 0000000..38bc43e
--- /dev/null
+++ b/src/libpcp_pmda/GNUlocaldefs.32
@@ -0,0 +1,4 @@
+LCFLAGS += -m32 -march=i386
+LLDFLAGS += -m32 -march=i386
+PCP_LIB_DIR=$(PCP_LIB32_DIR)
+LIBPCP_ABIDIR=32
diff --git a/src/libpcp_pmda/GNUmakefile b/src/libpcp_pmda/GNUmakefile
new file mode 100644
index 0000000..982d0ad
--- /dev/null
+++ b/src/libpcp_pmda/GNUmakefile
@@ -0,0 +1,31 @@
+#
+# Copyright (c) 2000,2004 Silicon Graphics, Inc. All Rights Reserved.
+#
+# This library is free software; you can redistribute it and/or modify it
+# under the terms of the GNU Lesser General Public License as published
+# by the Free Software Foundation; either version 2.1 of the License, or
+# (at your option) any later version.
+#
+# This library 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 Lesser General Public
+# License for more details.
+#
+
+TOPDIR = ../..
+
+include $(TOPDIR)/src/include/builddefs
+
+LSRCFILES = GNUlocaldefs.32
+
+SUBDIRS = src
+
+default install: $(SUBDIRS)
+ $(SUBDIRS_MAKERULE)
+
+include $(BUILDRULES)
+
+default_pcp : default
+
+install_pcp : install
+
diff --git a/src/libpcp_pmda/src/GNUmakefile b/src/libpcp_pmda/src/GNUmakefile
new file mode 100644
index 0000000..5812b73
--- /dev/null
+++ b/src/libpcp_pmda/src/GNUmakefile
@@ -0,0 +1,102 @@
+#
+# Copyright (c) 2013 Red Hat.
+# Copyright (c) 2009,2011 Aconex. All Rights Reserved.
+# Copyright (c) 2000,2004 Silicon Graphics, Inc. All Rights Reserved.
+#
+# This library is free software; you can redistribute it and/or modify it
+# under the terms of the GNU Lesser General Public License as published
+# by the Free Software Foundation; either version 2.1 of the License, or
+# (at your option) any later version.
+#
+# This library 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 Lesser General Public
+# License for more details.
+#
+
+TOPDIR = ../../..
+include $(TOPDIR)/src/include/builddefs
+
+CFILES = callback.c open.c mainloop.c help.c cache.c tree.c context.c \
+ events.c queues.c dynamic.c
+HFILES = libdefs.h queues.h
+LLDLIBS = -lpcp
+
+STATICLIBTARGET = libpcp_pmda.a
+
+#
+# libpcp_pmda.so -> libpcp_pmda.so.3
+# libpcp_pmda.so.2 -> libpcp_pmda.so.3
+#
+DSOVERSION_V2 = 2
+DSOVERSION_V3 = 3
+VERSION_SCRIPT = exports
+
+ifneq "$(TARGET_OS)" "darwin"
+LIBTARGET_V1 = libpcp_pmda.$(DSOSUFFIX)
+LIBTARGET_V2 = libpcp_pmda.$(DSOSUFFIX).$(DSOVERSION_V2)
+LIBTARGET_V3 = libpcp_pmda.$(DSOSUFFIX).$(DSOVERSION_V3)
+else
+LIBTARGET_V1 = libpcp_pmda.$(DSOSUFFIX)
+LIBTARGET_V2 =
+LIBTARGET_V3 = libpcp_pmda.$(DSOVERSION_V3).$(DSOSUFFIX)
+endif
+ifeq "$(PACKAGE_DISTRIBUTION)" "debian"
+LIBTARGET_V2 =
+endif
+LIBTARGET = $(LIBTARGET_V3)
+
+ifeq "$(TARGET_OS)" "mingw"
+LIBTARGET = libpcp_pmda.$(DSOSUFFIX)
+STATICLIBTARGET =
+LIBTARGET_V1 =
+LIBTARGET_V2 =
+LIBTARGET_V3 =
+endif
+
+ifeq "$(ENABLE_SHARED)" "no"
+LIBTARGET =
+LIBTARGET_V1 =
+LIBTARGET_V2 =
+LIBTARGET_V3 =
+endif
+
+LSRCFILES = $(VERSION_SCRIPT)
+LDIRT = $(LIBTARGET_V1) $(LIBTARGET_V2) $(LIBTARGET_V3)
+
+default: $(LIBTARGET_V1) $(LIBTARGET_V2) $(LIBTARGET) $(STATICLIBTARGET)
+
+ifneq ($(LIBTARGET_V1),)
+$(LIBTARGET_V1): $(LIBTARGET_V3)
+ $(LN_S) -f $(LIBTARGET_V3) $(LIBTARGET_V1)
+endif
+ifneq ($(LIBTARGET_V2),)
+$(LIBTARGET_V2): $(LIBTARGET_V3)
+ $(LN_S) -f $(LIBTARGET_V3) $(LIBTARGET_V2)
+endif
+
+callback.o mainloop.o open.o : libdefs.h
+
+include $(BUILDRULES)
+
+install : default
+ifneq ($(LIBTARGET),)
+ $(INSTALL) -m 755 $(LIBTARGET) $(PCP_LIB_DIR)/$(LIBTARGET)
+endif
+ifneq ($(LIBTARGET_V1),)
+ $(INSTALL) -S $(LIBTARGET_V3) $(PCP_LIB_DIR)/$(LIBTARGET_V1)
+endif
+ifneq ($(LIBTARGET_V2),)
+ $(INSTALL) -S $(LIBTARGET) $(PCP_LIB_DIR)/$(LIBTARGET_V2)
+endif
+ifneq ($(STATICLIBTARGET),)
+ $(INSTALL) -m 755 $(STATICLIBTARGET) $(PCP_LIB_DIR)/$(STATICLIBTARGET)
+endif
+
+default_pcp : default
+
+install_pcp : install
+
+ifneq ($(LIBTARGET),)
+$(LIBTARGET): $(VERSION_SCRIPT)
+endif
diff --git a/src/libpcp_pmda/src/cache.c b/src/libpcp_pmda/src/cache.c
new file mode 100644
index 0000000..dd74b3f
--- /dev/null
+++ b/src/libpcp_pmda/src/cache.c
@@ -0,0 +1,1543 @@
+/*
+ * Copyright (c) 2013 Red Hat.
+ * Copyright (c) 2005 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This library 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 Lesser General Public
+ * License for more details.
+ */
+
+#include "pmapi.h"
+#include "impl.h"
+#include "pmda.h"
+#include <fcntl.h>
+#include <ctype.h>
+#include <sys/stat.h>
+
+static __uint32_t hash(const char *, int, __uint32_t);
+
+/*
+ * simple linked list for each cache at this stage
+ */
+typedef struct entry {
+ struct entry *next; /* in inst identifier order */
+ struct entry *h_inst; /* inst hash chain */
+ struct entry *h_name; /* name hash chain */
+ int inst;
+ char *name;
+ int hashlen; /* smaller of strlen(name) and chars to first space */
+ int keylen; /* > 0 if have key from pmdaCacheStoreKey() */
+ void *key; /* != NULL if have key from pmdaCacheStoreKey() */
+ int state;
+ void *private;
+ time_t stamp;
+} entry_t;
+
+#define VERSION 1 /* version of external file format */
+#define MAX_HASH_TRY 10
+
+/*
+ * linked list of cache headers
+ */
+typedef struct hdr {
+ struct hdr *next; /* linked list of indoms */
+ entry_t *first; /* in inst order */
+ entry_t *last; /* in inst order */
+ entry_t *save; /* used in cache_walk() */
+ entry_t **ctl_inst; /* hash by inst chains */
+ entry_t **ctl_name; /* hash by name chains */
+ pmInDom indom;
+ int hsize;
+ int hbits;
+ int nentry; /* number of entries */
+ int ins_mode; /* see insert_cache() */
+ int hstate; /* dirty/clean/string state */
+ int keyhash_cnt[MAX_HASH_TRY];
+} hdr_t;
+
+/* bitfields for hstate */
+#define DIRTY_INSTANCE 0x1
+#define DIRTY_STAMP 0x2
+#define CACHE_STRINGS 0x4
+
+static hdr_t *base; /* start of cache headers */
+static char filename[MAXPATHLEN];
+ /* for load/save ops */
+static char *vdp; /* first trip mkdir for load/save */
+
+/*
+ * Count character to end of string or first space, whichever comes
+ * first. In the special case of string caches, spaces are allowed.
+ */
+static int
+get_hashlen(hdr_t *h, const char *str)
+{
+ const char *q = str;
+
+ while (*q && (*q != ' ' || (h->hstate & CACHE_STRINGS) != 0))
+ q++;
+ return (int)(q-str);
+}
+
+static unsigned int
+hash_str(const char *str, int len)
+{
+ return hash(str, len, 0);
+}
+
+static void
+KeyStr(FILE *f, int keylen, const char *key)
+{
+ int i;
+ if (keylen > 0) {
+ fprintf(f, "[key=0x");
+ for (i = 0; i < keylen; i++, key++)
+ fprintf(f, "%02x", (*key & 0xff));
+ fputc(']', f);
+ }
+ else
+ fprintf(f, "[no key]");
+}
+
+/*
+ * The magic "match up to a space" instance name matching ...
+ *
+ * e->name e->hashlen name hashlen result
+ * foo... >3 foo 3 0
+ * foo 3 foo... >3 0
+ * foo 3 foo 3 1
+ * foo bar 3 foo 3 1
+ * foo bar 3 foo bar 3 1
+ * foo 3 foo bar 3 -1 bad
+ * foo blah 3 foo bar 3 -1 bad
+ *
+ */
+static int
+name_eq(entry_t *e, const char *name, int hashlen)
+{
+ if (e->hashlen != hashlen)
+ return 0;
+ if (strncmp(e->name, name, hashlen) != 0)
+ return 0;
+ if (name[hashlen] == '\0')
+ return 1;
+ if (e->name[hashlen] == '\0')
+ return -1;
+ if (strcmp(&e->name[hashlen+1], &name[hashlen+1]) == 0)
+ return 1;
+ return -1;
+}
+
+static int
+key_eq(entry_t *e, int keylen, const char *key)
+{
+ const char *ekp;
+ const char *kp;
+ int i;
+
+ if (e->keylen != keylen)
+ return 0;
+
+ ekp = (const char *)e->key;
+ kp = (const char *)key;
+ for (i = 0; i < keylen; i++) {
+ if (*ekp != *kp)
+ return 0;
+ ekp++;
+ kp++;
+ }
+
+ return 1;
+}
+
+static hdr_t *
+find_cache(pmInDom indom, int *sts)
+{
+ hdr_t *h;
+ int i;
+
+ for (h = base; h != NULL; h = h->next) {
+ if (h->indom == indom)
+ return h;
+ }
+
+ if ((h = (hdr_t *)malloc(sizeof(hdr_t))) == NULL) {
+ char strbuf[20];
+ __pmNotifyErr(LOG_ERR,
+ "find_cache: indom %s: unable to allocate memory for hdr_t",
+ pmInDomStr_r(indom, strbuf, sizeof(strbuf)));
+ *sts = PM_ERR_GENERIC;
+ return NULL;
+ }
+ h->next = base;
+ base = h;
+ h->first = NULL;
+ h->last = NULL;
+ h->hsize = 16;
+ h->hbits = 0xf;
+ h->ctl_inst = (entry_t **)calloc(h->hsize, sizeof(entry_t *));
+ h->ctl_name = (entry_t **)calloc(h->hsize, sizeof(entry_t *));
+ h->indom = indom;
+ h->nentry = 0;
+ h->ins_mode = 0;
+ h->hstate = 0;
+ for (i = 0; i < MAX_HASH_TRY; i++)
+ h->keyhash_cnt[i] = 0;
+ return h;
+}
+
+/*
+ * Traverse the cache in ascending inst order
+ */
+static entry_t *
+walk_cache(hdr_t *h, int op)
+{
+ entry_t *e;
+
+ if (op == PMDA_CACHE_WALK_REWIND) {
+ h->save = h->first;
+ return NULL;
+ }
+ e = h->save;
+ if (e != NULL)
+ h->save = e->next;
+ return e;
+}
+
+/*
+ * inst_or_name is 0 for inst hash list, 1 for name hash list
+ */
+static void
+dump_hash_list(FILE *fp, hdr_t *h, int inst_or_name, int i)
+{
+ entry_t *e;
+
+ fprintf(fp, " [%03d]", i);
+ if (inst_or_name == 1) {
+ for (e = h->ctl_name[i]; e != NULL; e = e->h_name) {
+ fprintf(fp, " -> %d", e->inst);
+ if (e->state == PMDA_CACHE_EMPTY)
+ fputc('E', fp);
+ else if (e->state == PMDA_CACHE_INACTIVE)
+ fputc('I', fp);
+ }
+ fputc('\n', fp);
+ }
+ else {
+ for (e = h->ctl_inst[i]; e != NULL; e = e->h_inst) {
+ fprintf(fp, " -> %d", e->inst);
+ if (e->state == PMDA_CACHE_EMPTY)
+ fputc('E', fp);
+ else if (e->state == PMDA_CACHE_INACTIVE)
+ fputc('I', fp);
+ }
+ fputc('\n', fp);
+ }
+}
+
+static void
+dump(FILE *fp, hdr_t *h, int do_hash)
+{
+ entry_t *e;
+ char strbuf[20];
+ int i;
+
+ fprintf(fp, "pmdaCacheDump: indom %s: nentry=%d ins_mode=%d hstate=%d hsize=%d\n",
+ pmInDomStr_r(h->indom, strbuf, sizeof(strbuf)), h->nentry, h->ins_mode, h->hstate, h->hsize);
+ for (e = h->first; e != NULL; e = e->next) {
+ if (e->state == PMDA_CACHE_EMPTY) {
+ fprintf(fp, "(%10d) %8s\n", e->inst, "empty");
+ }
+ else {
+ fprintf(fp, " %10d %8s " PRINTF_P_PFX "%p %s",
+ e->inst, e->state == PMDA_CACHE_ACTIVE ? "active" : "inactive",
+ e->private, e->name);
+ if (strlen(e->name) > e->hashlen)
+ fprintf(fp, " [match len=%d]", e->hashlen);
+ if (e->keylen > 0) {
+ fputc(' ', fp);
+ KeyStr(fp, e->keylen, (const char *)e->key);
+ }
+ fputc('\n', fp);
+ }
+ }
+
+ if (do_hash == 0)
+ return;
+
+ for (i = 0; i < MAX_HASH_TRY; i++) {
+ if (h->keyhash_cnt[i])
+ break;
+ }
+
+ if (i < MAX_HASH_TRY) {
+ fprintf(fp, "pmdaCacheStoreKey hash stats ...\n");
+ for (i = 0; i < MAX_HASH_TRY; i++) {
+ if (h->keyhash_cnt[i] != 0) {
+ if (i == 0)
+ fprintf(fp, "hash once: %d times\n", h->keyhash_cnt[i]);
+ else
+ fprintf(fp, "%d hash attempts: %d times\n", i+1, h->keyhash_cnt[i]);
+ }
+ }
+ }
+
+ if (h->ctl_inst != NULL) {
+ int i;
+ fprintf(fp, "inst hash\n");
+ for (i = 0; i < h->hsize; i++) {
+ dump_hash_list(fp, h, 0, i);
+ }
+ }
+ if (h->ctl_name != NULL) {
+ int i;
+ fprintf(fp, "name hash\n");
+ for (i = 0; i < h->hsize; i++) {
+ dump_hash_list(fp, h, 1, i);
+ }
+ }
+}
+
+static entry_t *
+find_name(hdr_t *h, const char *name, int *sts)
+{
+ entry_t *e;
+ int hashlen = get_hashlen(h, name);
+
+ *sts = 0;
+ for (e = h->first; e != NULL; e = e->next) {
+ if (e->state != PMDA_CACHE_EMPTY) {
+ if ((*sts = name_eq(e, name, hashlen)))
+ break;
+ }
+ }
+ return e;
+}
+
+static entry_t *
+find_inst(hdr_t *h, int inst)
+{
+ entry_t *e;
+
+ for (e = h->first; e != NULL; e = e->next) {
+ if (e->inst == inst && e->state != PMDA_CACHE_EMPTY)
+ break;
+ }
+ return e;
+}
+
+/*
+ * supports find by instance identifier (name == NULL) else
+ * find by instance name
+ */
+static entry_t *
+find_entry(hdr_t *h, const char *name, int inst, int *sts)
+{
+ entry_t *e;
+
+ *sts = 0;
+ if (name == NULL) {
+ /*
+ * search by instance identifier (inst)
+ */
+ if (h->ctl_inst == NULL)
+ /* no hash, use linear search */
+ return find_inst(h, inst);
+ for (e = h->ctl_inst[inst & h->hbits]; e != NULL; e = e->h_inst) {
+ if (e->inst == inst && e->state != PMDA_CACHE_EMPTY)
+ return e;
+ }
+ }
+ else {
+ /*
+ * search by instance name
+ */
+ int hashlen = get_hashlen(h, name);
+
+ if (h->ctl_name == NULL)
+ /* no hash, use linear search */
+ return find_name(h, name, sts);
+ for (e = h->ctl_name[hash_str(name, hashlen) & h->hbits]; e != NULL; e = e->h_name) {
+ if (e->state != PMDA_CACHE_EMPTY) {
+ if ((*sts = name_eq(e, name, hashlen)))
+ return e;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+/*
+ * optionally resize the hash table first (if resize == 1)
+ *
+ * then re-order each hash chain so that active entires are
+ * before inactive entries, and culled entries dropped
+ *
+ * applies to _both_ the inst and name hashes
+ */
+static void
+redo_hash(hdr_t *h, int resize)
+{
+ entry_t *e;
+ entry_t *last_e = NULL;
+ entry_t *t;
+ int i;
+ entry_t *last_active;
+ entry_t *inactive;
+ entry_t *last_inactive;
+
+ if (resize) {
+ entry_t **old_inst;
+ entry_t **old_name;
+ int oldsize;
+ int oldi;
+
+ old_inst = h->ctl_inst;
+ old_name = h->ctl_name;
+ oldsize = h->hsize;
+ h->hsize <<= 1;
+ h->ctl_inst = (entry_t **)calloc(h->hsize, sizeof(entry_t *));
+ if (h->ctl_inst == NULL) {
+ h->ctl_inst = old_inst;
+ h->hsize = oldsize;
+ goto reorder;
+ }
+ h->ctl_name = (entry_t **)calloc(h->hsize, sizeof(entry_t *));
+ if (h->ctl_name == NULL) {
+ free(h->ctl_inst);
+ h->ctl_inst = old_inst;
+ h->ctl_name = old_name;
+ h->hsize = oldsize;
+ goto reorder;
+ }
+ h->hbits = (h->hbits << 1) | 1;
+ for (oldi = 0; oldi < oldsize; oldi++) {
+ for (e = old_inst[oldi]; e != NULL; ) {
+ t = e;
+ e = e->h_inst;
+ i = t->inst & h->hbits;
+ t->h_inst = h->ctl_inst[i];
+ h->ctl_inst[i] = t;
+ }
+ }
+ for (oldi = 0; oldi < oldsize; oldi++) {
+ for (e = old_name[oldi]; e != NULL; ) {
+ t = e;
+ e = e->h_name;
+ i = hash_str(t->name, t->hashlen) & h->hbits;
+ t->h_name = h->ctl_name[i];
+ h->ctl_name[i] = t;
+ }
+ }
+ free(old_inst);
+ free(old_name);
+ }
+reorder:
+
+ /*
+ * first the inst hash list, moving active entries before inactive ones,
+ * and unlinking any empty ones
+ */
+ for (i = 0; i < h->hsize; i++) {
+ last_active = NULL;
+ inactive = NULL;
+ last_inactive = NULL;
+ e = h->ctl_inst[i];
+ h->ctl_inst[i] = NULL;
+ while (e != NULL) {
+ t = e;
+ e = e->h_inst;
+ t->h_inst = NULL;
+ if (t->state == PMDA_CACHE_ACTIVE) {
+ if (last_active == NULL)
+ h->ctl_inst[i] = t;
+ else
+ last_active->h_inst = t;
+ last_active = t;
+ }
+ else if (t->state == PMDA_CACHE_INACTIVE) {
+ if (last_inactive == NULL)
+ inactive = t;
+ else
+ last_inactive->h_inst = t;
+ last_inactive = t;
+ }
+ }
+ if (last_active == NULL)
+ h->ctl_inst[i] = inactive;
+ else
+ last_active->h_inst = inactive;
+ }
+
+ /*
+ * and now the name hash list, doing the same thing
+ */
+ for (i = 0; i < h->hsize; i++) {
+ last_active = NULL;
+ inactive = NULL;
+ last_inactive = NULL;
+ e = h->ctl_name[i];
+ h->ctl_name[i] = NULL;
+ while (e != NULL) {
+ t = e;
+ e = e->h_name;
+ t->h_name = NULL;
+ if (t->state == PMDA_CACHE_ACTIVE) {
+ if (last_active == NULL)
+ h->ctl_name[i] = t;
+ else
+ last_active->h_name = t;
+ last_active = t;
+ }
+ else if (t->state == PMDA_CACHE_INACTIVE) {
+ if (last_inactive == NULL)
+ inactive = t;
+ else
+ last_inactive->h_name = t;
+ last_inactive = t;
+ }
+ }
+ if (last_active == NULL)
+ h->ctl_name[i] = inactive;
+ else
+ last_active->h_name = inactive;
+ }
+
+ /*
+ * now walk the instance list, removing any culled entries and
+ * rebuilding the linked list
+ */
+ e = h->first;
+ while (e != NULL) {
+ t = e;
+ e = e->next;
+ if (t->state == PMDA_CACHE_EMPTY) {
+ if (last_e == NULL)
+ h->first = e;
+ else
+ last_e->next = e;
+ if (t->name)
+ free(t->name);
+ free(t);
+ }
+ else
+ last_e = t;
+ }
+
+}
+
+/*
+ * We need to keep the instances in ascending inst order.
+ * If inst _is_ PM_IN_NULL, then we need to choose a value ...
+ * The default mode is appending to use the last value+1 (this is
+ * ins_mode == 0). If we wrap the instance identifier range, or
+ * PMDA_CACHE_REUSE has been used, then ins_mode == 1 and we walk
+ * the list starting from the beginning, looking for the first
+ * unused inst value.
+ *
+ * If inst is _not_ PM_IN_NULL, we're being called from load_cache
+ * or pmdaCacheStoreKey() and the inst is known ... so we need to
+ * check for possible duplicate entries.
+ */
+static entry_t *
+insert_cache(hdr_t *h, const char *name, int inst, int *sts)
+{
+ entry_t *e;
+ entry_t *last_e = NULL;
+ char *dup;
+ int i;
+ int hashlen = get_hashlen(h, name);
+
+ *sts = 0;
+
+ if (inst != PM_IN_NULL) {
+ /*
+ * Check if instance id or instance name already in cache
+ * ... if id and name are the the same, keep the existing one
+ * and ignore the new one (in particular state is not reset to
+ * inactive).
+ * If one matches but the other is different, keep the
+ * matching entry, but return an error as a warning.
+ * If both fail to match, we're OK to insert the new entry,
+ * although we need to run down the list quickly to find the
+ * correct place to insert the new one.
+ */
+ e = find_entry(h, NULL, inst, sts);
+ if (e != NULL) {
+ if (name_eq(e, name, hashlen) != 1) {
+ /* instance id the same, different name */
+#ifdef PCP_DEBUG
+ if (pmDebug & DBG_TRACE_INDOM) {
+ fprintf(stderr, "pmdaCache: store: indom %s: instance %d ", pmInDomStr(h->indom), e->inst);
+ fprintf(stderr, " in cache, name \"%s\" does not match new entry \"%s\"\n", e->name, name);
+ }
+#endif
+ *sts = PM_ERR_INST;
+ }
+ return e;
+ }
+ e = find_entry(h, name, PM_IN_NULL, sts);
+ if (e != NULL) {
+#ifdef PCP_DEBUG
+ if (pmDebug & DBG_TRACE_INDOM) {
+ fprintf(stderr, "pmdaCacheStoreKey: indom %s: instance \"%s\"", pmInDomStr(h->indom), e->name);
+ fprintf(stderr, " in cache, id %d does not match new entry %d\n", e->inst, inst);
+ }
+#endif
+ *sts = PM_ERR_INST;
+ return e;
+ }
+ for (e = h->first; e != NULL; e = e->next) {
+ if (e->inst < inst)
+ last_e = e;
+ else if (e->inst > inst)
+ break;
+ }
+ }
+
+ if ((dup = strdup(name)) == NULL) {
+ char strbuf[20];
+ __pmNotifyErr(LOG_ERR,
+ "insert_cache: indom %s: unable to allocate %d bytes for name: %s\n",
+ pmInDomStr_r(h->indom, strbuf, sizeof(strbuf)), (int)strlen(name), name);
+ *sts = PM_ERR_GENERIC;
+ return NULL;
+ }
+
+ if (inst == PM_IN_NULL) {
+ if (h->ins_mode == 0) {
+ last_e = h->last;
+ if (last_e == NULL)
+ inst = 0;
+ else {
+ if (last_e->inst == 0x7fffffff) {
+ /*
+ * overflowed inst identifier, need to shift to
+ * ins_mode == 1
+ */
+ h->ins_mode = 1;
+ last_e = NULL;
+ goto retry;
+ }
+ inst = last_e->inst+1;
+ }
+ }
+ else {
+retry:
+ inst = 0;
+ for (e = h->first; e != NULL; e = e->next) {
+ if (inst < e->inst)
+ break;
+ if (inst == 0x7fffffff) {
+ /*
+ * 2^32-1 is the maximum number of instances we can have
+ */
+ char strbuf[20];
+ __pmNotifyErr(LOG_ERR,
+ "insert_cache: indom %s: too many instances",
+ pmInDomStr_r(h->indom, strbuf, sizeof(strbuf)));
+ *sts = PM_ERR_GENERIC;
+ free(dup);
+ return NULL;
+ }
+ inst++;
+ last_e = e;
+ }
+ }
+ }
+
+ if ((e = (entry_t *)malloc(sizeof(entry_t))) == NULL) {
+ char strbuf[20];
+ __pmNotifyErr(LOG_ERR,
+ "insert_cache: indom %s: unable to allocate memory for entry_t",
+ pmInDomStr_r(h->indom, strbuf, sizeof(strbuf)));
+ *sts = PM_ERR_GENERIC;
+ free(dup);
+ return NULL;
+ }
+
+ if (last_e == NULL) {
+ /* head of list */
+ e->next = h->first;
+ h->first = e;
+ }
+ else {
+ /* middle of list */
+ e->next = last_e->next;
+ last_e->next = e;
+ }
+ e->inst = inst;
+ e->name = dup;
+ e->hashlen = get_hashlen(h, dup);
+ e->key = NULL;
+ e->state = PMDA_CACHE_INACTIVE;
+ e->private = NULL;
+ e->stamp = 0;
+ if (h->last == NULL || h->last->inst < inst)
+ h->last = e;
+ h->nentry++;
+
+ if (h->hsize > 0 && h->hsize < 1024 && h->nentry > 4 * h->hsize)
+ redo_hash(h, 1);
+
+ /* link into the inst hash list, if any */
+ if (h->ctl_inst != NULL) {
+ i = inst & h->hbits;
+ e->h_inst = h->ctl_inst[i];
+ h->ctl_inst[i] = e;
+ }
+ else
+ e->h_inst = NULL;
+
+ /* link into the name hash list, if any */
+ if (h->ctl_name != NULL) {
+ i = hash_str(e->name, e->hashlen) & h->hbits;
+ e->h_name = h->ctl_name[i];
+ h->ctl_name[i] = e;
+ }
+ else
+ e->h_name = NULL;
+
+ return e;
+}
+
+static int
+load_cache(hdr_t *h)
+{
+ FILE *fp;
+ entry_t *e;
+ int cnt;
+ int x;
+ int inst;
+ int keylen = 0;
+ void *key = NULL;
+ int s;
+ char buf[1024]; /* input line buffer, is this big enough? */
+ char *p;
+ int sts;
+ int sep = __pmPathSeparator();
+ char strbuf[20];
+
+ if (vdp == NULL) {
+ vdp = pmGetConfig("PCP_VAR_DIR");
+ snprintf(filename, sizeof(filename),
+ "%s%c" "config" "%c" "pmda", vdp, sep, sep);
+ mkdir2(filename, 0755);
+ }
+
+ snprintf(filename, sizeof(filename), "%s%cconfig%cpmda%c%s",
+ vdp, sep, sep, sep, pmInDomStr_r(h->indom, strbuf, sizeof(strbuf)));
+ if ((fp = fopen(filename, "r")) == NULL)
+ return -oserror();
+ if (fgets(buf, sizeof(buf), fp) == NULL) {
+ __pmNotifyErr(LOG_ERR,
+ "pmdaCacheOp: %s: empty file?", filename);
+ fclose(fp);
+ return PM_ERR_GENERIC;
+ }
+ s = sscanf(buf, "%d %d", &x, &h->ins_mode);
+ if (s != 2 || x != 1 || h->ins_mode < 0 || h->ins_mode > 1) {
+ __pmNotifyErr(LOG_ERR,
+ "pmdaCacheOp: %s: illegal first record: %s",
+ filename, buf);
+ fclose(fp);
+ return PM_ERR_GENERIC;
+ }
+
+ for (cnt = 0; ; cnt++) {
+ if (fgets(buf, sizeof(buf), fp) == NULL)
+ break;
+ if ((p = strchr(buf, '\n')) != NULL)
+ *p = '\0';
+ p = buf;
+ while (*p && isascii((int)*p) && isspace((int)*p))
+ p++;
+ if (*p == '\0') goto bad;
+ inst = 0;
+ while (*p && isascii((int)*p) && isdigit((int)*p)) {
+ inst = inst*10 + (*p-'0');
+ p++;
+ }
+ while (*p && isascii((int)*p) && isspace((int)*p))
+ p++;
+ if (inst < 0 || *p == '\0') goto bad;
+ x = 0;
+ while (*p && isascii((int)*p) && isdigit((int)*p)) {
+ x = x*10 + (*p-'0');
+ p++;
+ }
+ while (*p && isascii((int)*p) && isspace((int)*p))
+ p++;
+ if (*p == '[') {
+ char *pend;
+ char *q;
+ int i;
+ int tmp;
+ p++;
+ pend = p;
+ while (*pend && *pend != ']')
+ pend++;
+ if (*pend != ']')
+ goto bad;
+ /*
+ * convert key in place ...
+ */
+ keylen = (pend - p) / 2;
+ if ((key = malloc(keylen)) == NULL) {
+ __pmNotifyErr(LOG_ERR,
+ "load_cache: indom %s: unable to allocate memory for keylen=%d",
+ pmInDomStr(h->indom), keylen);
+ fclose(fp);
+ return PM_ERR_GENERIC;
+ }
+ q = key;
+ for (i = 0; i < keylen; i++) {
+ sscanf(p, "%2x", &tmp);
+ *q++ = (tmp & 0xff);
+ p += 2;
+ }
+ p += 2;
+ while (*p && isascii((int)*p) && isspace((int)*p))
+ p++;
+ }
+ else {
+ keylen = 0;
+ key = NULL;
+ }
+ if (*p == '\0') {
+bad:
+ __pmNotifyErr(LOG_ERR,
+ "pmdaCacheOp: %s: illegal record: %s",
+ filename, buf);
+ if (key) free(key);
+ fclose(fp);
+ return PM_ERR_GENERIC;
+ }
+ e = insert_cache(h, p, inst, &sts);
+ if (e == NULL) {
+ if (key) free(key);
+ fclose(fp);
+ return sts;
+ }
+ if (sts != 0) {
+ __pmNotifyErr(LOG_WARNING,
+ "pmdaCacheOp: %s: loading instance %d (\"%s\") ignored, already in cache as %d (\"%s\")",
+ filename, inst, p, e->inst, e->name);
+ }
+ e->keylen = keylen;
+ e->key = key;
+ e->stamp = x;
+ }
+ fclose(fp);
+
+#ifdef PCP_DEBUG
+ if (pmDebug & DBG_TRACE_INDOM) {
+ fprintf(stderr, "After PMDA_CACHE_LOAD\n");
+ dump(stderr, h, 0);
+ }
+#endif
+
+ return cnt;
+}
+
+static int
+save_cache(hdr_t *h, int hstate)
+{
+ FILE *fp;
+ entry_t *e;
+ int cnt;
+ time_t now;
+ int sep = __pmPathSeparator();
+ char strbuf[20];
+
+ if ((h->hstate & hstate) == 0 || (h->hstate & CACHE_STRINGS) != 0) {
+ /* nothing to be done */
+ return 0;
+ }
+
+ if (vdp == NULL) {
+ vdp = pmGetConfig("PCP_VAR_DIR");
+ snprintf(filename, sizeof(filename),
+ "%s%c" "config" "%c" "pmda", vdp, sep, sep);
+ mkdir2(filename, 0755);
+ }
+
+ snprintf(filename, sizeof(filename), "%s%cconfig%cpmda%c%s",
+ vdp, sep, sep, sep, pmInDomStr_r(h->indom, strbuf, sizeof(strbuf)));
+ if ((fp = fopen(filename, "w")) == NULL)
+ return -oserror();
+ fprintf(fp, "%d %d\n", VERSION, h->ins_mode);
+
+ now = time(NULL);
+ cnt = 0;
+ for (e = h->first; e != NULL; e = e->next) {
+ if (e->state == PMDA_CACHE_EMPTY)
+ continue;
+ if (e->stamp == 0)
+ e->stamp = now;
+ fprintf(fp, "%d %d", e->inst, (int)e->stamp);
+ if (e->keylen > 0) {
+ char *p = (char *)e->key;
+ int i;
+ fprintf(fp, " [");
+ for (i = 0; i < e->keylen; i++, p++)
+ fprintf(fp, "%02x", (*p & 0xff));
+ fputc(']', fp);
+ }
+ fprintf(fp, " %s\n", e->name);
+ cnt++;
+ }
+ fclose(fp);
+ h->hstate = 0;
+
+#ifdef PCP_DEBUG
+ if (pmDebug & DBG_TRACE_INDOM) {
+ fprintf(stderr, "After cache_save hstate={");
+ if (hstate & DIRTY_INSTANCE) fprintf(stderr, "DIRTY_INSTANCE");
+ if (hstate & DIRTY_STAMP) fprintf(stderr, "DIRTY_STAMP");
+ fprintf(stderr, "}\n");
+ dump(stderr, h, 0);
+ }
+#endif
+
+ return cnt;
+}
+
+void
+__pmdaCacheDumpAll(FILE *fp, int do_hash)
+{
+ hdr_t *h;
+
+ for (h = base; h != NULL; h = h->next) {
+ dump(fp, h, do_hash);
+ }
+}
+
+void
+__pmdaCacheDump(FILE *fp, pmInDom indom, int do_hash)
+{
+ hdr_t *h;
+
+ for (h = base; h != NULL; h = h->next) {
+ if (h->indom == indom) {
+ dump(fp, h, do_hash);
+ }
+ }
+}
+
+static int
+store(pmInDom indom, int flags, const char *name, pmInDom inst, int keylen, const char *key, void *private)
+{
+ hdr_t *h;
+ entry_t *e;
+ int sts;
+
+ if ((h = find_cache(indom, &sts)) == NULL)
+ return sts;
+
+ if ((e = find_entry(h, name, inst, &sts)) == NULL) {
+
+ if (flags != PMDA_CACHE_ADD) {
+#ifdef PCP_DEBUG
+ if (pmDebug & DBG_TRACE_INDOM) {
+ fprintf(stderr, "pmdaCache store: indom %s: instance \"%s\"", pmInDomStr(indom), name);
+ if (inst != PM_IN_NULL)
+ fprintf(stderr, " (%d)", inst);
+ fprintf(stderr, " not in cache: flags=%d not allowed\n", flags);
+ }
+#endif
+ return PM_ERR_INST;
+ }
+
+ if ((e = insert_cache(h, name, inst, &sts)) == NULL)
+ return sts;
+ h->hstate |= DIRTY_INSTANCE; /* added a new entry */
+ }
+ else {
+ if (sts == -1)
+ /*
+ * name contains space, match up to space (short name) but
+ * mismatch after that ... cannot store name in the cache,
+ * the PMDA would have to cull the entry that matches on
+ * the short name first
+ */
+ return -EINVAL;
+ }
+
+ switch (flags) {
+ case PMDA_CACHE_ADD:
+ e->keylen = keylen;
+ if (keylen > 0) {
+ if ((e->key = malloc(keylen)) == NULL) {
+ __pmNotifyErr(LOG_ERR,
+ "store: indom %s: unable to allocate memory for keylen=%d",
+ pmInDomStr(indom), keylen);
+ return PM_ERR_GENERIC;
+ }
+ memcpy(e->key, key, keylen);
+ }
+ else
+ e->key = NULL;
+ e->state = PMDA_CACHE_ACTIVE;
+ e->private = private;
+ e->stamp = 0; /* flag, updated at next cache_save() */
+ h->hstate |= DIRTY_STAMP; /* timestamp needs updating */
+ break;
+
+ case PMDA_CACHE_HIDE:
+ e->state = PMDA_CACHE_INACTIVE;
+ break;
+
+ case PMDA_CACHE_CULL:
+ e->state = PMDA_CACHE_EMPTY;
+ /*
+ * we don't clean anything up, which may be a problem in the
+ * presence of lots of culling ... see redo_hash() for how
+ * the culled entries can be reclaimed
+ */
+ h->hstate |= DIRTY_INSTANCE; /* entry will not be saved */
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return e->inst;
+}
+
+int
+pmdaCacheStore(pmInDom indom, int flags, const char *name, void *private)
+{
+ if (indom == PM_INDOM_NULL)
+ return PM_ERR_INDOM;
+
+ return store(indom, flags, name, PM_IN_NULL, 0, NULL, private);
+}
+
+/*
+ * Generate a new 31-bit (positive) instance number from a key provided
+ * as a ``hint'' via key[] (first keylen bytes) or name[] if keylen < 1
+ * or key == NULL ... useful for compressing natural 64-bit or larger
+ * instance identifiers into the 31-bits required for the PCP APIs
+ * and PDUs.
+ */
+int
+pmdaCacheStoreKey(pmInDom indom, int flags, const char *name, int keylen, const void *key, void *private)
+{
+ int inst;
+ int sts;
+ int i;
+ __uint32_t try = 0;
+ hdr_t *h;
+ entry_t *e;
+ const char *mykey;
+ int mykeylen;
+
+ if (indom == PM_INDOM_NULL)
+ return PM_ERR_INDOM;
+
+ if (flags != PMDA_CACHE_ADD)
+ return store(indom, flags, name, PM_IN_NULL, 0, NULL, private);
+
+ /*
+ * This is the PMDA_CACHE_ADD case, so need to find an instance id
+ */
+ if ((h = find_cache(indom, &sts)) == NULL)
+ return sts;
+
+ if (keylen < 1 || key == NULL) {
+ /* use name[] instead of keybuf[] */
+ mykey = (const char *)name;
+ mykeylen = strlen(name);
+ }
+ else {
+ mykey = key;
+ mykeylen = keylen;
+ }
+
+ if ((e = find_entry(h, name, PM_IN_NULL, &sts)) != NULL) {
+ /*
+ * cache entry already exists for this name ...
+ * if keys are not equal => failure
+ */
+ if (key_eq(e, mykeylen, mykey) == 0) {
+#ifdef PCP_DEBUG
+ if (pmDebug & DBG_TRACE_INDOM) {
+ fprintf(stderr, "pmdaCacheStoreKey: indom %s: instance \"%s\" (%d) in cache ", pmInDomStr(indom), e->name, e->inst);
+ KeyStr(stderr, e->keylen, (const char *)e->key);
+ fprintf(stderr, " does not match new entry ");
+ KeyStr(stderr, mykeylen, mykey);
+ fputc('\n', stderr);
+ }
+#endif
+ return PM_ERR_INST;
+ }
+ /* keys the same, use inst from existing entry */
+ inst = e->inst;
+ }
+ else {
+ /* we're in the inst guessing game ... */
+ for (i = 0; i < MAX_HASH_TRY; i++) {
+ try = hash(mykey, mykeylen, try);
+ /* strip top bit ... instance id must be positive */
+ inst = try & ~(1 << (8*sizeof(__uint32_t)-1));
+ e = find_entry(h, NULL, inst, &sts);
+ if (e == NULL) {
+ h->keyhash_cnt[i]++;
+ break;
+ }
+ /*
+ * Found matching entry using the guessed inst ...
+ *
+ * If the key[]s are the same and the name[]s are the same
+ * then the matching entry is already in the cache, so use
+ * this instance identifier.
+ *
+ * If the key[]s match, but the name[]s are different, this
+ * is an error (duplicate instance name).
+ *
+ * If the names[] match, but the key[]s are different, this
+ * is an error (duplicate key).
+ *
+ * Otherwise instance id is in use for another instance, so
+ * keep trying by rehashing.
+ */
+ if (strcmp(e->name, name) == 0) {
+ if (key_eq(e, mykeylen, mykey) == 1)
+ break;
+#ifdef PCP_DEBUG
+ if (pmDebug & DBG_TRACE_INDOM) {
+ fprintf(stderr, "pmdaCacheStoreKey: indom %s: instance \"%s\" (%d) in cache, ", pmInDomStr(indom), e->name, e->inst);
+ KeyStr(stderr, e->keylen, (const char *)e->key);
+ fprintf(stderr, " does not match new entry ");
+ KeyStr(stderr, mykeylen, mykey);
+ fputc('\n', stderr);
+ }
+#endif
+ return PM_ERR_INST;
+ }
+ else if (key_eq(e, mykeylen, mykey) == 1) {
+#ifdef PCP_DEBUG
+ if (pmDebug & DBG_TRACE_INDOM) {
+ fprintf(stderr, "pmdaCacheStoreKey: indom %s: instance %d ", pmInDomStr(indom), e->inst);
+ KeyStr(stderr, e->keylen, (const char *)e->key);
+ fprintf(stderr, " in cache, name \"%s\" does not match new entry \"%s\"\n", e->name, name);
+ }
+#endif
+ return PM_ERR_INST;
+ }
+ }
+ if (i == MAX_HASH_TRY) {
+ /* failed after MAX_HASH_TRY rehash attempts ... */
+ __pmNotifyErr(LOG_ERR,
+ "pmdaCacheStoreKey: indom %s: unable allocate a new id for instance \"%s\" based on a key of %d bytes\n",
+ pmInDomStr(h->indom), name, keylen);
+ return PM_ERR_GENERIC;
+ }
+
+ /*
+ * when using key[] or name[] as a hint, we permanently change
+ * to PMDA_CACHE_REUSE mode
+ */
+ h->ins_mode = 1;
+ }
+
+ return store(indom, flags, name, inst, mykeylen, mykey, private);
+}
+
+int pmdaCacheOp(pmInDom indom, int op)
+{
+ hdr_t *h;
+ entry_t *e;
+ int sts;
+
+ if (indom == PM_INDOM_NULL)
+ return PM_ERR_INDOM;
+
+ if (op == PMDA_CACHE_CHECK) {
+ /* is there a cache for this one? */
+ for (h = base; h != NULL; h = h->next) {
+ if (h->indom == indom)
+ return 1;
+ }
+ return 0;
+ }
+
+ if ((h = find_cache(indom, &sts)) == NULL)
+ return sts;
+
+ switch (op) {
+ case PMDA_CACHE_LOAD:
+ return load_cache(h);
+
+ case PMDA_CACHE_SAVE:
+ return save_cache(h, DIRTY_INSTANCE);
+
+ case PMDA_CACHE_SYNC:
+ return save_cache(h, DIRTY_INSTANCE|DIRTY_STAMP);
+
+ case PMDA_CACHE_STRINGS:
+ /* must be set before any cache entries are added */
+ if (h->nentry > 0)
+ return -E2BIG;
+ h->hstate |= CACHE_STRINGS;
+ return 0;
+
+ case PMDA_CACHE_ACTIVE:
+ sts = 0;
+ for (e = h->first; e != NULL; e = e->next) {
+ if (e->state == PMDA_CACHE_INACTIVE) {
+ e->state = PMDA_CACHE_ACTIVE;
+ sts++;
+ }
+ }
+ /* no instances added or deleted, so no need to save */
+ return sts;
+
+ case PMDA_CACHE_INACTIVE:
+ sts = 0;
+ for (e = h->first; e != NULL; e = e->next) {
+ if (e->state == PMDA_CACHE_ACTIVE) {
+ e->state = PMDA_CACHE_INACTIVE;
+ sts++;
+ }
+ }
+ /* no instances added or deleted, so no need to save */
+ return sts;
+
+ case PMDA_CACHE_CULL:
+ sts = 0;
+ for (e = h->first; e != NULL; e = e->next) {
+ if (e->state != PMDA_CACHE_EMPTY) {
+ e->state = PMDA_CACHE_EMPTY;
+ sts++;
+ }
+ }
+ if (sts > 0)
+ h->hstate |= DIRTY_INSTANCE; /* entries culled */
+ return sts;
+
+ case PMDA_CACHE_SIZE:
+ return h->nentry;
+
+ case PMDA_CACHE_SIZE_ACTIVE:
+ sts = 0;
+ for (e = h->first; e != NULL; e = e->next) {
+ if (e->state == PMDA_CACHE_ACTIVE)
+ sts++;
+ }
+ return sts;
+
+ case PMDA_CACHE_SIZE_INACTIVE:
+ sts = 0;
+ for (e = h->first; e != NULL; e = e->next) {
+ if (e->state == PMDA_CACHE_INACTIVE)
+ sts++;
+ }
+ return sts;
+
+ case PMDA_CACHE_REUSE:
+ h->ins_mode = 1;
+ return 0;
+
+ case PMDA_CACHE_REORG:
+ redo_hash(h, 0);
+ return 0;
+
+ case PMDA_CACHE_WALK_REWIND:
+ walk_cache(h, PMDA_CACHE_WALK_REWIND);
+ return 0;
+
+ case PMDA_CACHE_WALK_NEXT:
+ while ((e = walk_cache(h, PMDA_CACHE_WALK_NEXT)) != NULL) {
+ if (e->state == PMDA_CACHE_ACTIVE)
+ return e->inst;
+ }
+ return -1;
+
+ case PMDA_CACHE_DUMP:
+ dump(stderr, h, 0);
+ return 0;
+
+ case PMDA_CACHE_DUMP_ALL:
+ dump(stderr, h, 1);
+ return 0;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+int pmdaCacheLookupName(pmInDom indom, const char *name, int *inst, void **private)
+{
+ hdr_t *h;
+ entry_t *e;
+ int sts;
+
+ if (indom == PM_INDOM_NULL)
+ return PM_ERR_INDOM;
+
+ if ((h = find_cache(indom, &sts)) == NULL)
+ return sts;
+
+ if ((e = find_entry(h, name, PM_IN_NULL, &sts)) == NULL) {
+ if (sts == 0) sts = PM_ERR_INST;
+ return sts;
+ }
+
+ if (private != NULL)
+ *private = e->private;
+
+ if (inst != NULL)
+ *inst = e->inst;
+
+ return e->state;
+}
+
+int pmdaCacheLookup(pmInDom indom, int inst, char **name, void **private)
+{
+ hdr_t *h;
+ entry_t *e;
+ int sts;
+
+ if (indom == PM_INDOM_NULL)
+ return PM_ERR_INDOM;
+
+ if ((h = find_cache(indom, &sts)) == NULL)
+ return sts;
+
+ if ((e = find_entry(h, NULL, inst, &sts)) == NULL) {
+ if (sts == 0) sts = PM_ERR_INST;
+ return sts;
+ }
+
+ if (name != NULL)
+ *name = e->name;
+ if (private != NULL)
+ *private = e->private;
+
+ return e->state;
+}
+
+int pmdaCacheLookupKey(pmInDom indom, const char *name, int keylen, const void *key, char **oname, int *inst, void **private)
+{
+ hdr_t *h;
+ entry_t *e;
+ int sts;
+ const char *mykey;
+ int mykeylen;
+
+ if (indom == PM_INDOM_NULL)
+ return PM_ERR_INDOM;
+
+ if ((h = find_cache(indom, &sts)) == NULL)
+ return sts;
+
+ if (keylen < 1 || key == NULL) {
+ /* use name[] instead of keybuf[] */
+ mykey = (const char *)name;
+ mykeylen = strlen(name);
+ }
+ else {
+ mykey = key;
+ mykeylen = keylen;
+ }
+
+ /*
+ * No hash list for key[]s ... have to walk the cache.
+ * pmdaCacheStoreKey() ensures the key[]s are unique, so first match
+ * wins.
+ */
+ walk_cache(h, PMDA_CACHE_WALK_REWIND);
+ while ((e = walk_cache(h, PMDA_CACHE_WALK_NEXT)) != NULL) {
+ if (e->state == PMDA_CACHE_EMPTY)
+ continue;
+ if (key_eq(e, mykeylen, mykey) == 1) {
+ if (oname != NULL)
+ *oname = e->name;
+ if (inst != NULL)
+ *inst = e->inst;
+ if (private != NULL)
+ *private = e->private;
+ return e->state;
+ }
+ }
+
+#ifdef PCP_DEBUG
+ if (pmDebug & DBG_TRACE_INDOM) {
+ fprintf(stderr, "pmdaCacheLookupKey: indom %s: ", pmInDomStr(h->indom));
+ KeyStr(stderr, mykeylen, mykey);
+ fprintf(stderr, ": no matching key in cache\n");
+ }
+#endif
+ return PM_ERR_INST;
+}
+
+int pmdaCachePurge(pmInDom indom, time_t recent)
+{
+ hdr_t *h;
+ entry_t *e;
+ time_t epoch = time(NULL) - recent;
+ int cnt;
+ int sts;
+
+ if (indom == PM_INDOM_NULL)
+ return PM_ERR_INDOM;
+
+ if ((h = find_cache(indom, &sts)) == NULL)
+ return sts;
+
+ cnt = 0;
+ for (e = h->first; e != NULL; e = e->next) {
+ /*
+ * e->stamp == 0 => recently ACTIVE and no subsequent SAVE ...
+ * keep these ones
+ */
+ if (e->stamp != 0 && e->stamp < epoch) {
+ e->state = PMDA_CACHE_EMPTY;
+ cnt++;
+ }
+ }
+ if (cnt > 0)
+ h->hstate |= DIRTY_INSTANCE; /* entries marked empty */
+
+ return cnt;
+}
+
+/*
+--------------------------------------------------------------------
+lookup2.c, by Bob Jenkins, December 1996, Public Domain.
+hash(), hash2(), hash3, and mix() are externally useful functions.
+Routines to test the hash are included if SELF_TEST is defined.
+You can use this free for any purpose. It has no warranty.
+--------------------------------------------------------------------
+*/
+
+#include <stdio.h>
+#include <stddef.h>
+#include <stdlib.h>
+typedef unsigned long int ub4; /* unsigned 4-byte quantities */
+typedef unsigned char ub1;
+
+#define hashsize(n) ((ub4)1<<(n))
+#define hashmask(n) (hashsize(n)-1)
+
+/*
+--------------------------------------------------------------------
+mix -- mix 3 32-bit values reversibly.
+For every delta with one or two bit set, and the deltas of all three
+ high bits or all three low bits, whether the original value of a,b,c
+ is almost all zero or is uniformly distributed,
+* If mix() is run forward or backward, at least 32 bits in a,b,c
+ have at least 1/4 probability of changing.
+* If mix() is run forward, every bit of c will change between 1/3 and
+ 2/3 of the time. (Well, 22/100 and 78/100 for some 2-bit deltas.)
+mix() was built out of 36 single-cycle latency instructions in a
+ structure that could supported 2x parallelism, like so:
+ a -= b;
+ a -= c; x = (c>>13);
+ b -= c; a ^= x;
+ b -= a; x = (a<<8);
+ c -= a; b ^= x;
+ c -= b; x = (b>>13);
+ ...
+ Unfortunately, superscalar Pentiums and Sparcs can't take advantage
+ of that parallelism. They've also turned some of those single-cycle
+ latency instructions into multi-cycle latency instructions. Still,
+ this is the fastest good hash I could find. There were about 2^^68
+ to choose from. I only looked at a billion or so.
+--------------------------------------------------------------------
+*/
+#define mix(a,b,c) \
+{ \
+ a -= b; a -= c; a ^= (c>>13); \
+ b -= c; b -= a; b ^= (a<<8); \
+ c -= a; c -= b; c ^= (b>>13); \
+ a -= b; a -= c; a ^= (c>>12); \
+ b -= c; b -= a; b ^= (a<<16); \
+ c -= a; c -= b; c ^= (b>>5); \
+ a -= b; a -= c; a ^= (c>>3); \
+ b -= c; b -= a; b ^= (a<<10); \
+ c -= a; c -= b; c ^= (b>>15); \
+}
+
+/*
+--------------------------------------------------------------------
+hash() -- hash a variable-length key into a 32-bit value
+ k : the key (the unaligned variable-length array of bytes)
+ len : the length of the key, counting by bytes
+ level : can be any 4-byte value
+Returns a 32-bit value. Every bit of the key affects every bit of
+the return value. Every 1-bit and 2-bit delta achieves avalanche.
+About 36+6len instructions.
+
+The best hash table sizes are powers of 2. There is no need to do
+mod a prime (mod is sooo slow!). If you need less than 32 bits,
+use a bitmask. For example, if you need only 10 bits, do
+ h = (h & hashmask(10));
+In which case, the hash table should have hashsize(10) elements.
+
+If you are hashing n strings (ub1 **)k, do it like this:
+ for (i=0, h=0; i<n; ++i) h = hash( k[i], len[i], h);
+
+By Bob Jenkins, 1996. bob_jenkins@burtleburtle.net. You may use this
+code any way you wish, private, educational, or commercial. It's free.
+
+See http://burlteburtle.net/bob/hash/evahash.html
+Use for hash table lookup, or anything where one collision in 2^32 is
+acceptable. Do NOT use for cryptographic purposes.
+--------------------------------------------------------------------
+*/
+
+static __uint32_t
+hash(const char *k, int length, __uint32_t initval)
+{
+ __uint32_t a,b,c,len;
+
+ /* Set up the internal state */
+ len = length;
+ a = b = 0x9e3779b9; /* the golden ratio; an arbitrary value */
+ c = initval; /* the previous hash value */
+
+ /*---------------------------------------- handle most of the key */
+ while (len >= 12)
+ {
+ a += (k[0] +((__uint32_t)k[1]<<8) +((__uint32_t)k[2]<<16)
+ +((__uint32_t)k[3]<<24));
+ b += (k[4] +((__uint32_t)k[5]<<8) +((__uint32_t)k[6]<<16)
+ +((__uint32_t)k[7]<<24));
+ c += (k[8] +((__uint32_t)k[9]<<8)
+ +((__uint32_t)k[10]<<16)+((__uint32_t)k[11]<<24));
+ mix(a,b,c);
+ k += 12; len -= 12;
+ }
+
+ /*------------------------------------- handle the last 11 bytes */
+ c += length;
+ switch(len) /* all the case statements fall through */
+ {
+ case 11: c+=((__uint32_t)k[10]<<24);
+ case 10: c+=((__uint32_t)k[9]<<16);
+ case 9 : c+=((__uint32_t)k[8]<<8);
+ /* the first byte of c is reserved for the length */
+ case 8 : b+=((__uint32_t)k[7]<<24);
+ case 7 : b+=((__uint32_t)k[6]<<16);
+ case 6 : b+=((__uint32_t)k[5]<<8);
+ case 5 : b+=k[4];
+ case 4 : a+=((__uint32_t)k[3]<<24);
+ case 3 : a+=((__uint32_t)k[2]<<16);
+ case 2 : a+=((__uint32_t)k[1]<<8);
+ case 1 : a+=k[0];
+ /* case 0: nothing left to add */
+ }
+ mix(a,b,c);
+ /*-------------------------------------------- report the result */
+ return c;
+}
diff --git a/src/libpcp_pmda/src/callback.c b/src/libpcp_pmda/src/callback.c
new file mode 100644
index 0000000..c1a1d86
--- /dev/null
+++ b/src/libpcp_pmda/src/callback.c
@@ -0,0 +1,753 @@
+/*
+ * Copyright (c) 2013 Red Hat.
+ * Copyright (c) 1995-2000 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This library 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 Lesser General Public
+ * License for more details.
+ */
+
+#include "pmapi.h"
+#include "impl.h"
+#include "pmda.h"
+#include "libdefs.h"
+
+/*
+ * count the number of instances in an instance domain
+ */
+
+int
+__pmdaCntInst(pmInDom indom, pmdaExt *pmda)
+{
+ int i;
+ int sts = 0;
+
+ if (indom == PM_INDOM_NULL)
+ return 1;
+ if (pmdaCacheOp(indom, PMDA_CACHE_CHECK)) {
+ sts = pmdaCacheOp(indom, PMDA_CACHE_SIZE_ACTIVE);
+ }
+ else {
+ for (i = 0; i < pmda->e_nindoms; i++) {
+ if (pmda->e_indoms[i].it_indom == indom) {
+ sts = pmda->e_indoms[i].it_numinst;
+ break;
+ }
+ }
+ if (i == pmda->e_nindoms) {
+ char strbuf[20];
+ __pmNotifyErr(LOG_WARNING, "cntinst: unknown indom %s", pmInDomStr_r(indom, strbuf, sizeof(strbuf)));
+ }
+ }
+
+#ifdef PCP_DEBUG
+ if (pmDebug & DBG_TRACE_INDOM) {
+ char strbuf[20];
+ fprintf(stderr, "__pmdaCntInst(indom=%s) -> %d\n", pmInDomStr_r(indom, strbuf, sizeof(strbuf)), sts);
+ }
+#endif
+
+ return sts;
+}
+
+/*
+ * commence a new round of instance selection
+ */
+
+static pmdaIndom last;
+
+/*
+ * State between here and __pmdaNextInst is a little strange
+ *
+ * for the classical method,
+ * - pmda->e_idp is set here (points into indomtab[]) and
+ * pmda->e_idp->it_indom is used in __pmdaNextInst
+ *
+ * for the cache method
+ * - pmda->e_idp is set here (points into last) which is also set
+ * up with the it_indom field (other fields in last are not used),
+ * and pmda->e_idp->it_indom in __pmdaNextInst
+ *
+ * In both cases, pmda->e_ordinal and pmda->e_singular are set here
+ * and updated in __pmdaNextInst.
+ *
+ * As in most other places, this is not thread-safe and we assume we
+ * call __pmdaStartInst and then repeatedly call __pmdaNextInst all
+ * for the same indom, before calling __pmdaStartInst again.
+ *
+ * If we could do this again, adding an indom argument to __pmdaNextInst
+ * would be a better design, but the API to __pmdaNextInst has escaped.
+ */
+void
+__pmdaStartInst(pmInDom indom, pmdaExt *pmda)
+{
+ int i;
+
+ pmda->e_ordinal = pmda->e_singular = -1;
+ if (indom == PM_INDOM_NULL) {
+ /* singular value */
+ pmda->e_singular = 0;
+ }
+ else {
+ if (pmdaCacheOp(indom, PMDA_CACHE_CHECK)) {
+ pmdaCacheOp(indom, PMDA_CACHE_WALK_REWIND);
+ last.it_indom = indom;
+ pmda->e_idp = &last;
+ pmda->e_ordinal = 0;
+ }
+ else {
+ for (i = 0; i < pmda->e_nindoms; i++) {
+ if (pmda->e_indoms[i].it_indom == indom) {
+ /* multiple values are possible */
+ pmda->e_idp = &pmda->e_indoms[i];
+ pmda->e_ordinal = 0;
+ break;
+ }
+ }
+ }
+ }
+
+#ifdef PCP_DEBUG
+ if (pmDebug & DBG_TRACE_INDOM) {
+ char strbuf[20];
+ fprintf(stderr, "__pmdaStartInst(indom=%s) e_ordinal=%d\n",
+ pmInDomStr_r(indom, strbuf, sizeof(strbuf)), pmda->e_ordinal);
+ }
+#endif
+ return;
+}
+
+/*
+ * select the next instance
+ */
+
+int
+__pmdaNextInst(int *inst, pmdaExt *pmda)
+{
+ int j;
+ int myinst;
+
+ if (pmda->e_singular == 0) {
+ /* PM_INDOM_NULL ... just the one value */
+ *inst = 0;
+ pmda->e_singular = -1;
+ return 1;
+ }
+ if (pmda->e_ordinal >= 0) {
+ /* scan for next value in the profile */
+ if (pmda->e_idp == &last) {
+ /* cache-driven */
+ while ((myinst = pmdaCacheOp(pmda->e_idp->it_indom, PMDA_CACHE_WALK_NEXT)) != -1) {
+ pmda->e_ordinal++;
+ if (__pmInProfile(pmda->e_idp->it_indom, pmda->e_prof, myinst)) {
+ *inst = myinst;
+#ifdef PCP_DEBUG
+ if (pmDebug & DBG_TRACE_INDOM) {
+ char strbuf[20];
+ fprintf(stderr, "__pmdaNextInst(indom=%s) -> %d e_ordinal=%d (cache)\n",
+ pmInDomStr_r(pmda->e_idp->it_indom, strbuf, sizeof(strbuf)), myinst, pmda->e_ordinal);
+ }
+#endif
+ return 1;
+ }
+ }
+ }
+ else {
+ /* indomtab[]-driven */
+ for (j = pmda->e_ordinal; j < pmda->e_idp->it_numinst; j++) {
+ if (__pmInProfile(pmda->e_idp->it_indom,
+ pmda->e_prof,
+ pmda->e_idp->it_set[j].i_inst)) {
+ *inst = pmda->e_idp->it_set[j].i_inst;
+ pmda->e_ordinal = j+1;
+#ifdef PCP_DEBUG
+ if (pmDebug & DBG_TRACE_INDOM) {
+ char strbuf[20];
+ fprintf(stderr, "__pmdaNextInst(indom=%s) -> %d e_ordinal=%d\n",
+ pmInDomStr_r(pmda->e_idp->it_indom, strbuf, sizeof(strbuf)), *inst, pmda->e_ordinal);
+ }
+#endif
+ return 1;
+ }
+ }
+ }
+ pmda->e_ordinal = -1;
+ }
+ return 0;
+}
+
+
+/*
+ * Helper routines for performing metric table searches.
+ *
+ * There are currently three ways - using the PMID hash that hangs off
+ * of the e_ext structure, using the direct mapping mechanism (require
+ * PMIDs be allocated one after-the-other, all in one cluster), or via
+ * a linear search of the metric table array.
+ */
+
+static pmdaMetric *
+__pmdaHashedSearch(pmID pmid, __pmHashCtl *hash)
+{
+ __pmHashNode *node;
+
+ if ((node = __pmHashSearch(pmid, hash)) == NULL)
+ return NULL;
+ return (pmdaMetric *)node->data;
+}
+
+static pmdaMetric *
+__pmdaDirectSearch(pmID pmid, pmdaExt *pmda)
+{
+ __pmID_int *pmidp = (__pmID_int *)&pmid;
+
+ /*
+ * pmidp->domain is correct ... PMCD guarantees this, but
+ * pmda->e_direct only works for a single cluster
+ */
+ if (pmidp->item < pmda->e_nmetrics &&
+ pmidp->cluster ==
+ ((__pmID_int *)&pmda->e_metrics[pmidp->item].m_desc.pmid)->cluster) {
+ /* pmidp->item is unsigned, so must be >= 0 */
+ return &pmda->e_metrics[pmidp->item];
+ }
+ return NULL;
+}
+
+static pmdaMetric *
+__pmdaLinearSearch(pmID pmid, pmdaExt *pmda)
+{
+ int i;
+
+ for (i = 0; i < pmda->e_nmetrics; i++) {
+ if (pmda->e_metrics[i].m_desc.pmid == pmid) {
+ /* found the hard way */
+ return &pmda->e_metrics[i];
+ }
+ }
+ return NULL;
+}
+
+/*
+ * Save the profile away for use in __pmdaNextInst() during subsequent
+ * fetches ... it is the _caller_ of pmdaProfile()'s responsibility to
+ * ensure that the profile is not freed while it is being used here.
+ *
+ * For DSO pmdas, the profiles are managed per client in DoProfile() and
+ * DeleteClient() but sent to the pmda in SendFetch()
+ *
+ * For daemon pmdas, the profile is received from pmcd in __pmdaMainPDU
+ * and the last received profile is held there
+ */
+
+int
+pmdaProfile(__pmProfile *prof, pmdaExt *pmda)
+{
+ pmda->e_prof = prof;
+ return 0;
+}
+
+/*
+ * return desciption of an instance or instance domain
+ */
+
+int
+pmdaInstance(pmInDom indom, int inst, char *name, __pmInResult **result, pmdaExt *pmda)
+{
+ int i;
+ int namelen;
+ int err = 0;
+ __pmInResult *res;
+ pmdaIndom *idp = NULL; /* initialize to pander to gcc */
+ int have_cache = 0;
+ int myinst;
+ char *np;
+
+ if (pmdaCacheOp(indom, PMDA_CACHE_CHECK)) {
+ have_cache = 1;
+ }
+ else {
+ /*
+ * check this is an instance domain we know about -- code below
+ * assumes this test is complete
+ */
+ for (i = 0; i < pmda->e_nindoms; i++) {
+ if (pmda->e_indoms[i].it_indom == indom)
+ break;
+ }
+ if (i >= pmda->e_nindoms)
+ return PM_ERR_INDOM;
+ idp = &pmda->e_indoms[i];
+ }
+
+ if ((res = (__pmInResult *)malloc(sizeof(*res))) == NULL)
+ return -oserror();
+ res->indom = indom;
+
+ if (name == NULL && inst == PM_IN_NULL)
+ res->numinst = __pmdaCntInst(indom, pmda);
+ else
+ res->numinst = 1;
+
+ if (inst == PM_IN_NULL) {
+ if ((res->instlist = (int *)malloc(res->numinst * sizeof(res->instlist[0]))) == NULL) {
+ free(res);
+ return -oserror();
+ }
+ }
+ else
+ res->instlist = NULL;
+
+ if (name == NULL) {
+ if ((res->namelist = (char **)malloc(res->numinst * sizeof(res->namelist[0]))) == NULL) {
+ __pmFreeInResult(res);
+ return -oserror();
+ }
+ for (i = 0; i < res->numinst; i++)
+ res->namelist[0] = NULL;
+ }
+ else
+ res->namelist = NULL;
+
+ if (name == NULL && inst == PM_IN_NULL) {
+ /* return inst and name for everything */
+ if (have_cache) {
+ pmdaCacheOp(indom, PMDA_CACHE_WALK_REWIND);
+ i = 0;
+ while (i < res->numinst && (myinst = pmdaCacheOp(indom, PMDA_CACHE_WALK_NEXT)) != -1) {
+ if (pmdaCacheLookup(indom, myinst, &np, NULL) != PMDA_CACHE_ACTIVE)
+ continue;
+
+ res->instlist[i] = myinst;
+ if ((res->namelist[i++] = strdup(np)) == NULL) {
+ __pmFreeInResult(res);
+ return -oserror();
+ }
+ }
+ }
+ else {
+ for (i = 0; i < res->numinst; i++) {
+ res->instlist[i] = idp->it_set[i].i_inst;
+ if ((res->namelist[i] = strdup(idp->it_set[i].i_name)) == NULL) {
+ __pmFreeInResult(res);
+ return -oserror();
+ }
+ }
+ }
+ }
+ else if (name == NULL) {
+ /* given an inst, return the name */
+ if (have_cache) {
+ if (pmdaCacheLookup(indom, inst, &np, NULL) == PMDA_CACHE_ACTIVE) {
+ if ((res->namelist[0] = strdup(np)) == NULL) {
+ __pmFreeInResult(res);
+ return -oserror();
+ }
+ }
+ else
+ err = 1;
+ }
+ else {
+ for (i = 0; i < idp->it_numinst; i++) {
+ if (inst == idp->it_set[i].i_inst) {
+ if ((res->namelist[0] = strdup(idp->it_set[i].i_name)) == NULL) {
+ __pmFreeInResult(res);
+ return -oserror();
+ }
+ break;
+ }
+ }
+ if (i == idp->it_numinst)
+ err = 1;
+ }
+ }
+ else if (inst == PM_IN_NULL && (namelen = (int)strlen(name)) > 0) {
+ if (have_cache) {
+ if (pmdaCacheLookupName(indom, name, &myinst, NULL) == PMDA_CACHE_ACTIVE)
+ res->instlist[0] = myinst;
+ else
+ err = 1;
+ }
+ else {
+ /* given a name, return an inst. If the name contains spaces,
+ * only exact matches are good enough for us, otherwise, we're
+ * prepared to accept a match upto the first space in the
+ * instance name on the assumption that pmdas will play by the
+ * rules and guarantee the first "word" in the instance name
+ * is unique. That allows for the things like "1 5 15" to match
+ * instances for kernel.all.load["1 minute","5 minute","15 minutes"]
+ */
+ char * nspace = strchr (name, ' ');
+
+ for (i = 0; i < idp->it_numinst; i++) {
+ char *instname = idp->it_set[i].i_name;
+ if (strcmp(name, instname) == 0) {
+ /* accept an exact match */
+#ifdef PCP_DEBUG
+ if (pmDebug & DBG_TRACE_LIBPMDA) {
+ fprintf(stderr,
+ "pmdaInstance: exact match name=%s id=%d\n",
+ name, idp->it_set[i].i_inst);
+ }
+#endif
+ res->instlist[0] = idp->it_set[i].i_inst;
+ break;
+ }
+ else if (nspace == NULL) {
+ /* all of name must match instname up to the the first
+ * space in instname. */
+ char *p = strchr(instname, ' ');
+ if (p != NULL) {
+ int len = (int)(p - instname);
+ if (namelen == len && strncmp(name, instname, len) == 0) {
+#ifdef PCP_DEBUG
+ if (pmDebug & DBG_TRACE_LIBPMDA) {
+ fprintf(stderr, "pmdaInstance: matched argument name=\"%s\" with indom id=%d name=\"%s\" len=%d\n",
+ name, idp->it_set[i].i_inst, instname, len);
+ }
+#endif
+ res->instlist[0] = idp->it_set[i].i_inst;
+ break;
+ }
+ }
+ }
+ }
+ if (i == idp->it_numinst)
+ err = 1;
+ }
+ }
+ else
+ err = 1;
+ if (err == 1) {
+ /* bogus arguments or instance id/name */
+ __pmFreeInResult(res);
+ return PM_ERR_INST;
+ }
+
+ *result = res;
+ return 0;
+}
+
+/*
+ * resize the pmResult and call the e_callback for each metric instance
+ * required in the profile.
+ */
+
+int
+pmdaFetch(int numpmid, pmID pmidlist[], pmResult **resp, pmdaExt *pmda)
+{
+ int i; /* over pmidlist[] */
+ int j; /* over metatab and vset->vlist[] */
+ int sts;
+ int need;
+ int inst;
+ int numval;
+ pmValueSet *vset;
+ pmDesc *dp;
+ pmdaMetric *metap;
+ pmAtomValue atom;
+ int type;
+ e_ext_t *extp = (e_ext_t *)pmda->e_ext;
+
+ if (extp->pmda_interface >= PMDA_INTERFACE_5)
+ __pmdaSetContext(pmda->e_context);
+
+ if (numpmid > extp->maxnpmids) {
+ if (extp->res != NULL)
+ free(extp->res);
+ /* (numpmid - 1) because there's room for one valueSet in a pmResult */
+ need = (int)sizeof(pmResult) + (numpmid - 1) * (int)sizeof(pmValueSet *);
+ if ((extp->res = (pmResult *) malloc(need)) == NULL)
+ return -oserror();
+ extp->maxnpmids = numpmid;
+ }
+
+ extp->res->timestamp.tv_sec = 0;
+ extp->res->timestamp.tv_usec = 0;
+ extp->res->numpmid = numpmid;
+
+ for (i = 0; i < numpmid; i++) {
+ if (pmda->e_flags & PMDA_EXT_FLAG_HASHED)
+ metap = __pmdaHashedSearch(pmidlist[i], &extp->hashpmids);
+ else if (pmda->e_direct)
+ metap = __pmdaDirectSearch(pmidlist[i], pmda);
+ else
+ metap = __pmdaLinearSearch(pmidlist[i], pmda);
+
+ if (metap) {
+ dp = &(metap->m_desc);
+ if (dp->indom != PM_INDOM_NULL) {
+ /* count instances in the profile */
+ numval = 0;
+ /* count instances in indom */
+ __pmdaStartInst(dp->indom, pmda);
+ while (__pmdaNextInst(&inst, pmda)) {
+ numval++;
+ }
+ }
+ else {
+ /* singular instance domains */
+ numval = 1;
+ }
+ }
+ else {
+ dp = NULL;
+ /* dynamic name metrics may often vanish, avoid log spam */
+ if (extp->pmda_interface < PMDA_INTERFACE_4) {
+ char strbuf[20];
+ __pmNotifyErr(LOG_ERR,
+ "pmdaFetch: Requested metric %s is not defined",
+ pmIDStr_r(pmidlist[i], strbuf, sizeof(strbuf)));
+ }
+ numval = PM_ERR_PMID;
+ }
+
+
+ /* Must use individual malloc()s because of pmFreeResult() */
+ if (numval >= 1)
+ extp->res->vset[i] = vset = (pmValueSet *)malloc(sizeof(pmValueSet) +
+ (numval - 1)*sizeof(pmValue));
+ else
+ extp->res->vset[i] = vset = (pmValueSet *)malloc(sizeof(pmValueSet) -
+ sizeof(pmValue));
+ if (vset == NULL) {
+ sts = -oserror();
+ goto error;
+ }
+ vset->pmid = pmidlist[i];
+ vset->numval = numval;
+ vset->valfmt = PM_VAL_INSITU;
+ if (vset->numval <= 0)
+ continue;
+
+ if (dp->indom == PM_INDOM_NULL)
+ inst = PM_IN_NULL;
+ else {
+ __pmdaStartInst(dp->indom, pmda);
+ __pmdaNextInst(&inst, pmda);
+ }
+ type = dp->type;
+ j = 0;
+ do {
+ if (j == numval) {
+ /* more instances than expected! */
+ numval++;
+ extp->res->vset[i] = vset = (pmValueSet *)realloc(vset,
+ sizeof(pmValueSet) + (numval - 1)*sizeof(pmValue));
+ if (vset == NULL) {
+ sts = -oserror();
+ goto error;
+ }
+ }
+ vset->vlist[j].inst = inst;
+
+ if ((sts = (*(pmda->e_fetchCallBack))(metap, inst, &atom)) < 0) {
+ char strbuf[20];
+
+ pmIDStr_r(dp->pmid, strbuf, sizeof(strbuf));
+ if (sts == PM_ERR_PMID) {
+ __pmNotifyErr(LOG_ERR,
+ "pmdaFetch: PMID %s not handled by fetch callback\n",
+ strbuf);
+ }
+ else if (sts == PM_ERR_INST) {
+#ifdef PCP_DEBUG
+ if (pmDebug & DBG_TRACE_LIBPMDA) {
+ __pmNotifyErr(LOG_ERR,
+ "pmdaFetch: Instance %d of PMID %s not handled by fetch callback\n",
+ inst, strbuf);
+ }
+#endif
+ }
+ else if (sts == PM_ERR_APPVERSION ||
+ sts == PM_ERR_PERMISSION ||
+ sts == PM_ERR_AGAIN ||
+ sts == PM_ERR_NYI) {
+#ifdef PCP_DEBUG
+ if (pmDebug & DBG_TRACE_LIBPMDA) {
+ __pmNotifyErr(LOG_ERR,
+ "pmdaFetch: Unavailable metric PMID %s[%d]\n",
+ strbuf, inst);
+ }
+#endif
+ }
+ else {
+ __pmNotifyErr(LOG_ERR,
+ "pmdaFetch: Fetch callback error from metric PMID %s[%d]: %s\n",
+ strbuf, inst, pmErrStr(sts));
+ }
+ }
+ else {
+ /*
+ * PMDA_INTERFACE_2
+ * >= 0 => OK
+ * PMDA_INTERFACE_3 or PMDA_INTERFACE_4
+ * == 0 => no values
+ * > 0 => OK
+ * PMDA_INTERFACE_5 or later
+ * == 0 (PMDA_FETCH_NOVALUES) => no values
+ * == 1 (PMDA_FETCH_STATIC) or > 2 => OK
+ * == 2 (PMDA_FETCH_DYNAMIC) => OK and free(atom.vp)
+ * after __pmStuffValue() called
+ */
+ if (extp->pmda_interface == PMDA_INTERFACE_2 ||
+ (extp->pmda_interface >= PMDA_INTERFACE_3 && sts > 0)) {
+ int lsts;
+
+ if ((lsts = __pmStuffValue(&atom, &vset->vlist[j], type)) == PM_ERR_TYPE) {
+ char strbuf[20];
+ char st2buf[20];
+ __pmNotifyErr(LOG_ERR,
+ "pmdaFetch: Descriptor type (%s) for metric %s is bad",
+ pmTypeStr_r(type, strbuf, sizeof(strbuf)),
+ pmIDStr_r(dp->pmid, st2buf, sizeof(st2buf)));
+ }
+ else if (lsts >= 0) {
+ vset->valfmt = lsts;
+ j++;
+ }
+ if (extp->pmda_interface >= PMDA_INTERFACE_5 && sts == PMDA_FETCH_DYNAMIC) {
+ if (type == PM_TYPE_STRING)
+ free(atom.cp);
+ else if (type == PM_TYPE_AGGREGATE)
+ free(atom.vbp);
+ else {
+ char strbuf[20];
+ char st2buf[20];
+ __pmNotifyErr(LOG_WARNING,
+ "pmdaFetch: Attempt to free value for metric %s of wrong type %s\n",
+ pmIDStr_r(dp->pmid, strbuf, sizeof(strbuf)),
+ pmTypeStr_r(type, st2buf, sizeof(st2buf)));
+ }
+ }
+ if (lsts < 0)
+ sts = lsts;
+ }
+ }
+ } while (dp->indom != PM_INDOM_NULL && __pmdaNextInst(&inst, pmda));
+
+ if (j == 0)
+ vset->numval = sts;
+ else
+ vset->numval = j;
+
+ }
+ *resp = extp->res;
+ return 0;
+
+ error:
+
+ if (i) {
+ extp->res->numpmid = i;
+ __pmFreeResultValues(extp->res);
+ }
+ return sts;
+}
+
+/*
+ * Return the metric description
+ */
+
+int
+pmdaDesc(pmID pmid, pmDesc *desc, pmdaExt *pmda)
+{
+ e_ext_t *extp = (e_ext_t *)pmda->e_ext;
+ pmdaMetric *metric;
+ char strbuf[32];
+
+ if (extp->pmda_interface >= PMDA_INTERFACE_5)
+ __pmdaSetContext(pmda->e_context);
+
+ if (pmda->e_flags & PMDA_EXT_FLAG_HASHED)
+ metric = __pmdaHashedSearch(pmid, &extp->hashpmids);
+ else if (pmda->e_direct)
+ metric = __pmdaDirectSearch(pmid, pmda);
+ else
+ metric = __pmdaLinearSearch(pmid, pmda);
+
+ if (metric) {
+ *desc = metric->m_desc;
+ return 0;
+ }
+
+ __pmNotifyErr(LOG_ERR, "Requested metric %s is not defined",
+ pmIDStr_r(pmid, strbuf, sizeof(strbuf)));
+ return PM_ERR_PMID;
+}
+
+/*
+ * Return the help text for a metric
+ */
+
+int
+pmdaText(int ident, int type, char **buffer, pmdaExt *pmda)
+{
+ e_ext_t *extp = (e_ext_t *)pmda->e_ext;
+
+ if (extp->pmda_interface >= PMDA_INTERFACE_5)
+ __pmdaSetContext(pmda->e_context);
+
+ if (pmda->e_help >= 0) {
+ if ((type & PM_TEXT_PMID) == PM_TEXT_PMID)
+ *buffer = pmdaGetHelp(pmda->e_help, (pmID)ident, type);
+ else
+ *buffer = pmdaGetInDomHelp(pmda->e_help, (pmInDom)ident, type);
+ }
+ else
+ *buffer = NULL;
+
+ return (*buffer == NULL) ? PM_ERR_TEXT : 0;
+}
+
+/*
+ * Tell PMCD there is nothing to store
+ */
+
+int
+pmdaStore(pmResult *result, pmdaExt *pmda)
+{
+ return PM_ERR_PERMISSION;
+}
+
+/*
+ * Expect routines pmdaPMID(), pmdaName() and pmdaChildren() below
+ * to be overridden with real routines for any PMDA that is
+ * using PMDA_INTERFACE_4 or later and supporting dynamic metrics.
+ *
+ * Expect the pmdaAttribute() routine to be overridden with a real
+ * routine for a PMDA using PMDA_INTERFACE_6 or later supporting
+ * metrics whose behaviour depends on clients being authenticated.
+ *
+ * These implementations are stubs that return appropriate errors
+ * if they are ever called.
+ */
+
+int
+pmdaPMID(const char *name, pmID *pmid, pmdaExt *pmda)
+{
+ return PM_ERR_NAME;
+}
+
+int
+pmdaName(pmID pmid, char ***nameset, pmdaExt *pmda)
+{
+ return PM_ERR_PMID;
+}
+
+int
+pmdaChildren(const char *name, int traverse, char ***offspring, int **status, pmdaExt *pmda)
+{
+ return PM_ERR_NAME;
+}
+
+int
+pmdaAttribute(int context, int attribute, const char *value, int size, pmdaExt *pmda)
+{
+ return 0; /* simply ignore everything by default */
+}
diff --git a/src/libpcp_pmda/src/context.c b/src/libpcp_pmda/src/context.c
new file mode 100644
index 0000000..fb0f0e2
--- /dev/null
+++ b/src/libpcp_pmda/src/context.c
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2010 Ken McDonell. All Rights Reserved.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This library 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 Lesser General Public
+ * License for more details.
+ */
+
+#include "pmapi.h"
+#include "impl.h"
+#include "pmda.h"
+
+static int last_ctx = -1; /* not thread safe! */
+
+void
+__pmdaSetContext(int ctx)
+{
+ last_ctx = ctx;
+}
+
+int
+pmdaGetContext(void)
+{
+ return last_ctx;
+}
diff --git a/src/libpcp_pmda/src/dynamic.c b/src/libpcp_pmda/src/dynamic.c
new file mode 100644
index 0000000..3d6e73d
--- /dev/null
+++ b/src/libpcp_pmda/src/dynamic.c
@@ -0,0 +1,217 @@
+/*
+ * Dynamic namespace metrics, PMDA helper routines.
+ *
+ * Copyright (c) 2013-2014 Red Hat.
+ * Copyright (c) 2010 Aconex. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#include "pmapi.h"
+#include "impl.h"
+#include "pmda.h"
+
+static struct dynamic {
+ const char *prefix;
+ int prefixlen;
+ int mtabcount; /* internal use only */
+ int extratrees; /* internal use only */
+ int nclusters;
+ int *clusters;
+ pmdaUpdatePMNS pmnsupdate;
+ pmdaUpdateText textupdate;
+ pmdaUpdateMetric mtabupdate;
+ pmdaCountMetrics mtabcounts;
+ pmdaNameSpace *pmns;
+ pmdaMetric *metrics; /* original fixed table */
+ int nmetrics; /* fixed metrics number */
+ unsigned int clustermask; /* mask out parts of PMID cluster */
+} *dynamic;
+
+static int dynamic_count;
+
+static inline unsigned int
+dynamic_pmid_cluster(int index, pmID pmid)
+{
+ if (dynamic[index].clustermask)
+ return pmid_cluster(pmid) & dynamic[index].clustermask;
+ return pmid_cluster(pmid);
+}
+
+int
+pmdaDynamicSetClusterMask(const char *prefix, unsigned int mask)
+{
+ int i;
+
+ for (i = 0; i < dynamic_count; i++) {
+ if (strcmp(prefix, dynamic[i].prefix) == 0)
+ continue;
+ dynamic[i].clustermask = mask;
+ return 0;
+ }
+ return -EINVAL;
+}
+
+void
+pmdaDynamicPMNS(const char *prefix,
+ int *clusters, int nclusters,
+ pmdaUpdatePMNS pmnsupdate, pmdaUpdateText textupdate,
+ pmdaUpdateMetric mtabupdate, pmdaCountMetrics mtabcounts,
+ pmdaMetric *metrics, int nmetrics)
+{
+ int size = (dynamic_count+1) * sizeof(struct dynamic);
+ int *ctab;
+ size_t ctabsz;
+
+ if ((dynamic = (struct dynamic *)realloc(dynamic, size)) == NULL) {
+ __pmNotifyErr(LOG_ERR, "out-of-memory registering dynamic metrics");
+ return;
+ }
+ ctabsz = sizeof(int) * nclusters;
+ if ((ctab = (int *)malloc(ctabsz)) == NULL) {
+ __pmNotifyErr(LOG_ERR, "out-of-memory registering dynamic clusters");
+ free(dynamic);
+ return;
+ }
+ dynamic[dynamic_count].prefix = prefix;
+ dynamic[dynamic_count].prefixlen = strlen(prefix);
+ dynamic[dynamic_count].nclusters = nclusters;
+ dynamic[dynamic_count].clusters = ctab;
+ memcpy(dynamic[dynamic_count].clusters, clusters, ctabsz);
+ dynamic[dynamic_count].pmnsupdate = pmnsupdate;
+ dynamic[dynamic_count].textupdate = textupdate;
+ dynamic[dynamic_count].mtabupdate = mtabupdate;
+ dynamic[dynamic_count].mtabcounts = mtabcounts;
+ dynamic[dynamic_count].pmns = NULL;
+ dynamic[dynamic_count].metrics = metrics;
+ dynamic[dynamic_count].nmetrics = nmetrics;
+ dynamic[dynamic_count].clustermask = 0;
+ dynamic_count++;
+}
+
+pmdaNameSpace *
+pmdaDynamicLookupName(pmdaExt *pmda, const char *name)
+{
+ int i;
+
+ for (i = 0; i < dynamic_count; i++) {
+ if (strncmp(name, dynamic[i].prefix, dynamic[i].prefixlen) == 0) {
+ if (dynamic[i].pmnsupdate(pmda, &dynamic[i].pmns))
+ pmdaDynamicMetricTable(pmda);
+ return dynamic[i].pmns;
+ }
+ }
+ return NULL;
+}
+
+pmdaNameSpace *
+pmdaDynamicLookupPMID(pmdaExt *pmda, pmID pmid)
+{
+ int i, j, cluster;
+
+ for (i = 0; i < dynamic_count; i++) {
+ cluster = dynamic_pmid_cluster(i, pmid);
+ for (j = 0; j < dynamic[i].nclusters; j++) {
+ if (cluster == dynamic[i].clusters[j]) {
+ if (dynamic[i].pmnsupdate(pmda, &dynamic[i].pmns))
+ pmdaDynamicMetricTable(pmda);
+ return dynamic[i].pmns;
+ }
+ }
+ }
+ return NULL;
+}
+
+int
+pmdaDynamicLookupText(pmID pmid, int type, char **buf, pmdaExt *pmda)
+{
+ int i, j;
+
+ for (i = 0; i < dynamic_count; i++) {
+ int cluster = dynamic_pmid_cluster(i, pmid);
+ for (j = 0; j < dynamic[i].nclusters; j++)
+ if (cluster == dynamic[i].clusters[j])
+ return dynamic[i].textupdate(pmda, pmid, type, buf);
+ }
+ return -ENOENT;
+}
+
+/*
+ * Call the update function for each new metric we're adding.
+ * We pass in the original metric, and the new (uninit'd) slot
+ * which needs to be filled in. All a bit obscure, really.
+ */
+static pmdaMetric *
+dynamic_metric_table(int index, pmdaMetric *offset)
+{
+ struct dynamic *dp = &dynamic[index];
+ int m, tree_count = dp->extratrees;
+
+ for (m = 0; m < dp->nmetrics; m++) {
+ pmdaMetric *mp = &dp->metrics[m];
+ int cluster = dynamic_pmid_cluster(index, mp->m_desc.pmid);
+ int c, gid;
+
+ for (c = 0; c < dp->nclusters; c++)
+ if (dp->clusters[c] == cluster)
+ break;
+ if (c < dp->nclusters)
+ for (gid = 0; gid < tree_count; gid++)
+ dp->mtabupdate(mp, offset++, gid + 1);
+ }
+ return offset;
+}
+
+/*
+ * Iterate through the dynamic table working out how many additional metric
+ * table entries are needed. Then allocate a new metric table, if needed,
+ * and run through the dynamic table once again to fill in the additional
+ * entries. Finally, we update the metric table pointer within the pmdaExt
+ * for libpcp_pmda callback routines subsequent use.
+ */
+void
+pmdaDynamicMetricTable(pmdaExt *pmda)
+{
+ int i, trees, total, resize = 0;
+ pmdaMetric *mtab, *fixed, *offset;
+
+ for (i = 0; i < dynamic_count; i++)
+ dynamic[i].mtabcount = dynamic[i].extratrees = 0;
+
+ for (i = 0; i < dynamic_count; i++) {
+ dynamic[i].mtabcounts(&total, &trees);
+ dynamic[i].mtabcount += total;
+ dynamic[i].extratrees += trees;
+ resize += (total * trees);
+ }
+
+ fixed = dynamic[0].metrics; /* fixed metrics */
+ total = dynamic[0].nmetrics; /* and the count */
+
+ if (resize == 0) {
+ /* Fits into the default metric table - reset it to original values */
+fallback:
+ if (pmda->e_metrics != fixed)
+ free(pmda->e_metrics);
+ pmdaRehash(pmda, fixed, total);
+ } else {
+ resize += total;
+ if ((mtab = calloc(resize, sizeof(pmdaMetric))) == NULL)
+ goto fallback;
+ memcpy(mtab, fixed, total * sizeof(pmdaMetric));
+ offset = mtab + total;
+ for (i = 0; i < dynamic_count; i++)
+ offset = dynamic_metric_table(i, offset);
+ if (pmda->e_metrics != fixed)
+ free(pmda->e_metrics);
+ pmdaRehash(pmda, mtab, resize);
+ }
+}
diff --git a/src/libpcp_pmda/src/events.c b/src/libpcp_pmda/src/events.c
new file mode 100644
index 0000000..09abb8d
--- /dev/null
+++ b/src/libpcp_pmda/src/events.c
@@ -0,0 +1,391 @@
+/*
+ * Service routines for managing a packed array of event records
+ *
+ * Copyright (c) 2010 Ken McDonell. All Rights Reserved.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This library 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 Lesser General Public
+ * License for more details.
+ */
+
+#include "pmapi.h"
+#include "impl.h"
+#include "pmda.h"
+
+typedef struct {
+ char *baddr; /* base address of the buffer */
+ char *bptr; /* next location to be filled in the buffer */
+ void *berp; /* current pmEvent[HighRes]Record in buffer */
+ int blen; /* buffer size */
+ int bstate;
+} bufctl_t;
+
+#define B_FREE 0
+#define B_INUSE 1
+
+static int nbuf;
+static bufctl_t *bufs;
+
+static int
+check_buf(bufctl_t *bp, int need)
+{
+ int offset = bp->bptr - bp->baddr;
+ int er_offset = (char *)bp->berp - bp->baddr;
+
+ while (bp->blen == 0 || &bp->bptr[need] >= &bp->baddr[bp->blen-1]) {
+ if (bp->blen == 0)
+ /* first time, punt on a 512 byte buffer */
+ bp->blen = 512;
+ else
+ bp->blen *= 2;
+ if ((bp->baddr = (char *)realloc(bp->baddr, bp->blen)) == NULL)
+ return -oserror();
+ bp->bptr = &bp->baddr[offset];
+ bp->berp = (void *)&bp->baddr[er_offset];
+ }
+ return 0;
+}
+
+static int
+event_array(void)
+{
+ int i;
+
+ for (i = 0; i < nbuf; i++) {
+ if (bufs[i].bstate == B_FREE)
+ break;
+ }
+
+ if (i == nbuf) {
+ nbuf++;
+ bufs = (bufctl_t *)realloc(bufs, nbuf*sizeof(bufs[0]));
+ if (bufs == NULL) {
+ nbuf = 0;
+ return -oserror();
+ }
+ }
+
+ bufs[i].bptr = bufs[i].baddr = NULL;
+ bufs[i].blen = 0;
+ bufs[i].bstate = B_INUSE;
+ return i;
+}
+
+int
+pmdaEventNewArray(void)
+{
+ int sts = event_array();
+
+ if (sts >= 0)
+ pmdaEventResetArray(sts);
+ return sts;
+}
+
+int
+pmdaEventNewHighResArray(void)
+{
+ int sts = event_array();
+
+ if (sts >= 0)
+ pmdaEventResetHighResArray(sts);
+ return sts;
+}
+
+/* prepare to reuse an array */
+int
+pmdaEventResetArray(int idx)
+{
+ bufctl_t *bp;
+ pmEventArray *eap;
+ int sts;
+
+ if (idx < 0 || idx >= nbuf || bufs[idx].bstate == B_FREE)
+ return PM_ERR_NOCONTEXT;
+ bp = &bufs[idx];
+ if ((sts = check_buf(bp, sizeof(pmEventArray) - sizeof(pmEventRecord))) < 0)
+ return sts;
+
+ eap = (pmEventArray *)bp->baddr;
+ eap->ea_nrecords = 0;
+ bp->bptr = bp->baddr + sizeof(pmEventArray) - sizeof(pmEventRecord);
+ return 0;
+}
+
+int
+pmdaEventResetHighResArray(int idx)
+{
+ bufctl_t *bp;
+ pmHighResEventArray *hreap;
+ int sts;
+
+ if (idx < 0 || idx >= nbuf || bufs[idx].bstate == B_FREE)
+ return PM_ERR_NOCONTEXT;
+ bp = &bufs[idx];
+ if ((sts = check_buf(bp, sizeof(*hreap) - sizeof(pmHighResEventRecord))) < 0)
+ return sts;
+
+ hreap = (pmHighResEventArray *)bp->baddr;
+ hreap->ea_nrecords = 0;
+ bp->bptr = bp->baddr + sizeof(*hreap) - sizeof(pmHighResEventRecord);
+ return 0;
+}
+
+/* release buffer space associated with a packed event array */
+int
+pmdaEventReleaseArray(int idx)
+{
+ if (idx < 0 || idx >= nbuf || bufs[idx].bstate == B_FREE)
+ return PM_ERR_NOCONTEXT;
+
+ free(bufs[idx].baddr);
+ bufs[idx].bstate = B_FREE;
+ return 0;
+}
+
+int
+pmdaEventReleaseHighResArray(int idx)
+{
+ return pmdaEventReleaseArray(idx);
+}
+
+int
+pmdaEventAddRecord(int idx, struct timeval *tp, int flags)
+{
+ int sts;
+ bufctl_t *bp;
+ pmEventArray *eap;
+ pmEventRecord *erp;
+
+ if (idx < 0 || idx >= nbuf || bufs[idx].bstate == B_FREE)
+ return PM_ERR_NOCONTEXT;
+ bp = &bufs[idx];
+
+ /* use pmdaEventAddMissedRecord for missed records ... */
+ if (flags & PM_EVENT_FLAG_MISSED)
+ return PM_ERR_CONV;
+
+ if ((sts = check_buf(bp, sizeof(*erp) - sizeof(pmEventParameter))) < 0)
+ return sts;
+ eap = (pmEventArray *)bp->baddr;
+ eap->ea_nrecords++;
+ erp = (pmEventRecord *)bp->bptr;
+ erp->er_timestamp.tv_sec = (__int32_t)tp->tv_sec;
+ erp->er_timestamp.tv_usec = (__int32_t)tp->tv_usec;
+ erp->er_nparams = 0;
+ erp->er_flags = flags;
+ bp->berp = (void *)erp;
+ bp->bptr += sizeof(pmEventRecord) - sizeof(pmEventParameter);
+ return 0;
+}
+
+int
+pmdaEventAddHighResRecord(int idx, struct timespec *ts, int flags)
+{
+ int sts;
+ bufctl_t *bp;
+ pmHighResEventArray *hreap;
+ pmHighResEventRecord *hrerp;
+
+ if (idx < 0 || idx >= nbuf || bufs[idx].bstate == B_FREE)
+ return PM_ERR_NOCONTEXT;
+ bp = &bufs[idx];
+
+ /* use pmdaEventAddMissedRecord for missed records ... */
+ if (flags & PM_EVENT_FLAG_MISSED)
+ return PM_ERR_CONV;
+
+ if ((sts = check_buf(bp, sizeof(*hrerp) - sizeof(pmEventParameter))) < 0)
+ return sts;
+ hreap = (pmHighResEventArray *)bp->baddr;
+ hreap->ea_nrecords++;
+ hrerp = (pmHighResEventRecord *)bp->bptr;
+ hrerp->er_timestamp.tv_sec = (__int64_t)ts->tv_sec;
+ hrerp->er_timestamp.tv_nsec = (__int64_t)ts->tv_nsec;
+ hrerp->er_nparams = 0;
+ hrerp->er_flags = flags;
+ bp->berp = (void *)hrerp;
+ bp->bptr += sizeof(pmHighResEventRecord) - sizeof(pmEventParameter);
+ return 0;
+}
+
+int
+pmdaEventAddMissedRecord(int idx, struct timeval *tp, int missed)
+{
+ int sts;
+ bufctl_t *bp;
+ pmEventArray *eap;
+ pmEventRecord *erp;
+
+ if (idx < 0 || idx >= nbuf || bufs[idx].bstate == B_FREE)
+ return PM_ERR_NOCONTEXT;
+ bp = &bufs[idx];
+
+ if ((sts = check_buf(bp, sizeof(*erp) - sizeof(pmEventParameter))) < 0)
+ return sts;
+ eap = (pmEventArray *)bp->baddr;
+ eap->ea_nrecords++;
+ erp = (pmEventRecord *)bp->bptr;
+ erp->er_timestamp.tv_sec = (__int32_t)tp->tv_sec;
+ erp->er_timestamp.tv_usec = (__int32_t)tp->tv_usec;
+ erp->er_nparams = missed;
+ erp->er_flags = PM_EVENT_FLAG_MISSED;
+ bp->berp = (void *)erp;
+ bp->bptr += sizeof(pmEventRecord) - sizeof(pmEventParameter);
+ return 0;
+}
+
+int
+pmdaEventAddHighResMissedRecord(int idx, struct timespec *ts, int missed)
+{
+ int sts;
+ bufctl_t *bp;
+ pmHighResEventArray *hreap;
+ pmHighResEventRecord *hrerp;
+
+ if (idx < 0 || idx >= nbuf || bufs[idx].bstate == B_FREE)
+ return PM_ERR_NOCONTEXT;
+ bp = &bufs[idx];
+
+ if ((sts = check_buf(bp, sizeof(*hrerp) - sizeof(pmEventParameter))) < 0)
+ return sts;
+ hreap = (pmHighResEventArray *)bp->baddr;
+ hreap->ea_nrecords++;
+ hrerp = (pmHighResEventRecord *)bp->bptr;
+ hrerp->er_timestamp.tv_sec = (__int32_t)ts->tv_sec;
+ hrerp->er_timestamp.tv_nsec = (__int32_t)ts->tv_nsec;
+ hrerp->er_nparams = missed;
+ hrerp->er_flags = PM_EVENT_FLAG_MISSED;
+ bp->berp = (void *)hrerp;
+ bp->bptr += sizeof(pmHighResEventRecord) - sizeof(pmEventParameter);
+ return 0;
+}
+
+int
+add_param(int idx, pmID pmid, int type, pmAtomValue *avp, bufctl_t **bpp)
+{
+ int sts;
+ int need; /* bytes in the buffer */
+ int vlen; /* value only length */
+ void *src;
+ pmEventParameter *epp;
+ bufctl_t *bp;
+
+ if (idx < 0 || idx >= nbuf || bufs[idx].bstate == B_FREE)
+ return PM_ERR_NOCONTEXT;
+ bp = &bufs[idx];
+
+ need = sizeof(pmEventParameter);
+ switch (type) {
+ case PM_TYPE_32:
+ case PM_TYPE_U32:
+ vlen = sizeof(avp->l);
+ need += vlen;
+ src = &avp->l;
+ break;
+ case PM_TYPE_64:
+ case PM_TYPE_U64:
+ vlen = sizeof(avp->ll);
+ need += vlen;
+ src = &avp->ll;
+ break;
+ case PM_TYPE_FLOAT:
+ vlen = sizeof(avp->f);
+ need += vlen;
+ src = &avp->f;
+ break;
+ case PM_TYPE_DOUBLE:
+ vlen = sizeof(avp->d);
+ need += vlen;
+ src = &avp->d;
+ break;
+ case PM_TYPE_STRING:
+ vlen = strlen(avp->cp);
+ need += PM_PDU_SIZE_BYTES(vlen);
+ src = avp->cp;
+ break;
+ case PM_TYPE_AGGREGATE:
+ /* PM_VAL_HDR_SIZE is added back in below */
+ vlen = avp->vbp->vlen - PM_VAL_HDR_SIZE;
+ need += PM_PDU_SIZE_BYTES(vlen);
+ src = avp->vbp->vbuf;
+ break;
+ default:
+ return PM_ERR_TYPE;
+ }
+ if ((sts = check_buf(bp, need)) < 0)
+ return sts;
+ epp = (pmEventParameter *)bp->bptr;
+ epp->ep_pmid = pmid;
+ epp->ep_len = PM_VAL_HDR_SIZE + vlen;
+ epp->ep_type = type;
+ memcpy((void *)(bp->bptr + sizeof(pmEventParameter)), src, vlen);
+ bp->bptr += need;
+ *bpp = bp;
+ return 0;
+}
+
+int
+pmdaEventAddParam(int idx, pmID pmid, int type, pmAtomValue *avp)
+{
+ int sts;
+ bufctl_t *bp;
+ pmEventRecord *erp;
+
+ if ((sts = add_param(idx, pmid, type, avp, &bp)) >= 0) {
+ erp = (pmEventRecord *)bp->berp;
+ erp->er_nparams++;
+ }
+ return sts;
+}
+
+int
+pmdaEventHighResAddParam(int idx, pmID pmid, int type, pmAtomValue *avp)
+{
+ int sts;
+ bufctl_t *bp;
+ pmHighResEventRecord *hrerp;
+
+ if ((sts = add_param(idx, pmid, type, avp, &bp)) >= 0) {
+ hrerp = (pmHighResEventRecord *)bp->berp;
+ hrerp->er_nparams++;
+ }
+ return sts;
+}
+
+/*
+ * fill in the vlen/vtype header and return the address of the whole
+ * structure
+ */
+pmEventArray *
+pmdaEventGetAddr(int idx)
+{
+ pmEventArray *eap;
+
+ if (idx < 0 || idx >= nbuf || bufs[idx].bstate == B_FREE)
+ return NULL;
+
+ eap = (pmEventArray *)bufs[idx].baddr;
+ eap->ea_type = PM_TYPE_EVENT;
+ eap->ea_len = bufs[idx].bptr - bufs[idx].baddr;
+ return eap;
+}
+
+pmHighResEventArray *
+pmdaEventHighResGetAddr(int idx)
+{
+ pmHighResEventArray *hreap;
+
+ if (idx < 0 || idx >= nbuf || bufs[idx].bstate == B_FREE)
+ return NULL;
+
+ hreap = (pmHighResEventArray *)bufs[idx].baddr;
+ hreap->ea_type = PM_TYPE_HIGHRES_EVENT;
+ hreap->ea_len = bufs[idx].bptr - bufs[idx].baddr;
+ return hreap;
+}
diff --git a/src/libpcp_pmda/src/exports b/src/libpcp_pmda/src/exports
new file mode 100644
index 0000000..ed8c313
--- /dev/null
+++ b/src/libpcp_pmda/src/exports
@@ -0,0 +1,115 @@
+PCP_PMDA_3.0 {
+ global:
+ pmdaInit;
+ pmdaMain;
+ pmdaGetOpt;
+ pmdaConnect;
+ pmdaOpenLog;
+ pmdaOpenHelp;
+ pmdaSetFlags;
+ pmdaGetContext;
+ pmdaGetInDomHelp;
+
+ pmdaDaemon;
+ pmdaDSO;
+
+ __pmdaInFd;
+ __pmdaMainPDU;
+ __pmdaSetContext;
+
+ pmdaPMID;
+ pmdaRehash;
+
+ pmdaSetCheckCallBack;
+ pmdaSetDoneCallBack;
+ pmdaSetEndContextCallBack;
+ pmdaSetFetchCallBack;
+ pmdaSetResultCallBack;
+
+ pmdaAttribute;
+ pmdaDesc;
+ pmdaFetch;
+ pmdaInstance;
+ pmdaProfile;
+ pmdaStore;
+ pmdaText;
+ pmdaName;
+ pmdaChildren;
+
+ pmdaCacheLookup;
+ pmdaCacheLookupKey;
+ pmdaCacheLookupName;
+ pmdaCacheOp;
+ pmdaCachePurge;
+ pmdaCacheStore;
+ pmdaCacheStoreKey;
+
+ __pmdaCacheDump;
+ __pmdaCacheDumpAll;
+ __pmdaStartInst;
+ __pmdaNextInst;
+ __pmdaCntInst;
+
+ pmdaCloseHelp;
+ pmdaGetHelp;
+ __pmdaHelpTab;
+
+ pmdaTreeChildren;
+ pmdaTreeName;
+ pmdaTreePMID;
+ pmdaTreeRebuildHash;
+ pmdaTreeSize;
+
+ pmdaEventAddMissedRecord;
+ pmdaEventAddParam;
+ pmdaEventAddRecord;
+ pmdaEventClients;
+ pmdaEventEndClient;
+ pmdaEventGetAddr;
+ pmdaEventNewActiveQueue;
+ pmdaEventNewArray;
+ pmdaEventNewClient;
+ pmdaEventNewQueue;
+ pmdaEventQueueAppend;
+ pmdaEventQueueBytes;
+ pmdaEventQueueClients;
+ pmdaEventQueueCounter;
+ pmdaEventQueueHandle;
+ pmdaEventQueueMemory;
+ pmdaEventQueueRecords;
+ pmdaEventReleaseArray;
+ pmdaEventResetArray;
+ pmdaEventSetAccess;
+ pmdaEventSetFilter;
+ __pmdaEventPrint;
+
+ pmdaDynamicLookupName;
+ pmdaDynamicLookupPMID;
+ pmdaDynamicLookupText;
+ pmdaDynamicMetricTable;
+ pmdaDynamicPMNS;
+
+ local: *;
+};
+
+PCP_PMDA_3.1 {
+ global:
+ pmdaGetOptions;
+ pmdaUsageMessage;
+} PCP_PMDA_3.0;
+
+PCP_PMDA_3.2 {
+ global:
+ pmdaDynamicSetClusterMask;
+} PCP_PMDA_3.1;
+
+PCP_PMDA_3.3 {
+ global:
+ pmdaEventNewHighResArray;
+ pmdaEventResetHighResArray;
+ pmdaEventReleaseHighResArray;
+ pmdaEventAddHighResRecord;
+ pmdaEventAddHighResMissedRecord;
+ pmdaEventHighResAddParam;
+ pmdaEventHighResGetAddr;
+} PCP_PMDA_3.2;
diff --git a/src/libpcp_pmda/src/help.c b/src/libpcp_pmda/src/help.c
new file mode 100644
index 0000000..d9d17b8
--- /dev/null
+++ b/src/libpcp_pmda/src/help.c
@@ -0,0 +1,217 @@
+/*
+ * Copyright (c) 1995-2003 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This library 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 Lesser General Public
+ * License for more details.
+ */
+
+/*
+ * Get help text from files built using newhelp
+ */
+#include "pmapi.h"
+#include "impl.h"
+#include "pmda.h"
+#include <sys/stat.h>
+
+typedef struct { /* beware: this data structure mirrored in chkhelp */
+ pmID pmid;
+ __uint32_t off_oneline;
+ __uint32_t off_text;
+} help_idx_t;
+
+typedef struct { /* beware: this data structure mirrored in chkhelp */
+ int dir_fd;
+ int pag_fd;
+ int numidx;
+ help_idx_t *index;
+ char *text;
+ int textlen;
+} help_t;
+
+static help_t *tab = NULL;
+static int numhelp = 0;
+
+/*
+ * open the help text files and return a handle on success
+ */
+int
+pmdaOpenHelp(char *fname)
+{
+ char pathname[MAXPATHLEN];
+ int sts, size;
+ help_idx_t hdr;
+ help_t *hp;
+ struct stat sbuf;
+
+ for (sts = 0; sts < numhelp; sts++) {
+ if (tab[sts].dir_fd == -1)
+ break;
+ }
+ if (sts == numhelp) {
+ sts = numhelp++;
+ tab = (help_t *)realloc(tab, numhelp * sizeof(tab[0]));
+ if (tab == NULL) {
+ __pmNoMem("pmdaOpenHelp", numhelp * sizeof(tab[0]), PM_RECOV_ERR);
+ numhelp = 0;
+ return -oserror();
+ }
+ }
+ hp = &tab[sts];
+ memset(hp, 0, sizeof(*hp));
+ hp->dir_fd = -1;
+ hp->pag_fd = -1;
+
+ snprintf(pathname, sizeof(pathname), "%s.dir", fname);
+ hp->dir_fd = open(pathname, O_RDONLY);
+ if (hp->dir_fd < 0) {
+ sts = -oserror();
+ goto failed;
+ }
+
+ if (read(hp->dir_fd, &hdr, sizeof(hdr)) != sizeof(hdr)) {
+ sts = -EINVAL;
+ goto failed;
+ }
+
+ if (hdr.pmid != 0x50635068 ||
+ (hdr.off_oneline & 0xffff0000) != 0x31320000) {
+ sts = -EINVAL;
+ goto failed;
+ }
+
+ hp->numidx = hdr.off_text;
+ size = (hp->numidx + 1) * sizeof(help_idx_t);
+
+ hp->index = (help_idx_t *)__pmMemoryMap(hp->dir_fd, size, 0);
+ if (hp->index == NULL) {
+ sts = -oserror();
+ goto failed;
+ }
+
+ snprintf(pathname, sizeof(pathname), "%s.pag", fname);
+ hp->pag_fd = open(pathname, O_RDONLY);
+ if (hp->pag_fd < 0) {
+ sts = -oserror();
+ goto failed;
+ }
+ if (fstat(hp->pag_fd, &sbuf) < 0) {
+ sts = -oserror();
+ goto failed;
+ }
+ hp->textlen = (int)sbuf.st_size;
+ hp->text = (char *)__pmMemoryMap(hp->pag_fd, hp->textlen, 0);
+ if (hp->text == NULL) {
+ sts = -oserror();
+ goto failed;
+ }
+ return numhelp - 1;
+
+failed:
+ pmdaCloseHelp(numhelp-1);
+ return sts;
+}
+
+/*
+ * retrieve pmID help text, ...
+ *
+ */
+char *
+pmdaGetHelp(int handle, pmID pmid, int type)
+{
+ int i;
+ help_t *hp;
+
+ if (handle < 0 || handle >= numhelp)
+ return NULL;
+ hp = &tab[handle];
+
+ /* search forwards -- could use binary chop */
+ for (i = 1; i <= hp->numidx; i++) {
+ if (hp->index[i].pmid == pmid) {
+ if (type & PM_TEXT_ONELINE)
+ return &hp->text[hp->index[i].off_oneline];
+ else
+ return &hp->text[hp->index[i].off_text];
+ }
+ }
+
+ return NULL;
+}
+
+/*
+ * retrieve pmInDom help text, ...
+ *
+ */
+char *
+pmdaGetInDomHelp(int handle, pmInDom indom, int type)
+{
+ int i;
+ help_t *hp;
+ pmID pmid;
+ __pmID_int *pip = (__pmID_int *)&pmid;
+
+ if (handle < 0 || handle >= numhelp)
+ return NULL;
+ hp = &tab[handle];
+
+ *pip = *((__pmID_int *)&indom);
+ /*
+ * set a bit here to disambiguate pmInDom from pmID
+ * -- this "hack" is shared between here and newhelp/newhelp.c
+ */
+ pip->flag = 1;
+
+ /* search backwards ... pmInDom entries are at the end */
+ for (i = hp->numidx; i >= 1; i--) {
+ if (hp->index[i].pmid == pmid) {
+ if (type & PM_TEXT_ONELINE)
+ return &hp->text[hp->index[i].off_oneline];
+ else
+ return &hp->text[hp->index[i].off_text];
+ }
+ }
+
+ return NULL;
+}
+
+/*
+ * this is only here for chkhelp(1) ... export the control data strcuture
+ */
+void *
+__pmdaHelpTab(void)
+{
+ return (void *)tab;
+}
+
+void
+pmdaCloseHelp(int handle)
+{
+ help_t *hp;
+
+ if (handle < 0 || handle >= numhelp)
+ return;
+ hp = &tab[handle];
+
+ if (hp->dir_fd != -1)
+ close(hp->dir_fd);
+ if (hp->pag_fd != -1)
+ close(hp->pag_fd);
+ if (hp->index != NULL)
+ __pmMemoryUnmap((void *)hp->index, (hp->numidx+1) * sizeof(help_idx_t));
+ if (hp->text != NULL)
+ __pmMemoryUnmap(hp->text, hp->textlen);
+
+ hp->textlen = 0;
+ hp->dir_fd = -1;
+ hp->pag_fd = -1;
+ hp->numidx = 0;
+ hp->index = NULL;
+ hp->text = NULL;
+}
diff --git a/src/libpcp_pmda/src/libdefs.h b/src/libpcp_pmda/src/libdefs.h
new file mode 100644
index 0000000..1d8b1c6
--- /dev/null
+++ b/src/libpcp_pmda/src/libdefs.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2013 Red Hat.
+ * Copyright (c) 1999-2000 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This library 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 Lesser General Public
+ * License for more details.
+ */
+#ifndef LIBDEFS_H
+#define LIBDEFS_H
+
+#define HAVE_V_TWO(interface) ((interface) >= PMDA_INTERFACE_2)
+#define HAVE_V_FOUR(interface) ((interface) >= PMDA_INTERFACE_4)
+#define HAVE_V_FIVE(interface) ((interface) >= PMDA_INTERFACE_5)
+#define HAVE_V_SIX(interface) ((interface) >= PMDA_INTERFACE_6)
+#define HAVE_ANY(interface) ((interface) <= PMDA_INTERFACE_6 && HAVE_V_TWO(interface))
+
+/*
+ * Auxilliary structure used to save data from pmdaDSO or pmdaDaemon and
+ * make it available to the other methods, also as private per PMDA data
+ * when multiple DSO PMDAs are in use
+ */
+typedef struct {
+ int pmda_interface;
+ pmResult *res; /* high-water allocation for */
+ int maxnpmids; /* pmResult for each PMDA */
+ __pmHashCtl hashpmids; /* hashed metrictab lookups */
+} e_ext_t;
+
+#endif /* LIBDEFS_H */
diff --git a/src/libpcp_pmda/src/mainloop.c b/src/libpcp_pmda/src/mainloop.c
new file mode 100644
index 0000000..7b6b776
--- /dev/null
+++ b/src/libpcp_pmda/src/mainloop.c
@@ -0,0 +1,491 @@
+/*
+ * Copyright (c) 2013 Red Hat.
+ * Copyright (c) 1995-2000 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This library 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 Lesser General Public
+ * License for more details.
+ */
+
+#include "pmapi.h"
+#include "impl.h"
+#include "pmda.h"
+#include "libdefs.h"
+
+int
+__pmdaInFd(pmdaInterface *dispatch)
+{
+ if (HAVE_ANY(dispatch->comm.pmda_interface))
+ return dispatch->version.any.ext->e_infd;
+
+ __pmNotifyErr(LOG_CRIT, "PMDA interface version %d not supported",
+ dispatch->comm.pmda_interface);
+ return -1;
+}
+
+int
+__pmdaMainPDU(pmdaInterface *dispatch)
+{
+ __pmPDU *pb;
+ int sts;
+ int op_sts;
+ pmID pmid;
+ pmDesc desc;
+ int npmids;
+ pmID *pmidlist;
+ char **namelist = NULL;
+ char *name;
+ char **offspring = NULL;
+ int *statuslist = NULL;
+ int subtype;
+ pmResult *result;
+ int ctxnum;
+ int length;
+ __pmTimeval when;
+ int ident;
+ int type;
+ pmInDom indom;
+ int inst;
+ char *iname;
+ __pmInResult *inres;
+ char *buffer;
+ __pmProfile *new_profile;
+ static __pmProfile *profile = NULL;
+ static int first_time = 1;
+ static pmdaExt *pmda = NULL;
+ int pinpdu;
+
+ /* Initial version checks */
+ if (first_time) {
+ if (dispatch->status != 0) {
+ __pmNotifyErr(LOG_ERR, "PMDA Initialisation Failed");
+ return -1;
+ }
+ if (!HAVE_ANY(dispatch->comm.pmda_interface)) {
+ __pmNotifyErr(LOG_CRIT, "PMDA interface version %d not supported",
+ dispatch->comm.pmda_interface);
+ return -1;
+ }
+ pmda = dispatch->version.any.ext;
+ dispatch->comm.pmapi_version = PMAPI_VERSION;
+ first_time = 0;
+ }
+
+ pinpdu = sts = __pmGetPDU(pmda->e_infd, ANY_SIZE, TIMEOUT_NEVER, &pb);
+ if (sts == 0)
+ return PM_ERR_EOF;
+ if (sts < 0) {
+ __pmNotifyErr(LOG_ERR, "IPC Error: %s\n", pmErrStr(sts));
+ return sts;
+ }
+
+ if (HAVE_V_FIVE(dispatch->comm.pmda_interface)) {
+ /* set up sender context */
+ __pmPDUHdr *php = (__pmPDUHdr *)pb;
+ /* ntohl() converted already in __pmGetPDU() */
+ dispatch->version.four.ext->e_context = php->from;
+ }
+
+ /*
+ * if defined, callback once per PDU to check availability, etc.
+ */
+ if (pmda->e_checkCallBack) {
+ op_sts = (*(pmda->e_checkCallBack))();
+ if (op_sts < 0) {
+ if (sts != PDU_PROFILE)
+ /* all other PDUs expect an ACK */
+ __pmSendError(pmda->e_outfd, FROM_ANON, op_sts);
+ __pmUnpinPDUBuf(pb);
+ return 0;
+ }
+ }
+
+ switch (sts) {
+ case PDU_ERROR:
+ /*
+ * If __pmDecodeError() fails, just ignore it as no response PDU
+ * is required nor expected.
+ * Expect PM_ERR_NOTCONN to mark client context being closed.
+ */
+ if (__pmDecodeError(pb, &op_sts) >= 0) {
+ if (op_sts == PM_ERR_NOTCONN) {
+ if (HAVE_V_FIVE(dispatch->comm.pmda_interface)) {
+#ifdef PCP_DEBUG
+ if (pmDebug & DBG_TRACE_CONTEXT) {
+ __pmNotifyErr(LOG_DEBUG, "Received PDU_ERROR (end context %d)\n", dispatch->version.four.ext->e_context);
+ }
+#endif
+ if (pmda->e_endCallBack != NULL) {
+ (*(pmda->e_endCallBack))(dispatch->version.four.ext->e_context);
+ }
+ }
+ }
+ else {
+ __pmNotifyErr(LOG_ERR,
+ "%s: unexpected error pdu from pmcd: %s?\n",
+ pmda->e_name, pmErrStr(op_sts));
+ }
+ }
+ break;
+
+ case PDU_PROFILE:
+ /*
+ * can ignore ctxnum, since pmcd has already used this to send
+ * the correct profile, if required
+ */
+
+#ifdef PCP_DEBUG
+ if (pmDebug & DBG_TRACE_LIBPMDA) {
+ __pmNotifyErr(LOG_DEBUG, "Received PDU_PROFILE\n");
+ }
+#endif
+
+ /*
+ * free last profile received (if any)
+ * Note error responses are not sent for PDU_PROFILE
+ */
+ if (__pmDecodeProfile(pb, &ctxnum, &new_profile) < 0)
+ break;
+
+ sts = dispatch->version.any.profile(new_profile, pmda);
+ if (sts < 0) {
+ __pmFreeProfile(new_profile);
+ } else {
+ __pmFreeProfile(profile);
+ profile = new_profile;
+ }
+ break;
+
+ case PDU_FETCH:
+ /*
+ * can ignore ctxnum, since pmcd has already used this to send
+ * the correct profile, if required
+ */
+
+#ifdef PCP_DEBUG
+ if (pmDebug & DBG_TRACE_LIBPMDA) {
+ __pmNotifyErr(LOG_DEBUG, "Received PDU_FETCH\n");
+ }
+#endif
+
+ sts = __pmDecodeFetch(pb, &ctxnum, &when, &npmids, &pmidlist);
+ if (sts >= 0) {
+ sts = dispatch->version.any.fetch(npmids, pmidlist, &result, pmda);
+ __pmUnpinPDUBuf(pmidlist);
+ }
+ if (sts < 0)
+ __pmSendError(pmda->e_outfd, FROM_ANON, sts);
+ else {
+ /* this is for PURIFY to prevent a UMR in __pmXmitPDU */
+ result->timestamp.tv_sec = 0;
+ result->timestamp.tv_usec = 0;
+ __pmSendResult(pmda->e_outfd, FROM_ANON, result);
+ (pmda->e_resultCallBack)(result);
+ }
+ break;
+
+ case PDU_PMNS_NAMES:
+
+#ifdef PCP_DEBUG
+ if (pmDebug & DBG_TRACE_LIBPMDA) {
+ __pmNotifyErr(LOG_DEBUG, "Received PDU_PMNS_NAMES\n");
+ }
+#endif
+
+ if ((sts = __pmDecodeNameList(pb, &npmids, &namelist, NULL)) >= 0) {
+ if (HAVE_V_FOUR(dispatch->comm.pmda_interface)) {
+ if (npmids != 1)
+ /*
+ * expect only one name at a time to be sent to the
+ * pmda from pmcd
+ */
+ sts = PM_ERR_IPC;
+ else
+ sts = dispatch->version.four.pmid(namelist[0], &pmid, pmda);
+ }
+ else {
+ /* Not INTERFACE_4 or later */
+ sts = PM_ERR_NAME;
+ }
+ __pmUnpinPDUBuf(namelist);
+ }
+ if (sts < 0)
+ __pmSendError(pmda->e_outfd, FROM_ANON, sts);
+ else
+ __pmSendIDList(pmda->e_outfd, FROM_ANON, 1, &pmid, sts);
+ break;
+
+ case PDU_PMNS_CHILD:
+
+#ifdef PCP_DEBUG
+ if (pmDebug & DBG_TRACE_LIBPMDA) {
+ __pmNotifyErr(LOG_DEBUG, "Received PDU_PMNS_CHILD\n");
+ }
+#endif
+
+ if ((sts = __pmDecodeChildReq(pb, &name, &subtype)) >= 0) {
+ if (HAVE_V_FOUR(dispatch->comm.pmda_interface)) {
+ sts = dispatch->version.four.children(name, 0, &offspring, &statuslist, pmda);
+ if (sts >= 0) {
+ if (subtype == 0) {
+ if (statuslist) free(statuslist);
+ statuslist = NULL;
+ }
+ }
+ }
+ else {
+ /* Not INTERFACE_4 */
+ sts = PM_ERR_NAME;
+ }
+ }
+ if (sts < 0)
+ __pmSendError(pmda->e_outfd, FROM_ANON, sts);
+ else
+ __pmSendNameList(pmda->e_outfd, FROM_ANON, sts, offspring, statuslist);
+ if (offspring) free(offspring);
+ if (statuslist) free(statuslist);
+ break;
+
+ case PDU_PMNS_TRAVERSE:
+
+#ifdef PCP_DEBUG
+ if (pmDebug & DBG_TRACE_LIBPMDA) {
+ __pmNotifyErr(LOG_DEBUG, "Received PDU_PMNS_TRAVERSE\n");
+ }
+#endif
+
+ if ((sts = __pmDecodeTraversePMNSReq(pb, &name)) >= 0) {
+ if (HAVE_V_FOUR(dispatch->comm.pmda_interface)) {
+ sts = dispatch->version.four.children(name, 1, &offspring, &statuslist, pmda);
+ if (sts >= 0) {
+ if (statuslist) free(statuslist);
+ statuslist = NULL;
+ }
+ }
+ else {
+ /* Not INTERFACE_4 */
+ sts = PM_ERR_NAME;
+ }
+ free(name);
+ }
+ if (sts < 0)
+ __pmSendError(pmda->e_outfd, FROM_ANON, sts);
+ else
+ __pmSendNameList(pmda->e_outfd, FROM_ANON, sts, offspring, NULL);
+ if (offspring) free(offspring);
+ break;
+
+ case PDU_PMNS_IDS:
+
+#ifdef PCP_DEBUG
+ if (pmDebug & DBG_TRACE_LIBPMDA) {
+ __pmNotifyErr(LOG_DEBUG, "Received PDU_PMNS_IDS\n");
+ }
+#endif
+
+ sts = __pmDecodeIDList(pb, 1, &pmid, &op_sts);
+ if (sts >= 0)
+ sts = op_sts;
+ if (sts >= 0) {
+ if (HAVE_V_FOUR(dispatch->comm.pmda_interface)) {
+ sts = dispatch->version.four.name(pmid, &namelist, pmda);
+ }
+ else {
+ /* Not INTERFACE_4 */
+ sts = PM_ERR_PMID;
+ }
+ }
+ if (sts < 0)
+ __pmSendError(pmda->e_outfd, FROM_ANON, sts);
+ else
+ __pmSendNameList(pmda->e_outfd, FROM_ANON, sts, namelist, NULL);
+ if (namelist) free(namelist);
+ break;
+
+ case PDU_DESC_REQ:
+
+#ifdef PCP_DEBUG
+ if (pmDebug & DBG_TRACE_LIBPMDA) {
+ __pmNotifyErr(LOG_DEBUG, "Received PDU_DESC_REQ\n");
+ }
+#endif
+
+ if ((sts = __pmDecodeDescReq(pb, &pmid)) >= 0)
+ sts = dispatch->version.any.desc(pmid, &desc, pmda);
+ if (sts < 0)
+ __pmSendError(pmda->e_outfd, FROM_ANON, sts);
+ else
+ __pmSendDesc(pmda->e_outfd, FROM_ANON, &desc);
+ break;
+
+ case PDU_INSTANCE_REQ:
+
+#ifdef PCP_DEBUG
+ if (pmDebug & DBG_TRACE_LIBPMDA) {
+ __pmNotifyErr(LOG_DEBUG, "Received PDU_INSTANCE_REQ\n");
+ }
+#endif
+
+ if ((sts = __pmDecodeInstanceReq(pb, &when, &indom, &inst, &iname)) >= 0)
+ sts = dispatch->version.any.instance(indom, inst, iname, &inres, pmda);
+ if (sts < 0)
+ __pmSendError(pmda->e_outfd, FROM_ANON, sts);
+ else {
+ __pmSendInstance(pmda->e_outfd, FROM_ANON, inres);
+ __pmFreeInResult(inres);
+ }
+ if (iname)
+ free(iname);
+ break;
+
+ case PDU_TEXT_REQ:
+
+#ifdef PCP_DEBUG
+ if (pmDebug & DBG_TRACE_LIBPMDA) {
+ __pmNotifyErr(LOG_DEBUG, "Received PDU_TEXT_REQ\n");
+ }
+#endif
+
+ if ((sts = __pmDecodeTextReq(pb, &ident, &type)) >= 0)
+ sts = dispatch->version.any.text(ident, type, &buffer, pmda);
+ if (sts < 0)
+ __pmSendError(pmda->e_outfd, FROM_ANON, sts);
+ else {
+ __pmSendText(pmda->e_outfd, FROM_ANON, ident, buffer);
+ }
+ break;
+
+ case PDU_RESULT:
+
+#ifdef PCP_DEBUG
+ if (pmDebug & DBG_TRACE_LIBPMDA) {
+ __pmNotifyErr(LOG_DEBUG, "Received PDU_RESULT\n");
+ }
+#endif
+
+ if ((sts = __pmDecodeResult(pb, &result)) >= 0)
+ sts = dispatch->version.any.store(result, pmda);
+ __pmSendError(pmda->e_outfd, FROM_ANON, sts);
+ pmFreeResult(result);
+ break;
+
+ case PDU_CONTROL_REQ:
+#ifdef PCP_DEBUG
+ if (pmDebug & DBG_TRACE_LIBPMDA) {
+ __pmNotifyErr(LOG_DEBUG, "Received PDU_CONTROL_REQ\n");
+ }
+#endif
+ break;
+
+ case PDU_AUTH:
+#ifdef PCP_DEBUG
+ if (pmDebug & DBG_TRACE_LIBPMDA)
+ __pmNotifyErr(LOG_DEBUG, "Received PDU_AUTH\n");
+#endif
+ if (__pmDecodeAuth(pb, &subtype, &buffer, &length) < 0)
+ break;
+ if (HAVE_V_SIX(dispatch->comm.pmda_interface)) {
+ ctxnum = dispatch->version.six.ext->e_context;
+ sts = dispatch->version.six.attribute(ctxnum, subtype, buffer, length, pmda);
+ } else {
+ sts = PM_ERR_GENERIC;
+ }
+ break;
+
+ default: {
+ char strbuf[20];
+ __pmNotifyErr(LOG_ERR,
+ "%s: Unrecognised pdu type: %s?\n",
+ pmda->e_name, __pmPDUTypeStr_r(sts, strbuf, sizeof(strbuf)));
+ }
+ break;
+ }
+
+ if (pinpdu > 0)
+ __pmUnpinPDUBuf(pb);
+
+ /*
+ * if defined, callback once per PDU to do termination checks,
+ * stats, etc
+ */
+ if (pmda->e_doneCallBack)
+ (*(pmda->e_doneCallBack))();
+
+ return 0;
+}
+
+
+void
+pmdaMain(pmdaInterface *dispatch)
+{
+ for ( ; ; ) {
+ if (__pmdaMainPDU(dispatch) < 0)
+ break;
+ }
+}
+
+void
+pmdaSetResultCallBack(pmdaInterface *dispatch, pmdaResultCallBack callback)
+{
+ if (HAVE_ANY(dispatch->comm.pmda_interface))
+ dispatch->version.any.ext->e_resultCallBack = callback;
+ else {
+ __pmNotifyErr(LOG_CRIT, "Unable to set result callback for PMDA interface version %d.",
+ dispatch->comm.pmda_interface);
+ dispatch->status = PM_ERR_GENERIC;
+ }
+}
+
+void
+pmdaSetEndContextCallBack(pmdaInterface *dispatch, pmdaEndContextCallBack callback)
+{
+ if (HAVE_V_FIVE(dispatch->comm.pmda_interface) || callback == NULL)
+ dispatch->version.four.ext->e_endCallBack = callback;
+ else {
+ __pmNotifyErr(LOG_CRIT, "Unable to set end context callback for PMDA interface version %d.",
+ dispatch->comm.pmda_interface);
+ dispatch->status = PM_ERR_GENERIC;
+ }
+}
+
+void
+pmdaSetFetchCallBack(pmdaInterface *dispatch, pmdaFetchCallBack callback)
+{
+ if (HAVE_ANY(dispatch->comm.pmda_interface))
+ dispatch->version.any.ext->e_fetchCallBack = callback;
+ else {
+ __pmNotifyErr(LOG_CRIT, "Unable to set fetch callback for PMDA interface version %d.",
+ dispatch->comm.pmda_interface);
+ dispatch->status = PM_ERR_GENERIC;
+ }
+}
+
+void
+pmdaSetCheckCallBack(pmdaInterface *dispatch, pmdaCheckCallBack callback)
+{
+ if (HAVE_ANY(dispatch->comm.pmda_interface))
+ dispatch->version.any.ext->e_checkCallBack = callback;
+ else {
+ __pmNotifyErr(LOG_CRIT, "Unable to set check callback for PMDA interface version %d.",
+ dispatch->comm.pmda_interface);
+ dispatch->status = PM_ERR_GENERIC;
+ }
+}
+
+void
+pmdaSetDoneCallBack(pmdaInterface *dispatch, pmdaDoneCallBack callback)
+{
+ if (HAVE_ANY(dispatch->comm.pmda_interface))
+ dispatch->version.any.ext->e_doneCallBack = callback;
+ else {
+ __pmNotifyErr(LOG_CRIT, "Unable to set done callback for PMDA interface version %d.",
+ dispatch->comm.pmda_interface);
+ dispatch->status = PM_ERR_GENERIC;
+ }
+}
diff --git a/src/libpcp_pmda/src/open.c b/src/libpcp_pmda/src/open.c
new file mode 100644
index 0000000..3150c20
--- /dev/null
+++ b/src/libpcp_pmda/src/open.c
@@ -0,0 +1,987 @@
+/*
+ * Copyright (c) 2012-2014 Red Hat.
+ * Copyright (c) 1995-2000,2003,2004 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This library 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 Lesser General Public
+ * License for more details.
+ */
+
+#include "pmapi.h"
+#include "impl.h"
+#include "pmda.h"
+#include "libdefs.h"
+#ifdef HAVE_SYS_UN_H
+#include <sys/un.h>
+#endif
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+#ifdef HAVE_NETINET_TCP_H
+#include <netinet/tcp.h>
+#endif
+
+/*
+ * Open an inet port to PMCD
+ */
+
+static void
+__pmdaOpenSocket(char *sockname, int port, int family, int *infd, int *outfd)
+{
+ int sts;
+ int sfd;
+ struct servent *service;
+ __pmSockAddr *myaddr;
+ __pmSockLen addrlen;
+ __pmFdSet rfds;
+ int one = 1;
+
+ if (sockname != NULL) { /* Translate port name to port num */
+ service = getservbyname(sockname, NULL);
+ if (service == NULL) {
+ __pmNotifyErr(LOG_CRIT, "__pmdaOpenSocket: getservbyname(%s): %s\n",
+ sockname, netstrerror());
+ exit(1);
+ }
+ port = service->s_port;
+ }
+
+ if (family != AF_INET6) {
+ sfd = __pmCreateSocket();
+ if (sfd < 0) {
+ __pmNotifyErr(LOG_CRIT, "__pmdaOpenSocket: inet socket: %s\n",
+ netstrerror());
+ exit(1);
+ }
+ }
+ else {
+ sfd = __pmCreateIPv6Socket();
+ if (sfd < 0) {
+ __pmNotifyErr(LOG_CRIT, "__pmdaOpenSocket: ipv6 socket: %s\n",
+ netstrerror());
+ exit(1);
+ }
+ }
+#ifndef IS_MINGW
+ /*
+ * allow port to be quickly re-used, e.g. when Install and PMDA already
+ * installed, this becomes terminate and restart in a hurry ...
+ */
+ if (__pmSetSockOpt(sfd, SOL_SOCKET, SO_REUSEADDR, (char *)&one,
+ (__pmSockLen)sizeof(one)) < 0) {
+ __pmNotifyErr(LOG_CRIT, "__pmdaOpenSocket: __pmSetSockOpt(reuseaddr): %s\n",
+ netstrerror());
+ exit(1);
+ }
+#else
+ /* see MSDN tech note: "Using SO_REUSEADDR and SO_EXCLUSIVEADDRUSE" */
+ if (__pmSetSockOpt(sfd, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, (char *)&one,
+ (__pmSockLen)sizeof(one)) < 0) {
+ __pmNotifyErr(LOG_CRIT, "__pmdaOpenSocket: __pmSetSockOpt(excladdruse): %s\n",
+ netstrerror());
+ exit(1);
+ }
+#endif
+
+ if ((myaddr =__pmSockAddrAlloc()) == NULL) {
+ __pmNotifyErr(LOG_CRIT, "__pmdaOpenSocket: sock addr alloc failed\n");
+ exit(1);
+ }
+ __pmSockAddrInit(myaddr, family, INADDR_LOOPBACK, port);
+ sts = __pmBind(sfd, (void *)myaddr, __pmSockAddrSize());
+ if (sts < 0) {
+ __pmSockAddrFree(myaddr);
+ __pmNotifyErr(LOG_CRIT, "__pmdaOpenSocket: bind: %s\n",
+ netstrerror());
+ exit(1);
+ }
+
+ sts = __pmListen(sfd, 1); /* Max. 1 pending connection request (pmcd) */
+ if (sts == -1) {
+ __pmSockAddrFree(myaddr);
+ __pmNotifyErr(LOG_CRIT, "__pmdaOpenSocket: listen: %s\n",
+ netstrerror());
+ exit(1);
+ }
+
+ /* block here indefinitely, waiting for a connection */
+ __pmFD_ZERO(&rfds);
+ __pmFD_SET(sfd, &rfds);
+ if ((sts = __pmSelectRead(sfd+1, &rfds, NULL)) != 1) {
+ sts = (sts < 0) ? -neterror() : -EINVAL;
+ }
+ if (sts < 0) {
+ __pmSockAddrFree(myaddr);
+ __pmNotifyErr(LOG_CRIT, "__pmdaOpenSocket: select: %s\n",
+ pmErrStr(sts));
+ exit(1);
+ }
+
+ addrlen = __pmSockAddrSize();
+ if ((*infd = __pmAccept(sfd, myaddr, &addrlen)) < 0) {
+ __pmSockAddrFree(myaddr);
+ __pmNotifyErr(LOG_CRIT, "__pmdaOpenSocket: accept: %s\n",
+ netstrerror());
+ exit(1);
+ }
+ __pmCloseSocket(sfd);
+ __pmSetSocketIPC(*infd);
+ __pmSockAddrFree(myaddr);
+
+ *outfd = *infd;
+}
+
+static void
+__pmdaOpenInet(char *sockname, int port, int *infd, int *outfd)
+{
+ __pmdaOpenSocket(sockname, port, AF_INET, infd, outfd);
+}
+
+static void
+__pmdaOpenIPv6(char *sockname, int port, int *infd, int *outfd)
+{
+ __pmdaOpenSocket(sockname, port, AF_INET6, infd, outfd);
+}
+
+#ifdef HAVE_STRUCT_SOCKADDR_UN
+/*
+ * Open a unix port to PMCD
+ */
+static void
+__pmdaOpenUnix(char *sockname, int *infd, int *outfd)
+{
+ int sts;
+ int sfd;
+ int len;
+ __pmSockLen addrlen;
+ struct sockaddr_un myaddr;
+ struct sockaddr_un from;
+
+ sfd = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (sfd < 0) {
+ __pmNotifyErr(LOG_CRIT, "__pmdaOpenUnix: Unix domain socket: %s",
+ netstrerror());
+ exit(1);
+ }
+ /* Sockets in the Unix domain are named pipes in the file system.
+ * In case the socket is still hanging around, try to remove it. If it
+ * belonged to someone and is still open, they will still keep the
+ * connection because the reference count is non-zero.
+ */
+ if ((sts = unlink(sockname)) == 0)
+ __pmNotifyErr(LOG_WARNING, "__pmdaOpenUnix: Unix domain socket '%s' existed, unlinked it\n",
+ sockname);
+ else if (sts < 0 && oserror() != ENOENT) {
+ /* If can't unlink socket, give up. We might end up with an
+ * unwanted connection to some other socket (from outer space)
+ */
+ __pmNotifyErr(LOG_CRIT, "__pmdaOpenUnix: Unlinking Unix domain socket '%s': %s\n",
+ sockname, osstrerror());
+ exit(1);
+ }
+ memset(&myaddr, 0, sizeof(myaddr));
+ myaddr.sun_family = AF_UNIX;
+ strcpy(myaddr.sun_path, sockname);
+ len = (int)offsetof(struct sockaddr_un, sun_path) + (int)strlen(myaddr.sun_path);
+ sts = bind(sfd, (struct sockaddr*) &myaddr, len);
+ if (sts < 0) {
+ __pmNotifyErr(LOG_CRIT, "__pmdaOpenUnix: unix bind: %s\n",
+ netstrerror());
+ exit(1);
+ }
+
+ sts = listen(sfd, 5); /* Max. of 5 pending connection requests */
+ if (sts == -1) {
+ __pmNotifyErr(LOG_CRIT, "__pmdaOpenUnix: unix listen: %s\n",
+ netstrerror());
+ exit(1);
+ }
+ addrlen = sizeof(from);
+ /* block here, waiting for a connection */
+ if ((*infd = accept(sfd, (struct sockaddr *)&from, &addrlen)) < 0) {
+ __pmNotifyErr(LOG_CRIT, "__pmdaOpenUnix: unix accept: %s\n",
+ netstrerror());
+ exit(1);
+ }
+ close(sfd);
+ *outfd = *infd;
+}
+#else
+static void
+__pmdaOpenUnix(char *sockname, int *infd, int *outfd)
+{
+ __pmNotifyErr(LOG_CRIT, "__pmdaOpenUnix: UNIX domain sockets unsupported\n");
+ exit(1);
+}
+#endif
+
+/*
+ * Capture PMDA args using pmgetopts_r
+ */
+
+static char
+pmdaIoTypeToOption(pmdaIoType io)
+{
+ switch(io) {
+ case pmdaPipe:
+ return 'p';
+ case pmdaInet:
+ return 'i';
+ case pmdaIPv6:
+ return '6';
+ case pmdaUnix:
+ return 'u';
+ case pmdaUnknown:
+ default:
+ break;
+ }
+ return '?';
+}
+
+/*
+ * Backwards compatibility interface, short option support only.
+ * We override username (-U) to preserve backward-compatibility
+ * with the original pmdaGetOpt interface, which does not know
+ * about that option (and uses of -U have been observed in the
+ * wild). Happily, pmdaGetOptions gives us a much more flexible
+ * route forward.
+ */
+
+static int
+username_override(int opt, pmdaOptions *opts)
+{
+ (void)opts;
+ return opt == 'U';
+}
+
+int
+pmdaGetOpt(int argc, char *const *argv, const char *optstring, pmdaInterface *dispatch, int *err)
+{
+ int sts;
+ static pmdaOptions opts;
+
+ opts.flags |= PM_OPTFLAG_POSIX;
+ opts.short_options = optstring;
+ opts.override = username_override;
+ opts.errors = 0;
+
+ sts = pmdaGetOptions(argc, argv, &opts, dispatch);
+
+ optind = opts.optind;
+ opterr = opts.opterr;
+ optopt = opts.optopt;
+ optarg = opts.optarg;
+ *err += opts.errors;
+ return sts;
+}
+
+/*
+ * New, prefered interface - supports long and short options, allows
+ * caller to select whether POSIX style options are required. Also,
+ * handles the common -U,--username option setting automatically.
+ */
+int
+pmdaGetOptions(int argc, char *const *argv, pmdaOptions *opts, pmdaInterface *dispatch)
+{
+ int c = EOF;
+ int flag = 0;
+ char *endnum = NULL;
+ pmdaExt *pmda = NULL;
+
+ if (dispatch->status != 0) {
+ opts->errors++;
+ return EOF;
+ }
+
+ if (!HAVE_ANY(dispatch->comm.pmda_interface)) {
+ __pmNotifyErr(LOG_CRIT, "pmdaGetOptions: "
+ "PMDA interface version %d not supported (domain=%d)",
+ dispatch->comm.pmda_interface, dispatch->domain);
+ opts->errors++;
+ return EOF;
+ }
+
+ pmda = dispatch->version.any.ext;
+
+ /*
+ * We only support one version of pmdaOptions structure so far - tell
+ * the caller the struct size/fields we will be using (version zero),
+ * in case they are of later version (newer PMDA, older library).
+ *
+ * The pmOptions and pmdaOptions structures share initial pmgetopts_r
+ * fields, hence the cast below (and sharing of pmgetopt_r itself).
+ *
+ * So far, the only PMDA-specific field is from --username although,
+ * of course, more may be added in the future (bumping version as we
+ * go of course, and observing the version number the PMDA passes in).
+ */
+ opts->version = 0;
+
+ while (!flag && ((c = pmgetopt_r(argc, argv, (pmOptions *)opts)) != EOF)) {
+ int sts;
+
+ /* provide opportunity for overriding the general set of options */
+ if (opts->override && opts->override(c, opts))
+ break;
+
+ switch (c) {
+ case 'd':
+ dispatch->domain = (int)strtol(opts->optarg, &endnum, 10);
+ if (*endnum != '\0') {
+ pmprintf("%s: -d requires numeric domain number\n",
+ pmda->e_name);
+ opts->errors++;
+ }
+ pmda->e_domain = dispatch->domain;
+ break;
+
+ case 'D':
+ if ((sts = __pmParseDebug(opts->optarg)) < 0) {
+ pmprintf("%s: unrecognized debug flag specification (%s)\n",
+ pmda->e_name, opts->optarg);
+ opts->errors++;
+ } else {
+ pmDebug |= sts;
+ }
+ break;
+
+ case 'h': /* over-ride default help file */
+ pmda->e_helptext = opts->optarg;
+ break;
+
+ case 'i':
+ if (pmda->e_io != pmdaUnknown && pmda->e_io != pmdaInet) {
+ pmprintf("%s: -i option clashes with -%c option\n",
+ pmda->e_name, pmdaIoTypeToOption(pmda->e_io));
+ opts->errors++;
+ } else {
+ pmda->e_io = pmdaInet;
+ pmda->e_port = (int)strtol(opts->optarg, &endnum, 10);
+ if (*endnum != '\0')
+ pmda->e_sockname = opts->optarg;
+ }
+ break;
+
+ case 'l': /* over-ride default log file */
+ pmda->e_logfile = opts->optarg;
+ break;
+
+ case 'p':
+ if (pmda->e_io != pmdaUnknown && pmda->e_io != pmdaPipe) {
+ pmprintf("%s: -p option clashes with -%c option\n",
+ pmda->e_name, pmdaIoTypeToOption(pmda->e_io));
+ opts->errors++;
+ } else {
+ pmda->e_io = pmdaPipe;
+ }
+ break;
+
+ case 'u':
+ if (pmda->e_io != pmdaUnknown && pmda->e_io != pmdaUnix) {
+ pmprintf("%s: -u option clashes with -%c option\n",
+ pmda->e_name, pmdaIoTypeToOption(pmda->e_io));
+ opts->errors++;
+ } else {
+ pmda->e_io = pmdaUnix;
+ pmda->e_sockname = opts->optarg;
+ }
+ break;
+
+ case '6':
+ if (pmda->e_io != pmdaUnknown && pmda->e_io != pmdaIPv6) {
+ pmprintf("%s: -6 option clashes with -%c option\n",
+ pmda->e_name, pmdaIoTypeToOption(pmda->e_io));
+ opts->errors++;
+ } else {
+ pmda->e_io = pmdaIPv6;
+ pmda->e_port = (int)strtol(opts->optarg, &endnum, 10);
+ if (*endnum != '\0')
+ pmda->e_sockname = opts->optarg;
+ }
+ break;
+
+ case 'U':
+ opts->username = opts->optarg;
+ break;
+
+ case '?':
+ opts->errors++;
+ break;
+
+ default:
+ flag = 1;
+ }
+ }
+
+ return c;
+}
+
+void
+pmdaUsageMessage(pmdaOptions *opts)
+{
+ pmUsageMessage((pmOptions *)opts);
+}
+
+static __pmHashWalkState
+pmdaHashNodeDelete(const __pmHashNode *tp, void *cp)
+{
+ (void)tp;
+ (void)cp;
+ return PM_HASH_WALK_DELETE_NEXT;
+}
+
+static void
+pmdaHashDelete(__pmHashCtl *hashp)
+{
+ __pmHashWalkCB(pmdaHashNodeDelete, NULL, hashp);
+ __pmHashClear(hashp);
+}
+
+/*
+ * Recompute the hash table which maps metric PMIDs to metric table
+ * offsets. Provides an optimised lookup alternative when a direct
+ * mapping is inappropriate or impossible.
+ */
+void
+pmdaRehash(pmdaExt *pmda, pmdaMetric *metrics, int nmetrics)
+{
+ e_ext_t *extp = (e_ext_t *)pmda->e_ext;
+ __pmHashCtl *hashp = &extp->hashpmids;
+ pmdaMetric *metric;
+ char buf[32];
+ int m;
+
+ pmda->e_direct = 0;
+ pmda->e_metrics = metrics;
+ pmda->e_nmetrics = nmetrics;
+
+ pmdaHashDelete(hashp);
+ for (m = 0; m < pmda->e_nmetrics; m++) {
+ metric = &pmda->e_metrics[m];
+
+ if (__pmHashAdd(metric->m_desc.pmid, metric, hashp) < 0) {
+ __pmNotifyErr(LOG_WARNING, "pmdaRehash: PMDA %s: "
+ "Hashed mapping for metrics disabled @ metric[%d] %s\n",
+ pmda->e_name, m,
+ pmIDStr_r(metric->m_desc.pmid, buf, sizeof(buf)));
+ break;
+ }
+ }
+ if (m == pmda->e_nmetrics)
+ pmda->e_flags |= PMDA_EXT_FLAG_HASHED;
+ if (pmDebug & DBG_TRACE_LIBPMDA)
+ __pmNotifyErr(LOG_DEBUG, "pmdaRehash: PMDA %s: successful rebuild\n",
+ pmda->e_name);
+ else {
+ pmda->e_flags &= ~PMDA_EXT_FLAG_HASHED;
+ pmdaHashDelete(hashp);
+ }
+}
+
+static void
+pmdaDirect(pmdaExt *pmda, pmdaMetric *metrics, int nmetrics)
+{
+ __pmID_int *pmidp;
+ char buf[20];
+ int m;
+
+ pmda->e_direct = 1;
+ for (m = 0; m < pmda->e_nmetrics; m++) {
+ pmidp = (__pmID_int *)&pmda->e_metrics[m].m_desc.pmid;
+
+ if (pmidp->item == m)
+ continue;
+
+ pmda->e_direct = 0;
+ if ((pmda->e_flags & PMDA_EXT_FLAG_DIRECT) ||
+ (pmDebug & DBG_TRACE_LIBPMDA))
+ __pmNotifyErr(LOG_WARNING, "pmdaDirect: PMDA %s: "
+ "Direct mapping for metrics disabled @ metrics[%d] %s\n",
+ pmda->e_name, m,
+ pmIDStr_r(pmda->e_metrics[m].m_desc.pmid, buf, sizeof(buf)));
+ break;
+ }
+}
+
+void
+pmdaSetFlags(pmdaInterface *dispatch, int flags)
+{
+ pmdaExt *pmda;
+
+ if (!HAVE_ANY(dispatch->comm.pmda_interface)) {
+ __pmNotifyErr(LOG_CRIT, "pmdaSetFlags: PMDA interface version %d not supported (domain=%d)",
+ dispatch->comm.pmda_interface, dispatch->domain);
+ dispatch->status = PM_ERR_GENERIC;
+ return;
+ }
+ pmda = dispatch->version.any.ext;
+ pmda->e_flags |= flags;
+}
+
+/*
+ * Open the help text file, check for direct mapping into the metric table
+ * and whether a hash mapping has been requested.
+ */
+
+void
+pmdaInit(pmdaInterface *dispatch, pmdaIndom *indoms, int nindoms,
+ pmdaMetric *metrics, int nmetrics)
+{
+ int m = 0;
+ int i = 0;
+ __pmInDom_int *indomp = NULL;
+ __pmInDom_int *mindomp = NULL;
+ __pmID_int *pmidp = NULL;
+ pmdaExt *pmda = NULL;
+
+ if (!HAVE_ANY(dispatch->comm.pmda_interface)) {
+ __pmNotifyErr(LOG_CRIT, "pmdaInit: PMDA interface version %d not supported (domain=%d)",
+ dispatch->comm.pmda_interface, dispatch->domain);
+ dispatch->status = PM_ERR_GENERIC;
+ return;
+ }
+ pmda = dispatch->version.any.ext;
+
+ if (dispatch->version.any.fetch == pmdaFetch &&
+ pmda->e_fetchCallBack == (pmdaFetchCallBack)0) {
+ __pmNotifyErr(LOG_CRIT, "pmdaInit: PMDA %s: using pmdaFetch() but fetch call back not set", pmda->e_name);
+ dispatch->status = PM_ERR_GENERIC;
+ return;
+ }
+
+ /* parameter sanity checks */
+ if (nmetrics < 0) {
+ __pmNotifyErr(LOG_CRIT, "pmdaInit: PMDA %s: nmetrics (%d) should be non-negative", pmda->e_name, nmetrics);
+ dispatch->status = PM_ERR_GENERIC;
+ return;
+ }
+ if (nindoms < 0) {
+ __pmNotifyErr(LOG_CRIT, "pmdaInit: PMDA %s: nindoms (%d) should be non-negative", pmda->e_name, nindoms);
+ dispatch->status = PM_ERR_GENERIC;
+ return;
+ }
+ if ((nmetrics == 0 && metrics != NULL) ||
+ (nmetrics != 0 && metrics == NULL)){
+ __pmNotifyErr(LOG_CRIT, "pmdaInit: PMDA %s: metrics not consistent with nmetrics", pmda->e_name);
+ dispatch->status = PM_ERR_GENERIC;
+ return;
+ }
+ if ((nindoms == 0 && indoms != NULL) ||
+ (nindoms != 0 && indoms == NULL)){
+ __pmNotifyErr(LOG_CRIT, "pmdaInit: PMDA %s: indoms not consistent with nindoms", pmda->e_name);
+ dispatch->status = PM_ERR_GENERIC;
+ return;
+ }
+
+ pmda->e_indoms = indoms;
+ pmda->e_nindoms = nindoms;
+ pmda->e_metrics = metrics;
+ pmda->e_nmetrics = nmetrics;
+
+ /* fix bit fields in indom for all instance domains */
+ for (i = 0; i < pmda->e_nindoms; i++) {
+ unsigned int domain = dispatch->domain;
+ unsigned int serial = pmda->e_indoms[i].it_indom;
+ pmda->e_indoms[i].it_indom = pmInDom_build(domain, serial);
+ }
+
+ /* fix bit fields in indom for all metrics */
+ for (i = 0; i < pmda->e_nmetrics; i++) {
+ if (pmda->e_metrics[i].m_desc.indom != PM_INDOM_NULL) {
+ unsigned int domain = dispatch->domain;
+ unsigned int serial = pmda->e_metrics[i].m_desc.indom;
+ pmda->e_metrics[i].m_desc.indom = pmInDom_build(domain, serial);
+ }
+ }
+
+ /*
+ * For each metric, check the instance domain serial number is valid
+ */
+ for (m = 0; m < pmda->e_nmetrics; m++) {
+ if (pmda->e_metrics[m].m_desc.indom != PM_INDOM_NULL) {
+ mindomp = (__pmInDom_int *)&(pmda->e_metrics[m].m_desc.indom);
+ if (pmda->e_nindoms > 0) {
+ for (i = 0; i < pmda->e_nindoms; i++) {
+ indomp = (__pmInDom_int *)&(pmda->e_indoms[i].it_indom);
+ if (indomp->serial == mindomp->serial) {
+#ifdef PCP_DEBUG
+ if (pmDebug & DBG_TRACE_LIBPMDA) {
+ char strbuf[20];
+ char st2buf[20];
+ __pmNotifyErr(LOG_DEBUG,
+ "pmdaInit: PMDA %s: Metric %s(%d) matched to indom %s(%d)\n",
+ pmda->e_name,
+ pmIDStr_r(pmda->e_metrics[m].m_desc.pmid, strbuf, sizeof(strbuf)), m,
+ pmInDomStr_r(pmda->e_indoms[i].it_indom, st2buf, sizeof(st2buf)), i);
+ }
+#endif
+ break;
+ }
+ }
+ if (i == pmda->e_nindoms) {
+ char strbuf[20];
+ __pmNotifyErr(LOG_CRIT,
+ "pmdaInit: PMDA %s: Undefined instance domain serial (%d) specified in metric %s(%d)\n",
+ pmda->e_name, mindomp->serial,
+ pmIDStr_r(pmda->e_metrics[m].m_desc.pmid, strbuf, sizeof(strbuf)), m);
+ dispatch->status = PM_ERR_GENERIC;
+ return;
+ }
+
+ }
+ }
+ }
+
+ if (pmda->e_helptext != NULL) {
+ pmda->e_help = pmdaOpenHelp(pmda->e_helptext);
+ if (pmda->e_help < 0) {
+ __pmNotifyErr(LOG_WARNING, "pmdaInit: PMDA %s: Unable to open help text file(s) from \"%s\": %s\n",
+ pmda->e_name, pmda->e_helptext, pmErrStr(pmda->e_help));
+ }
+#ifdef PCP_DEBUG
+ else if (pmDebug & DBG_TRACE_LIBPMDA) {
+ __pmNotifyErr(LOG_DEBUG, "pmdaInit: PMDA %s: help file %s opened\n", pmda->e_name, pmda->e_helptext);
+ }
+#endif
+ }
+ else {
+ if (dispatch->version.two.text == pmdaText)
+ __pmNotifyErr(LOG_WARNING, "pmdaInit: PMDA %s: No help text file specified", pmda->e_name);
+#ifdef PCP_DEBUG
+ else
+ if (pmDebug & DBG_TRACE_LIBPMDA)
+ __pmNotifyErr(LOG_DEBUG, "pmdaInit: PMDA %s: No help text path specified", pmda->e_name);
+#endif
+ }
+
+ /*
+ * Stamp the correct domain number in each of the PMIDs
+ */
+ for (m = 0; m < pmda->e_nmetrics; m++) {
+ pmidp = (__pmID_int *)&pmda->e_metrics[m].m_desc.pmid;
+ pmidp->domain = dispatch->domain;
+ }
+
+ if (pmda->e_flags & PMDA_EXT_FLAG_HASHED)
+ pmdaRehash(pmda, metrics, nmetrics);
+ else
+ pmdaDirect(pmda, metrics, nmetrics);
+
+#ifdef PCP_DEBUG
+ if (pmDebug & DBG_TRACE_LIBPMDA) {
+ __pmNotifyErr(LOG_INFO, "name = %s\n", pmda->e_name);
+ __pmNotifyErr(LOG_INFO, "domain = %d\n", dispatch->domain);
+ if (dispatch->comm.flags)
+ __pmNotifyErr(LOG_INFO, "comm flags = %x\n", dispatch->comm.flags);
+ __pmNotifyErr(LOG_INFO, "ext flags = %x\n", pmda->e_flags);
+ __pmNotifyErr(LOG_INFO, "num metrics = %d\n", pmda->e_nmetrics);
+ __pmNotifyErr(LOG_INFO, "num indom = %d\n", pmda->e_nindoms);
+ __pmNotifyErr(LOG_INFO, "metric map = %s\n",
+ (pmda->e_flags & PMDA_EXT_FLAG_HASHED) ? "hashed" :
+ (pmda->e_direct ? "direct" : "linear"));
+ }
+#endif
+
+ dispatch->status = pmda->e_status;
+}
+
+/*
+ * version exchange with pmcd via credentials PDU
+ */
+
+static int
+__pmdaSetupPDU(int infd, int outfd, int flags, char *agentname)
+{
+ __pmVersionCred handshake;
+ __pmCred *credlist = NULL;
+ __pmPDU *pb;
+ int i, sts, pinpdu, vflag = 0;
+ int version = UNKNOWN_VERSION, credcount = 0, sender = 0;
+
+ handshake.c_type = CVERSION;
+ handshake.c_version = PDU_VERSION;
+ handshake.c_flags = flags;
+ if ((sts = __pmSendCreds(outfd, (int)getpid(), 1, (__pmCred *)&handshake)) < 0) {
+ __pmNotifyErr(LOG_CRIT, "__pmdaSetupPDU: PMDA %s send creds: %s\n", agentname, pmErrStr(sts));
+ return -1;
+ }
+
+ if ((pinpdu = sts = __pmGetPDU(infd, ANY_SIZE, TIMEOUT_DEFAULT, &pb)) < 0) {
+ __pmNotifyErr(LOG_CRIT, "__pmdaSetupPDU: PMDA %s getting creds: %s\n", agentname, pmErrStr(sts));
+ return -1;
+ }
+
+ if (sts == PDU_CREDS) {
+ if ((sts = __pmDecodeCreds(pb, &sender, &credcount, &credlist)) < 0) {
+ __pmNotifyErr(LOG_CRIT, "__pmdaSetupPDU: PMDA %s decode creds: %s\n", agentname, pmErrStr(sts));
+ __pmUnpinPDUBuf(pb);
+ return -1;
+ }
+
+ for (i = 0; i < credcount; i++) {
+ switch (credlist[i].c_type) {
+ case CVERSION:
+ version = credlist[i].c_vala;
+ vflag = 1;
+ break;
+ default:
+ __pmNotifyErr(LOG_WARNING, "__pmdaSetupPDU: PMDA %s: unexpected creds PDU\n", agentname);
+ }
+ }
+ if (vflag) {
+ __pmSetVersionIPC(infd, version);
+ __pmSetVersionIPC(outfd, version);
+ }
+ if (credlist != NULL)
+ free(credlist);
+ }
+ else
+ __pmNotifyErr(LOG_CRIT, "__pmdaSetupPDU: PMDA %s: version exchange failure\n", agentname);
+
+ if (pinpdu > 0)
+ __pmUnpinPDUBuf(pb);
+
+ return version;
+}
+
+/*
+ * set up connection to PMCD
+ */
+
+void
+pmdaConnect(pmdaInterface *dispatch)
+{
+ pmdaExt *pmda = NULL;
+ int sts, flags = dispatch->comm.flags;
+
+ if (dispatch->version.any.ext == NULL ||
+ (dispatch->version.any.ext->e_flags & PMDA_EXT_SETUPDONE) != PMDA_EXT_SETUPDONE) {
+ __pmNotifyErr(LOG_CRIT, "pmdaConnect: need to call pmdaDaemon() or pmdaDSO() first");
+ dispatch->status = PM_ERR_GENERIC;
+ return;
+ }
+
+ if ((dispatch->version.any.ext->e_flags & PMDA_EXT_CONNECTED) == PMDA_EXT_CONNECTED) {
+ __pmNotifyErr(LOG_CRIT, "pmdaConnect: called more than once");
+ dispatch->status = PM_ERR_GENERIC;
+ return;
+ }
+
+ if (!HAVE_ANY(dispatch->comm.pmda_interface)) {
+ __pmNotifyErr(LOG_CRIT, "pmdaConnect: PMDA interface version %d not supported (domain=%d)",
+ dispatch->comm.pmda_interface, dispatch->domain);
+ dispatch->status = PM_ERR_GENERIC;
+ return;
+ }
+ pmda = dispatch->version.any.ext;
+
+ switch (pmda->e_io) {
+ case pmdaPipe:
+ case pmdaUnknown: /* Default */
+
+ pmda->e_infd = fileno(stdin);
+ pmda->e_outfd = fileno(stdout);
+
+#ifdef PCP_DEBUG
+ if (pmDebug & DBG_TRACE_LIBPMDA) {
+ __pmNotifyErr(LOG_DEBUG, "pmdaConnect: PMDA %s: opened pipe to pmcd, infd = %d, outfd = %d\n",
+ pmda->e_name, pmda->e_infd, pmda->e_outfd);
+ }
+#endif
+ break;
+
+ case pmdaInet:
+
+ __pmdaOpenInet(pmda->e_sockname, pmda->e_port, &(pmda->e_infd),
+ &(pmda->e_outfd));
+
+#ifdef PCP_DEBUG
+ if (pmDebug & DBG_TRACE_LIBPMDA) {
+ __pmNotifyErr(LOG_DEBUG, "pmdaConnect: PMDA %s: opened inet connection, infd = %d, outfd = %d\n",
+ pmda->e_name, pmda->e_infd, pmda->e_outfd);
+ }
+#endif
+
+ break;
+
+ case pmdaIPv6:
+
+ __pmdaOpenIPv6(pmda->e_sockname, pmda->e_port, &(pmda->e_infd),
+ &(pmda->e_outfd));
+
+#ifdef PCP_DEBUG
+ if (pmDebug & DBG_TRACE_LIBPMDA) {
+ __pmNotifyErr(LOG_DEBUG, "pmdaConnect: PMDA %s: opened ipv6 connection, infd = %d, outfd = %d\n",
+ pmda->e_name, pmda->e_infd, pmda->e_outfd);
+ }
+#endif
+
+ break;
+
+ case pmdaUnix:
+
+ __pmdaOpenUnix(pmda->e_sockname, &(pmda->e_infd), &(pmda->e_outfd));
+
+#ifdef PCP_DEBUG
+ if (pmDebug & DBG_TRACE_LIBPMDA) {
+ __pmNotifyErr(LOG_DEBUG, "pmdaConnect: PMDA %s: Opened unix connection, infd = %d, outfd = %d\n",
+ pmda->e_name, pmda->e_infd, pmda->e_outfd);
+ }
+#endif
+
+ break;
+ default:
+ __pmNotifyErr(LOG_CRIT, "pmdaConnect: PMDA %s: Illegal iotype: %d\n", pmda->e_name, pmda->e_io);
+ exit(1);
+ }
+
+ sts = __pmdaSetupPDU(pmda->e_infd, pmda->e_outfd, flags, pmda->e_name);
+ if (sts < 0)
+ dispatch->status = sts;
+ else {
+ dispatch->comm.pmapi_version = (unsigned int)sts;
+ pmda->e_flags |= PMDA_EXT_CONNECTED;
+ }
+}
+
+/*
+ * initialise the pmdaExt and pmdaInterface structures for a daemon or DSO PMDA.
+ */
+
+static void
+__pmdaSetup(pmdaInterface *dispatch, int version, char *name)
+{
+ pmdaExt *pmda = NULL;
+ e_ext_t *extp;
+
+ if (!HAVE_ANY(version)) {
+ __pmNotifyErr(LOG_CRIT, "__pmdaSetup: %s PMDA: interface version %d not supported (domain=%d)",
+ name, version, dispatch->domain);
+ dispatch->status = PM_ERR_GENERIC;
+ return;
+ }
+
+ pmda = (pmdaExt *)calloc(1, sizeof(pmdaExt));
+ if (pmda == NULL) {
+ __pmNotifyErr(LOG_ERR,
+ "%s: Unable to allocate memory for pmdaExt structure (%d bytes)",
+ name, (int)sizeof(pmdaExt));
+ dispatch->status = PM_ERR_GENERIC;
+ return;
+ }
+
+ dispatch->status = 0;
+
+ dispatch->comm.pmda_interface = version;
+ dispatch->comm.pmapi_version = PMAPI_VERSION;
+ dispatch->comm.flags = 0;
+
+ if (HAVE_V_SIX(version)) {
+ dispatch->version.six.attribute = pmdaAttribute;
+ }
+ if (HAVE_V_FOUR(version)) {
+ dispatch->version.four.pmid = pmdaPMID;
+ dispatch->version.four.name = pmdaName;
+ dispatch->version.four.children = pmdaChildren;
+ }
+ dispatch->version.any.profile = pmdaProfile;
+ dispatch->version.any.fetch = pmdaFetch;
+ dispatch->version.any.desc = pmdaDesc;
+ dispatch->version.any.instance = pmdaInstance;
+ dispatch->version.any.text = pmdaText;
+ dispatch->version.any.store = pmdaStore;
+ dispatch->version.any.ext = pmda;
+
+ pmda->e_name = name;
+ pmda->e_infd = -1;
+ pmda->e_outfd = -1;
+ pmda->e_port = -1;
+ pmda->e_singular = -1;
+ pmda->e_ordinal = -1;
+ pmda->e_domain = dispatch->domain;
+ pmda->e_help = -1;
+ pmda->e_io = pmdaUnknown;
+ pmda->e_ext = (void *)dispatch;
+ pmda->e_flags |= PMDA_EXT_SETUPDONE;
+
+ extp = (e_ext_t *)calloc(1, sizeof(*extp));
+ if (extp == NULL) {
+ __pmNotifyErr(LOG_ERR,
+ "%s: Unable to allocate memory for e_ext_t structure (%d bytes)",
+ name, (int)sizeof(*extp));
+ free(pmda);
+ dispatch->version.any.ext = NULL;
+ dispatch->status = PM_ERR_GENERIC;
+ return;
+ }
+ extp->pmda_interface = version;
+ pmda->e_ext = (void *)extp;
+
+ pmdaSetResultCallBack(dispatch, __pmFreeResultValues);
+ pmdaSetFetchCallBack(dispatch, (pmdaFetchCallBack)0);
+ pmdaSetCheckCallBack(dispatch, (pmdaCheckCallBack)0);
+ pmdaSetDoneCallBack(dispatch, (pmdaDoneCallBack)0);
+ pmdaSetEndContextCallBack(dispatch, (pmdaEndContextCallBack)0);
+}
+
+/*
+ * initialise the pmdaExt and pmdaInterface structures for a daemon
+ * also set some globals
+ */
+
+void
+pmdaDaemon(pmdaInterface *dispatch, int version, char *name, int domain,
+ char *logfile, char *helptext)
+{
+ pmdaExt *pmda;
+
+ dispatch->domain = domain;
+ __pmdaSetup(dispatch, version, name);
+
+ if (dispatch->status < 0)
+ return;
+
+ pmda = dispatch->version.any.ext;
+ pmda->e_logfile = logfile;
+ pmda->e_helptext = helptext;
+
+ __pmSetInternalState(PM_STATE_PMCS);
+}
+
+/*
+ * initialise the pmdaExt structure for a DSO
+ * also set some globals
+ */
+
+void
+pmdaDSO(pmdaInterface *dispatch, int version, char *name, char *helptext)
+{
+ __pmdaSetup(dispatch, version, name);
+
+ if (dispatch->status < 0)
+ return;
+
+ dispatch->version.any.ext->e_helptext = helptext;
+}
+
+/*
+ * Redirect stderr to the log file
+ */
+
+void
+pmdaOpenLog(pmdaInterface *dispatch)
+{
+ int c;
+
+ if (dispatch->status < 0)
+ return;
+
+ __pmOpenLog(dispatch->version.any.ext->e_name,
+ dispatch->version.any.ext->e_logfile, stderr, &c);
+}
diff --git a/src/libpcp_pmda/src/queues.c b/src/libpcp_pmda/src/queues.c
new file mode 100644
index 0000000..ef01b59
--- /dev/null
+++ b/src/libpcp_pmda/src/queues.c
@@ -0,0 +1,610 @@
+/*
+ * Generic event queue support for PMDAs
+ *
+ * Copyright (c) 2011 Red Hat Inc.
+ * Copyright (c) 2011 Nathan Scott. 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 "pmda.h"
+#include "queues.h"
+#include <ctype.h>
+
+static event_queue_t *queues;
+static int numqueues;
+
+static event_client_t *clients;
+static int numclients;
+static event_client_t *client_lookup(int context);
+
+typedef void (*clientVisitCallBack)(event_clientq_t *, event_queue_t *, void *);
+static void client_iterate(clientVisitCallBack, int, event_queue_t *, void *);
+
+static event_queue_t *
+queue_lookup(int handle)
+{
+ if (handle >= numqueues || handle < 0)
+ return NULL;
+ if (queues[handle].inuse)
+ return &queues[handle];
+ return NULL;
+}
+
+/*
+ * Drop an event after it has been queued (i.e. client was too slow)
+ */
+static void
+queue_drop(event_clientq_t *clientq, event_queue_t *queue, void *data)
+{
+ event_t *event = (event_t *)data;
+
+ if (clientq->last != NULL && clientq->last == event) {
+ clientq->last = TAILQ_NEXT(event, events);
+ clientq->missed++;
+
+ if (pmDebug & DBG_TRACE_LIBPMDA)
+ __pmNotifyErr(LOG_INFO, "Client missed queue %s event %p",
+ queue->name, event);
+ }
+}
+
+static void
+queue_drop_bytes(int handle, event_queue_t *queue, size_t bytes)
+{
+ event_t *event, *next;
+
+ event = TAILQ_FIRST(&queue->tailq);
+ while (event) {
+ if (bytes <= queue->maxmemory - queue->qsize)
+ break;
+ next = TAILQ_NEXT(event, events);
+
+ if (pmDebug & DBG_TRACE_LIBPMDA)
+ __pmNotifyErr(LOG_INFO, "Dropping %s: e=%p sz=%d max=%d qsz=%d",
+ queue->name, event, (int)event->size,
+ (int)queue->maxmemory, (int)queue->qsize);
+
+ /* Walk clients - if event last seen, drop it and bump missed count */
+ client_iterate(queue_drop, handle, queue, event);
+
+ if (pmDebug & DBG_TRACE_LIBPMDA)
+ __pmNotifyErr(LOG_INFO, "Removing %s event %p (%d bytes)",
+ queue->name, event, (int)event->size);
+
+ TAILQ_REMOVE(&queue->tailq, event, events);
+ queue->qsize -= event->size;
+ free(event);
+ event = next;
+ }
+}
+
+int
+pmdaEventNewActiveQueue(const char *name, size_t maxmemory, unsigned int numclients)
+{
+ event_queue_t *queue;
+ size_t size;
+ int i;
+
+ if (name == NULL || maxmemory <= 0)
+ return -EINVAL;
+
+ for (i = 0; i < numqueues; i++)
+ if (queues[i].inuse && strcmp(queues[i].name, name) == 0)
+ return -EEXIST;
+
+ for (i = 0; i < numqueues; i++)
+ if (queues[i].inuse == 0)
+ break;
+ if (i == numqueues) {
+ /*
+ * No free slots - extend the available set.
+ * realloc() potential moves "queues" address, fix up
+ * must tear down existing queues which may have back
+ * references and then re-initialise them afterward.
+ */
+ for (i = 0; i < numqueues; i++)
+ queue_drop_bytes(i, &queues[i], INT_MAX);
+ size = (numqueues + 1) * sizeof(event_queue_t);
+ queues = realloc(queues, size);
+ if (!queues)
+ __pmNoMem("pmdaEventNewQueue", size, PM_FATAL_ERR);
+ /* realloc moves tailq tqh_last pointer - reset 'em */
+ for (i = 0; i < numqueues; i++)
+ TAILQ_INIT(&queues[i].tailq);
+ numqueues++;
+ }
+
+ /* "i" now indexes into a free slot */
+ queue = &queues[i];
+ memset(queue, 0, sizeof(*queue));
+ TAILQ_INIT(&queue->tailq);
+ queue->eventarray = pmdaEventNewArray();
+ queue->numclients = numclients;
+ queue->maxmemory = maxmemory;
+ queue->inuse = 1;
+ queue->name = name;
+ return i;
+}
+
+int
+pmdaEventNewQueue(const char *name, size_t maxmemory)
+{
+ return pmdaEventNewActiveQueue(name, maxmemory, 0);
+}
+
+int
+pmdaEventQueueHandle(const char *name)
+{
+ int i;
+
+ for (i = 0; i < numqueues; i++)
+ if (queues[i].inuse && strcmp(queues[i].name, name) == 0)
+ return i;
+ return -ESRCH;
+}
+
+int
+pmdaEventQueueCounter(int handle, pmAtomValue *atom)
+{
+ event_queue_t *queue = queue_lookup(handle);
+
+ if (!queue)
+ return -EINVAL;
+ atom->ul = queue->count;
+ return PMDA_FETCH_STATIC;
+}
+
+int
+pmdaEventQueueClients(int handle, pmAtomValue *atom)
+{
+ event_queue_t *queue = queue_lookup(handle);
+
+ if (!queue)
+ return -EINVAL;
+ atom->ul = queue->numclients;
+ return PMDA_FETCH_STATIC;
+}
+
+int
+pmdaEventQueueMemory(int handle, pmAtomValue *atom)
+{
+ event_queue_t *queue = queue_lookup(handle);
+
+ if (!queue)
+ return -EINVAL;
+ atom->ull = queue->qsize;
+ return PMDA_FETCH_STATIC;
+}
+
+int
+pmdaEventQueueBytes(int handle, pmAtomValue *atom)
+{
+ event_queue_t *queue = queue_lookup(handle);
+
+ if (!queue)
+ return -EINVAL;
+ atom->ull = queue->bytes;
+ return PMDA_FETCH_STATIC;
+}
+
+int
+pmdaEventQueueAppend(int handle, void *data, size_t bytes, struct timeval *tv)
+{
+ event_queue_t *queue = queue_lookup(handle);
+ event_t *event;
+
+ if (!queue)
+ return -EINVAL;
+ if (pmDebug & DBG_TRACE_LIBPMDA)
+ __pmNotifyErr(LOG_INFO, "Appending event: queue#%d \"%s\" (%ld bytes)",
+ handle, queue->name, (long)bytes);
+ if (bytes > queue->maxmemory) {
+ __pmNotifyErr(LOG_WARNING, "Event too large for queue %s (%ld > %ld)",
+ queue->name, (long)bytes, (long)queue->maxmemory);
+ goto done;
+ }
+
+ /*
+ * We may need to make room in the event queue. If so, start at the head
+ * and madly drop events until sufficient space exists or all are freed.
+ * Bump the missed counter for each client who missed an event we had to
+ * throw away.
+ */
+ queue_drop_bytes(handle, queue, bytes);
+ if (queue->numclients == 0)
+ goto done;
+
+ if ((event = malloc(sizeof(event_t) + bytes + 1)) == NULL) {
+ __pmNotifyErr(LOG_ERR, "event allocation failure: %ld bytes",
+ (long)(bytes + 1));
+ return -ENOMEM;
+ }
+
+ /* Track the actual event data */
+ event->count = queue->numclients;
+ memcpy(event->buffer, data, bytes);
+ memcpy(&event->time, tv, sizeof(*tv));
+ event->size = bytes;
+
+ /* Finally, store the event in the queue */
+ TAILQ_INSERT_TAIL(&queue->tailq, event, events);
+ queue->qsize += bytes;
+
+ if (pmDebug & DBG_TRACE_LIBPMDA)
+ __pmNotifyErr(LOG_INFO,
+ "Inserted %s event %p (%ld bytes) clients = %d.",
+ queue->name, event, (long)event->size, event->count);
+
+done:
+ /* Update event queue tracking stats (even for no-clients case) */
+ queue->bytes += bytes;
+ queue->count++;
+ return 0;
+}
+
+static int
+queue_filter(event_clientq_t *clientq, void *data, size_t size)
+{
+ /* Note: having a filter implies access (optionally) checked there */
+ if (clientq->filter) {
+ int sts = clientq->apply(clientq->filter, data, size);
+ if (pmDebug & DBG_TRACE_LIBPMDA)
+ __pmNotifyErr(LOG_INFO, "Clientq filter applied (%d)\n", sts);
+ return sts;
+ }
+ else if (!clientq->access) {
+ if (pmDebug & DBG_TRACE_LIBPMDA)
+ __pmNotifyErr(LOG_INFO, "Clientq access denied\n");
+ return -PM_ERR_PERMISSION;
+ }
+ return 0;
+}
+
+static int
+queue_fetch(event_queue_t *queue, event_clientq_t *clientq, pmAtomValue *atom,
+ pmdaEventDecodeCallBack queue_decoder, void *data)
+{
+ event_t *event, *next;
+ int records, key, sts;
+
+ /*
+ * Ensure the way we keep track of which clients are interested
+ * in which queues is up to date.
+ */
+ if (clientq->active == 0) {
+ clientq->active = 1;
+ queue->numclients++;
+ }
+ if (clientq->last == NULL)
+ clientq->last = TAILQ_FIRST(&queue->tailq);
+ event = clientq->last;
+
+ if (pmDebug & DBG_TRACE_LIBPMDA)
+ __pmNotifyErr(LOG_INFO, "queue_fetch start, last event=%p\n", event);
+
+ sts = records = 0;
+ key = queue->eventarray;
+ pmdaEventResetArray(key);
+
+ while (event != NULL) {
+ char message[64];
+
+ if (queue_filter(clientq, event->buffer, event->size)) {
+ if (pmDebug & DBG_TRACE_LIBPMDA)
+ __pmNotifyErr(LOG_INFO, "Culling event (sz=%ld): \"%s\"",
+ (long)event->size,
+ __pmdaEventPrint(event->buffer, event->size,
+ message, sizeof(message)));
+ } else {
+ if (pmDebug & DBG_TRACE_LIBPMDA)
+ __pmNotifyErr(LOG_INFO, "Adding event (sz=%ld): \"%s\"",
+ (long)event->size,
+ __pmdaEventPrint(event->buffer, event->size,
+ message, sizeof(message)));
+ if ((sts = queue_decoder(key,
+ event->buffer, event->size, &event->time, data)) < 0)
+ break;
+ records += sts;
+ sts = 0;
+ }
+
+ next = TAILQ_NEXT(event, events);
+
+ /* Remove the current one (if its use count hits zero) */
+ if (--event->count <= 0) {
+ if (pmDebug & DBG_TRACE_LIBPMDA)
+ __pmNotifyErr(LOG_INFO, "Removing %s event %p in fetch",
+ queue->name, event);
+ TAILQ_REMOVE(&queue->tailq, event, events);
+ queue->qsize -= event->size;
+ free(event);
+ }
+
+ /* Go on to the next event. */
+ event = next;
+ }
+
+ /*
+ * Did this client miss any events? The "extra" one is the last previously
+ * observed event (pointed at by per-context last pointer) - so, event only
+ * missed once we move past *more* than just that last observed event.
+ */
+ if (sts == 0) {
+ sts = clientq->missed - 1;
+ clientq->missed = 0;
+ if (sts > 0) {
+ struct timeval timestamp;
+ gettimeofday(&timestamp, NULL);
+ sts = pmdaEventAddMissedRecord(key, &timestamp, sts);
+ records++;
+ } else {
+ sts = 0;
+ }
+ }
+
+ /* Update queue tail pointer for this client. */
+ clientq->last = NULL;
+
+ atom->vbp = records ? (pmValueBlock *)pmdaEventGetAddr(key) : NULL;
+ return sts;
+}
+
+static event_clientq_t *
+client_queue_lookup(int context, int handle, int accessq)
+{
+ event_client_t *client = client_lookup(context);
+ event_queue_t *queue = queue_lookup(handle);
+ size_t size;
+
+ /*
+ * If context doesn't exist, bail out. But, if per-client
+ * queue information doesn't exist for that context yet, it
+ * is create iff an indication of interest was shown by the
+ * caller (i.e. "accessq" was set).
+ */
+ if (!client || !queue)
+ return NULL;
+ if (handle < client->nclientq)
+ return &client->clientq[handle];
+ if (!accessq)
+ return NULL;
+
+ /* allocate (possibly multiple) queue slots for this client */
+ size = (handle + 1) * sizeof(struct event_clientq);
+ client->clientq = realloc(client->clientq, size);
+ if (!client->clientq)
+ __pmNoMem("client_queue_lookup", size, PM_FATAL_ERR);
+
+ /* ensure any new clientq's up to this one are initialised */
+ size -= client->nclientq * sizeof(struct event_clientq);
+ memset(client->clientq + client->nclientq, 0, size);
+ client->nclientq = handle + 1;
+ return &client->clientq[handle];
+}
+
+int
+pmdaEventQueueRecords(int handle, pmAtomValue *atom, int context,
+ pmdaEventDecodeCallBack queue_decoder, void *data)
+{
+ event_clientq_t *clientq = client_queue_lookup(context, handle, 1);
+ event_queue_t *queue = queue_lookup(handle);
+ int sts;
+
+ if (!queue || !clientq)
+ return -EINVAL;
+
+ sts = queue_fetch(queue, clientq, atom, queue_decoder, data);
+ if (sts != 0)
+ return sts;
+ return (atom->vbp == NULL) ? PMDA_FETCH_NOVALUES : PMDA_FETCH_STATIC;
+}
+
+/*
+ * We've lost a client (disconnected).
+ * Cleanup any filter and any back references across the queues.
+ */
+static void
+queue_cleanup(int handle, event_clientq_t *clientq)
+{
+ event_queue_t *queue = queue_lookup(handle);
+ event_t *event, *next;
+
+ if (clientq->release)
+ clientq->release(clientq->filter);
+
+ if (!queue || !clientq->active)
+ return;
+
+ event = clientq->last;
+ while (event) {
+ next = TAILQ_NEXT(event, events);
+
+ /* Remove the current event (if use count hits zero) */
+ if (--event->count <= 0) {
+ if (pmDebug & DBG_TRACE_LIBPMDA)
+ __pmNotifyErr(LOG_INFO, "Removing %s event %p",
+ queue->name, event);
+ TAILQ_REMOVE(&queue->tailq, event, events);
+ queue->qsize -= event->size;
+ free(event);
+ }
+ event = next;
+ }
+
+ queue->numclients--;
+}
+
+char *
+__pmdaEventPrint(const char *buffer, int bufsize, char *msg, int msgsize)
+{
+ int minsize = msgsize < bufsize ? msgsize : bufsize;
+ int i;
+
+ if (msgsize < 4)
+ return NULL;
+ memcpy(msg, buffer, minsize);
+ memset(msg + minsize, '.', msgsize - minsize);
+ msg[minsize - 1] = '\0';
+ for (i = 0; i < minsize - 1; i++) {
+ if (isspace((int)msg[i]))
+ msg[i] = ' ';
+ else if (!isprint((int)msg[i]))
+ msg[i] = '.';
+ }
+ return msg;
+}
+
+int
+pmdaEventNewClient(int context)
+{
+ event_client_t *client;
+ int size, i;
+
+ for (i = 0; i < numclients; i++) {
+ if (clients[i].context == context && clients[i].inuse)
+ return i;
+ }
+ for (i = 0; i < numclients; i++) {
+ if (clients[i].inuse == 0)
+ break;
+ }
+ if (i == numclients) {
+ /* no free slots, extend the available set */
+ size = (numclients + 1) * sizeof(event_client_t);
+ clients = realloc(clients, size);
+ if (!clients)
+ __pmNoMem("pmdaEventNewClient", size, PM_FATAL_ERR);
+ numclients++;
+
+ if (pmDebug & DBG_TRACE_LIBPMDA)
+ __pmNotifyErr(LOG_INFO, "%s: new client, slot=%d (total=%d)\n",
+ __FUNCTION__, i, numclients);
+ }
+
+ /* "i" now indexes into a free slot */
+ client = &clients[i];
+ memset(client, 0, sizeof(*client));
+ client->context = context;
+ client->inuse = 1;
+ return i;
+}
+
+static event_client_t *
+client_lookup(int context)
+{
+ int i;
+
+ for (i = 0; i < numclients; i++)
+ if (clients[i].context == context && clients[i].inuse)
+ return &clients[i];
+ return NULL;
+}
+
+/*
+ * Visit each active context and run a supplied callback routine
+ */
+static void
+client_iterate(clientVisitCallBack visit,
+ int handle, event_queue_t *queue, void *data)
+{
+ event_clientq_t *clientq;
+ int i;
+
+ for (i = 0; i < numclients; i++) {
+ if (!clients[i].inuse)
+ continue;
+ clientq = client_queue_lookup(clients[i].context, handle, 0);
+ if (clientq && clientq->active)
+ visit(clientq, queue, data);
+ }
+}
+
+int
+pmdaEventEndClient(int context)
+{
+ event_client_t *client = client_lookup(context);
+ int i;
+
+ if (pmDebug & DBG_TRACE_LIBPMDA)
+ fprintf(stderr, "pmdaEventEndClient: ctx=%d slot=%d\n",
+ context, client ? (int)(client - clients) : 0);
+
+ if (!client) {
+ /*
+ * This is expected ... when a context is closed in pmcd
+ * (or for a local context or for dbpmda or ...) all the
+ * PMDAs with a registered pmdaEndContextCallBack will be
+ * called and some of the PMDAs may not have not serviced
+ * any previous requests for that context.
+ */
+ return 0;
+ }
+
+ for (i = 0; i < client->nclientq; i++)
+ queue_cleanup(i, &client->clientq[i]);
+ if (client->clientq)
+ free(client->clientq);
+
+ memset(client, 0, sizeof(*client)); /* sets !inuse */
+ return 0;
+}
+
+int
+pmdaEventClients(pmAtomValue *atom)
+{
+ __uint32_t i, c = 0;
+
+ for (i = 0; i < numclients; i++)
+ if (clients[i].inuse)
+ c++;
+ atom->ul = c;
+ return PMDA_FETCH_STATIC;
+}
+
+/*
+ * Marks context as having been pmStore'd into (access allowed),
+ * adds optional filtering data for the current client context.
+ */
+int
+pmdaEventSetFilter(int context, int handle, void *filter,
+ pmdaEventApplyFilterCallBack apply,
+ pmdaEventReleaseFilterCallBack release)
+{
+ event_clientq_t *clientq = client_queue_lookup(context, handle, 1);
+
+ if (!clientq)
+ return -EINVAL;
+
+ /* first, free up any existing filter */
+ if (clientq->filter)
+ clientq->release(clientq->filter);
+
+ clientq->apply = apply;
+ clientq->filter = filter;
+ clientq->release = release;
+ clientq->access = 1;
+ return 0;
+}
+
+int
+pmdaEventSetAccess(int context, int handle, int allow)
+{
+ event_clientq_t *clientq = client_queue_lookup(context, handle, 1);
+
+ if (!clientq)
+ return -EINVAL;
+
+ clientq->access = allow;
+ return 0;
+}
diff --git a/src/libpcp_pmda/src/queues.h b/src/libpcp_pmda/src/queues.h
new file mode 100644
index 0000000..607655e
--- /dev/null
+++ b/src/libpcp_pmda/src/queues.h
@@ -0,0 +1,172 @@
+/*
+ * Event queue support for PMDAs
+ *
+ * Copyright (c) 2011 Red Hat Inc.
+ * Copyright (c) 2011 Nathan Scott. 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.
+ *
+ * Portions Copyright (c) 1991, 1993
+ *
+ * The Regents of the University of California. All rights reserved.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _QUEUES_H
+#define _QUEUES_H
+
+/*
+ * Extracts of TAILQ implementation from <sys/queue.h> included directly
+ * for platforms without support (Win32, older Linux variants).
+ *
+ * A tail queue is headed by a pair of pointers, one to the head of the
+ * list and the other to the tail of the list. The elements are doubly
+ * linked so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before or
+ * after an existing element, at the head of the list, or at the end of
+ * the list. A tail queue may be traversed in either direction.
+ *
+ * For details on the use of these macros, see the queue(3) manual page.
+ */
+
+#define _TAILQ_HEAD(name, type, qual) \
+struct name { \
+ qual type *tqh_first; /* first element */ \
+ qual type *qual *tqh_last; /* addr of last next element */ \
+}
+#define TAILQ_HEAD(name, type) _TAILQ_HEAD(name, struct type,)
+
+#define _TAILQ_ENTRY(type, qual) \
+struct { \
+ qual type *tqe_next; /* next element */ \
+ qual type *qual *tqe_prev; /* address of previous next element */\
+}
+#define TAILQ_ENTRY(type) _TAILQ_ENTRY(struct type,)
+
+#define TAILQ_INIT(head) do { \
+ (head)->tqh_first = NULL; \
+ (head)->tqh_last = &(head)->tqh_first; \
+} while (0)
+
+#define TAILQ_INSERT_HEAD(head, elm, field) do { \
+ if (((elm)->field.tqe_next = (head)->tqh_first) != NULL) \
+ (head)->tqh_first->field.tqe_prev = \
+ &(elm)->field.tqe_next; \
+ else \
+ (head)->tqh_last = &(elm)->field.tqe_next; \
+ (head)->tqh_first = (elm); \
+ (elm)->field.tqe_prev = &(head)->tqh_first; \
+} while (0)
+
+#define TAILQ_INSERT_TAIL(head, elm, field) do { \
+ (elm)->field.tqe_next = NULL; \
+ (elm)->field.tqe_prev = (head)->tqh_last; \
+ *(head)->tqh_last = (elm); \
+ (head)->tqh_last = &(elm)->field.tqe_next; \
+} while (0)
+
+#define TAILQ_REMOVE(head, elm, field) do { \
+ if (((elm)->field.tqe_next) != NULL) \
+ (elm)->field.tqe_next->field.tqe_prev = \
+ (elm)->field.tqe_prev; \
+ else \
+ (head)->tqh_last = (elm)->field.tqe_prev; \
+ *(elm)->field.tqe_prev = (elm)->field.tqe_next; \
+} while (0)
+
+#define TAILQ_EMPTY(head) ((head)->tqh_first == NULL)
+#define TAILQ_FIRST(head) ((head)->tqh_first)
+#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next)
+
+#define TAILQ_LAST(head, headname) \
+ (*(((struct headname *)((head)->tqh_last))->tqh_last))
+#define TAILQ_PREV(elm, headname, field) \
+ (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last))
+
+
+/*
+ * Data structures used in the PMDA event queue implementation
+ * Every event is timestamped and linked into one (tail) queue.
+ * Events know nothing about the clients accessing them.
+ */
+
+typedef struct event {
+ TAILQ_ENTRY(event) events; /* link into queue of events */
+ struct timeval time; /* timestamp for this event */
+ int count; /* events reference count */
+ size_t size; /* buffer size in bytes */
+ char buffer[];
+} event_t;
+
+TAILQ_HEAD(tailqueue, event);
+
+typedef struct event_queue {
+ const char *name; /* callers identifier for this queue */
+ size_t maxmemory; /* max data bytes that can be queued */
+ int inuse; /* is this queue in use or free */
+ int eventarray; /* event records for this queue */
+ __uint32_t numclients; /* export: number of active clients */
+ __uint32_t count; /* exported: event counter */
+ __uint64_t bytes; /* exported: data throughput */
+ __uint64_t qsize; /* data in the queue (<= maxmem) */
+ struct tailqueue tailq; /* queue of events for clients */
+} event_queue_t;
+
+/*
+ * Data structures used in the PMDA event client implementation
+ * Each client is one PCP tool invocation (e.g. pmevent) and has
+ * a link back to those queues which it has fetched/stored into
+ * at some point in the past. The "last" event pointer, gives a
+ * pointer to the last observed event for that client, which is
+ * used as the starting point for a subsequent fetch request (or
+ * when dropping events, should the client not be keeping up).
+ */
+
+typedef struct event_clientq {
+ int active; /* client interest in this queue */
+ int missed; /* count of events missed on queue */
+ int access; /* is access restricted/permitted */
+ event_t *last; /* last event seen on this queue */
+ void *filter; /* filter data for the event queue */
+ pmdaEventApplyFilterCallBack apply; /* actual filter callback */
+ pmdaEventReleaseFilterCallBack release; /* remove filter callback */
+} event_clientq_t;
+
+typedef struct event_client {
+ int context; /* client context identifier */
+ int inuse; /* is this table slot in use */
+ int nclientq; /* allocated size of clientq */
+ event_clientq_t *clientq; /* per-queue client state */
+} event_client_t;
+
+#endif /* _QUEUES_H */
diff --git a/src/libpcp_pmda/src/tree.c b/src/libpcp_pmda/src/tree.c
new file mode 100644
index 0000000..ab24504
--- /dev/null
+++ b/src/libpcp_pmda/src/tree.c
@@ -0,0 +1,313 @@
+/*
+ * Copyright (c) 2014 Red Hat.
+ * Copyright (c) 2009-2010 Aconex. All Rights Reserved.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This library 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 Lesser General Public
+ * License for more details.
+ */
+
+#include "pmapi.h"
+#include "impl.h"
+#include "pmda.h"
+
+#define NONLEAF(node) ((node)->pmid == PM_ID_NULL)
+
+/*
+ * Fixup the parent pointers of the tree.
+ * Fill in the hash table with nodes from the tree.
+ * Hashing is done on pmid.
+ */
+static void
+__pmdaTreeReindexHash(__pmnsTree *tree, __pmnsNode *root)
+{
+ __pmnsNode *np;
+
+ for (np = root->first; np != NULL; np = np->next) {
+ np->parent = root;
+ if (np->pmid != PM_ID_NULL) {
+ int i = np->pmid % tree->htabsize;
+ np->hash = tree->htab[i];
+ tree->htab[i] = np;
+ }
+ __pmdaTreeReindexHash(tree, np);
+ }
+}
+
+/*
+ * "Make the average hash list no longer than 5, and the number
+ * of hash table entries not a multiple of 2, 3 or 5."
+ * [From __pmFixPMNSHashTab; without mark_all, dinks with pmids]
+ */
+void
+pmdaTreeRebuildHash(__pmnsTree *tree, int numpmid)
+{
+ if (tree) {
+ int htabsize = numpmid / 5;
+
+ if (htabsize % 2 == 0) htabsize++;
+ if (htabsize % 3 == 0) htabsize += 2;
+ if (htabsize % 5 == 0) htabsize += 2;
+ tree->htabsize = htabsize;
+ tree->htab = (__pmnsNode **)calloc(htabsize, sizeof(__pmnsNode *));
+ if (tree->htab) {
+ __pmdaTreeReindexHash(tree, tree->root);
+ } else {
+ __pmNoMem("pmdaTreeRebuildHash",
+ htabsize * sizeof(__pmnsNode *), PM_RECOV_ERR);
+ tree->htabsize = 0;
+ }
+ }
+}
+
+static int
+__pmdaNodeCount(__pmnsNode *parent)
+{
+ __pmnsNode *np;
+ int count;
+
+ count = 0;
+ for (np = parent->first; np != NULL; np = np->next) {
+ if (np->pmid != PM_ID_NULL)
+ count++;
+ else
+ count += __pmdaNodeCount(np);
+ }
+ return count;
+}
+
+int
+pmdaTreeSize(__pmnsTree *pmns)
+{
+ if (pmns && pmns->root)
+ return __pmdaNodeCount(pmns->root);
+ return 0;
+}
+
+static __pmnsNode *
+__pmdaNodeLookup(__pmnsNode *node, const char *name)
+{
+ while (node != NULL) {
+ size_t length = strlen(node->name);
+ if (strncmp(name, node->name, length) == 0) {
+ if (name[length] == '\0')
+ return node;
+ if (name[length] == '.' && NONLEAF(node))
+ return __pmdaNodeLookup(node->first, name + length + 1);
+ }
+ node = node->next;
+ }
+ return NULL;
+}
+
+int
+pmdaTreePMID(__pmnsTree *pmns, const char *name, pmID *pmid)
+{
+ if (pmns && pmns->root) {
+ __pmnsNode *node;
+
+ if ((node = __pmdaNodeLookup(pmns->root->first, name)) == NULL)
+ return PM_ERR_NAME;
+ if (NONLEAF(node))
+ return PM_ERR_NAME;
+ *pmid = node->pmid;
+ return 0;
+ }
+ return PM_ERR_NAME;
+}
+
+static char *
+__pmdaNodeAbsoluteName(__pmnsNode *node, char *buffer)
+{
+ if (node && node->parent) {
+ buffer = __pmdaNodeAbsoluteName(node->parent, buffer);
+ strcpy(buffer, node->name);
+ buffer += strlen(node->name);
+ *buffer++ = '.';
+ }
+ return buffer;
+}
+
+int
+pmdaTreeName(__pmnsTree *pmns, pmID pmid, char ***nameset)
+{
+ __pmnsNode *hashchain, *node, *parent;
+ int nmatch = 0, length = 0;
+ char *p, **list;
+
+ if (!pmns)
+ return PM_ERR_PMID;
+
+ hashchain = pmns->htab[pmid % pmns->htabsize];
+ for (node = hashchain; node != NULL; node = node->hash) {
+ if (node->pmid == pmid) {
+ for (parent = node; parent->parent; parent = parent->parent)
+ length += strlen(parent->name) + 1;
+ nmatch++;
+ }
+ }
+
+ if (nmatch == 0)
+ return PM_ERR_PMID;
+
+ length += nmatch * sizeof(char *); /* pointers to names */
+
+ if ((list = (char **)malloc(length)) == NULL)
+ return -oserror();
+
+ p = (char *)&list[nmatch];
+ nmatch = 0;
+ for (node = hashchain; node != NULL; node = node->hash) {
+ if (node->pmid == pmid) {
+ list[nmatch++] = p;
+ p = __pmdaNodeAbsoluteName(node, p);
+ *(p-1) = '\0'; /* overwrite final '.' */
+ }
+ }
+
+ *nameset = list;
+ return nmatch;
+}
+
+static int
+__pmdaNodeRelativeChildren(__pmnsNode *base, char ***offspring, int **status)
+{
+ __pmnsNode *node;
+ char **list, *p;
+ int *leaf, length = 0, nmatch = 0;
+
+ for (node = base; node != NULL; node = node->next, nmatch++)
+ length += strlen(node->name) + 1;
+ if (nmatch == 0) {
+ /*
+ * no need to allocate zero sized arrays for offspring[]
+ * and status[]
+ */
+ return 0;
+ }
+ length += nmatch * sizeof(char *); /* pointers to names */
+ if ((list = (char **)malloc(length)) == NULL)
+ return -oserror();
+ if ((leaf = (int *)malloc(nmatch * sizeof(int))) == NULL) {
+ free(list);
+ return -oserror();
+ }
+ p = (char *)&list[nmatch];
+ nmatch = 0;
+ for (node = base; node != NULL; node = node->next, nmatch++) {
+ leaf[nmatch] = NONLEAF(node) ? PMNS_NONLEAF_STATUS : PMNS_LEAF_STATUS;
+ list[nmatch] = p;
+ strcpy(p, node->name);
+ p += strlen(node->name);
+ *p++ = '\0';
+ }
+
+ *offspring = list;
+ *status = leaf;
+ return nmatch;
+}
+
+static void
+__pmdaNodeChildrenGetSize(__pmnsNode *base, int kids, int *length, int *nmetrics)
+{
+ __pmnsNode *node, *parent;
+
+ /* walk to every leaf & then add its (absolute name) length */
+ for (node = base; node != NULL; node = node->next) {
+ if (NONLEAF(node)) {
+ __pmdaNodeChildrenGetSize(node->first, 1, length, nmetrics);
+ continue;
+ }
+ for (parent = node; parent->parent; parent = parent->parent)
+ *length += strlen(parent->name) + 1;
+ (*nmetrics)++;
+ if (!kids)
+ break;
+ }
+}
+
+/*
+ * Fill the pmdaChildren buffers - names and leaf status. Called recursively
+ * to descend down to all leaf nodes. Offset parameter is the current offset
+ * into the name list buffer, and its also returned at the end of each call -
+ * it keeps track of where the next name is to start in (list) output buffer.
+ */
+static char *
+__pmdaNodeChildrenGetList(__pmnsNode *base, int kids, int *nmetrics, char *p, char **list, int *leaf)
+{
+ __pmnsNode *node;
+ int count = *nmetrics;
+ char *start = p;
+
+ for (node = base; node != NULL; node = node->next) {
+ if (NONLEAF(node)) {
+ p = __pmdaNodeChildrenGetList(node->first, 1, &count, p, list, leaf);
+ start = p;
+ continue;
+ }
+ leaf[count] = PMNS_LEAF_STATUS;
+ list[count] = start;
+ p = __pmdaNodeAbsoluteName(node, p);
+ *(p-1) = '\0'; /* overwrite final '.' */
+ start = p;
+ count++;
+ if (!kids)
+ break;
+ }
+ *nmetrics = count;
+ return p;
+}
+
+static int
+__pmdaNodeAbsoluteChildren(__pmnsNode *node, char ***offspring, int **status)
+{
+ char *p, **list;
+ int *leaf, descend = 0, length = 0, nmetrics = 0;
+
+ if (NONLEAF(node)) {
+ node = node->first;
+ descend = 1;
+ }
+ __pmdaNodeChildrenGetSize(node, descend, &length, &nmetrics);
+
+ length += nmetrics * sizeof(char *); /* pointers to names */
+ if ((list = (char **)malloc(length)) == NULL)
+ return -oserror();
+ if ((leaf = (int *)malloc(nmetrics * sizeof(int))) == NULL) {
+ free(list);
+ return -oserror();
+ }
+
+ p = (char *)&list[nmetrics];
+ nmetrics = 0; /* start at the start */
+ __pmdaNodeChildrenGetList(node, descend, &nmetrics, p, list, leaf);
+
+ *offspring = list;
+ *status = leaf;
+ return nmetrics;
+}
+
+int
+pmdaTreeChildren(__pmnsTree *pmns, const char *name, int traverse, char ***offspring, int **status)
+{
+ __pmnsNode *node;
+ int sts;
+
+ if (!pmns)
+ return PM_ERR_NAME;
+
+ if ((node = __pmdaNodeLookup(pmns->root->first, name)) == NULL)
+ return PM_ERR_NAME;
+
+ if (traverse == 0)
+ sts = __pmdaNodeRelativeChildren(node->first, offspring, status);
+ else
+ sts = __pmdaNodeAbsoluteChildren(node, offspring, status);
+ return sts;
+}