diff --git a/src/pmdas/mmv/GNUmakefile b/src/pmdas/mmv/GNUmakefile index 6205a8a..63f778a 100644 --- a/src/pmdas/mmv/GNUmakefile +++ b/src/pmdas/mmv/GNUmakefile @@ -21,7 +21,6 @@ DOMAIN = MMV CMDTARGET = pmda$(IAM)$(EXECSUFFIX) LIBTARGET = pmda_$(IAM).$(DSOSUFFIX) TARGETS = $(CMDTARGET) $(LIBTARGET) mmvdump$(EXECSUFFIX) -INSTTARGETS = $(CMDTARGET) mmvdump$(EXECSUFFIX) CFILES = mmv.c LSRCFILES = pmns Install Remove README help root mmvdump.c @@ -40,7 +39,7 @@ include $(BUILDRULES) install: default $(INSTALL) -m 755 -d $(PMDADIR) - $(INSTALL) -m 755 $(INSTTARGETS) Install Remove $(PMDADIR) + $(INSTALL) -m 755 $(TARGETS) Install Remove $(PMDADIR) $(INSTALL) -m 644 README root help pmns domain.h $(PMDADIR) $(CMDTARGET): $(OBJECTS) diff --git a/src/pmdas/mmv/Install b/src/pmdas/mmv/Install index d663f1b..e4cbaa8 100755 --- a/src/pmdas/mmv/Install +++ b/src/pmdas/mmv/Install @@ -18,7 +18,7 @@ . $PCP_SHARE_DIR/lib/pmdaproc.sh iam=mmv -dso_opt=false +dso_opt=true socket_opt=true socket_inet_def=2081 pmda_interface=3 diff --git a/src/pmdas/mmv/mmv.c b/src/pmdas/mmv/mmv.c index ef30ef0..d5fd366 100644 --- a/src/pmdas/mmv/mmv.c +++ b/src/pmdas/mmv/mmv.c @@ -30,7 +30,8 @@ #include "./domain.h" #include -static pmdaInterface dispatch; +#define NONLEAF(node) ((node)->pmid == PM_ID_NULL) + static char mypath[MAXPATHLEN]; static int isDSO = 1; @@ -40,6 +41,7 @@ static pmdaIndom * indoms; static int incnt; static int reload; +static __pmnsTree *pmns; static time_t statsdir_ts; /* last statsdir timestamp */ static char * prefix = "mmv"; @@ -67,100 +69,6 @@ typedef struct { static stats_t * slist; static int scnt; -static int -update_namespace(void) -{ - char script[3*MAXPATHLEN]; - int sep = __pmPathSeparator(); - - snprintf(script, sizeof(script), - "%s%c" "lib" "%c" "ReplacePmnsSubtree %s %s%c" "%s.new", - pmGetConfig("PCP_SHARE_DIR"), sep, sep, - prefix, pmnsdir, sep, prefix); - if (system(script) == -1) { - __pmNotifyErr (LOG_ERR, "%s: cannot exec %s", pmProgname, script); - return 1; - } - - return 0; -} - -static void -write_pmnspath(__pmnsNode *base, FILE *f) -{ - if (base && base->parent) { - write_pmnspath(base->parent, f); - fprintf(f, "%s.", base->name); - } -} - -static void -write_pmnsnode(__pmnsNode *base, FILE *f) -{ - __pmnsNode *np; - - /* Print out full path to this part of the tree */ - write_pmnspath(base->parent, f); - fprintf(f, "%s {\n", base->name); - - /* Print out nodes at this level of the tree */ - for (np = base->first; np != NULL; np = np->next) { - if (np->pmid == PM_ID_NULL) - fprintf(f, "\t%s\n", np->name); - else - fprintf(f, "\t%s\t\t%u:%u:%u\n", np->name, - pmid_domain(np->pmid), - pmid_cluster(np->pmid), - pmid_item(np->pmid)); - } - fprintf(f, "}\n\n"); - - /* Print out all the children of this subtree */ - for (np = base->first; np != NULL; np = np->next) - if (np->pmid == PM_ID_NULL) - write_pmnsnode(np, f); -} - -static void -write_pmnsfile(__pmnsTree *pmns) -{ - char tmppath[MAXPATHLEN]; - char path[MAXPATHLEN]; - char *fname = tmppath; - FILE *f = NULL; - - putenv("TMPDIR="); /* temp file must be in pmnsdir, for rename */ - -#if HAVE_MKSTEMP - sprintf(tmppath, "%s%c%s-XXXXXX", pmnsdir, __pmPathSeparator(), prefix); - int fd = mkstemp(tmppath); - if (fd != -1) - f = fdopen(fd, "w"); -#else - fname = tempnam(pmnsdir, prefix); - if (fname != NULL) { - strncpy(tmppath, fname, sizeof(tmppath)); - free(fname); - fname = tmppath; - f = fopen(fname, "w"); - } -#endif - - if (f == NULL) - __pmNotifyErr(LOG_ERR, "%s: failed to generate temporary file %s: %s", - pmProgname, fname, strerror(errno)); - else { - __pmnsNode *node; - for (node = pmns->root->first; node != NULL; node = node->next) - write_pmnsnode(node, f); - fclose(f); - sprintf(path, "%s%c" "%s.new", pmnsdir, __pmPathSeparator(), prefix); - if (rename2(fname, path) < 0) - __pmNotifyErr(LOG_ERR, "%s: cannot rename %s to %s - %s", - pmProgname, fname, path, strerror (errno)); - } -} - /* * Choose an unused cluster ID while honouring specific requests. * If a specific (non-zero) cluster is requested we always use it. @@ -193,24 +101,70 @@ choose_cluster(int requested, const char *path) return requested; } +/* + * Fixup the parent pointers of the tree. + * Fill in the hash table with nodes from the tree. + * Hashing is done on pmid. + */ +static void +mmv_reindex_hash(__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; + } + mmv_reindex_hash(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] + */ +static void +mmv_rebuild_hash(__pmnsTree *tree, int numpmid) +{ + 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 == NULL) + __pmNotifyErr(LOG_ERR, "%s: out of memory in pmns rebuild - %s", + pmProgname, strerror(errno)); + else + mmv_reindex_hash(tree, tree->root); +} + static void -map_stats(void) +map_stats(pmdaExt *pmda) { - __pmnsTree *pmns; struct dirent ** files; char name_reload[64]; int need_reload = 0; int i, sts, num; + if (pmns) + __pmFreePMNS(pmns); + if ((sts = __pmNewPMNS(&pmns)) < 0) { __pmNotifyErr(LOG_ERR, "%s: failed to create new pmns: %s\n", pmProgname, pmErrStr(sts)); + pmns = NULL; return; } mcnt = 1; snprintf(name_reload, sizeof(name_reload), "%s.reload", prefix); - __pmAddPMNSNode(pmns, pmid_build(dispatch.domain, 0, 0), name_reload); + __pmAddPMNSNode(pmns, pmid_build(pmda->e_domain, 0, 0), name_reload); if (indoms != NULL) { for (i = 0; i < incnt; i++) @@ -359,7 +313,7 @@ map_stats(void) metrics[mcnt].m_user = ml + k; metrics[mcnt].m_desc.pmid = pmid_build( - dispatch.domain, s->cluster, ml[k].item); + pmda->e_domain, s->cluster, ml[k].item); if (ml[k].type == MMV_TYPE_ELAPSED) { pmUnits unit = PMDA_PMUNITS(0,1,0,0,PM_TIME_USEC,0); @@ -378,18 +332,20 @@ map_stats(void) metrics[mcnt].m_desc.indom = (!ml[k].indom || ml[k].indom == PM_INDOM_NULL) ? PM_INDOM_NULL : - pmInDom_build(dispatch.domain, + pmInDom_build(pmda->e_domain, (s->cluster << 11) | ml[k].indom); strcat(name, ml[k].name); __pmAddPMNSNode(pmns, pmid_build( - dispatch.domain, s->cluster, ml[k].item), + pmda->e_domain, s->cluster, ml[k].item), name); mcnt++; } } else { __pmNotifyErr(LOG_ERR, "%s: cannot grow metric list", pmProgname); + if (isDSO) + return; exit(1); } break; @@ -405,7 +361,7 @@ map_stats(void) for (k = 0; k < toc[j].count; k++) { ip = &indoms[incnt + k]; - ip->it_indom = pmInDom_build(dispatch.domain, + ip->it_indom = pmInDom_build(pmda->e_domain, (slist[i].cluster << 11) | id[k].serial); ip->it_numinst = id[k].count; ip->it_set = (pmdaInstid *) @@ -422,6 +378,8 @@ map_stats(void) __pmNotifyErr(LOG_ERR, "%s: cannot get memory for instance list", pmProgname); + if (isDSO) + return; exit(1); } } @@ -429,6 +387,8 @@ map_stats(void) } else { __pmNotifyErr(LOG_ERR, "%s: cannot grow indom list", pmProgname); + if (isDSO) + return; exit(1); } break; @@ -445,9 +405,7 @@ map_stats(void) } } - write_pmnsfile(pmns); - __pmFreePMNS(pmns); - + mmv_rebuild_hash(pmns, mcnt); /* for reverse (pmid->name) lookups */ reload = need_reload; } @@ -549,8 +507,8 @@ mmv_fetchCallBack(pmdaMetric *mdesc, unsigned int inst, pmAtomValue *atom) return 0; } -static int -mmv_reload_maybe(void) +static void +mmv_reload_maybe(pmdaExt *pmda) { int i; struct stat s; @@ -572,14 +530,8 @@ mmv_reload_maybe(void) } if (need_reload) { - /* something changed - reload */ - pmdaExt * pmda = dispatch.version.two.ext; /* we know it is V.2 */ - - /* Note: this line means we cannot run as shared library */ - __pmSendError(pmda->e_outfd, PDU_BINARY, PM_ERR_PMDANOTREADY); - __pmNotifyErr(LOG_INFO, "%s: reloading", pmProgname); - map_stats(); + map_stats(pmda); pmda->e_indoms = indoms; pmda->e_nindoms = incnt; @@ -590,19 +542,14 @@ mmv_reload_maybe(void) __pmNotifyErr(LOG_INFO, "%s: %d metrics and %d indoms after reload", pmProgname, mcnt, incnt); - - reload = update_namespace(); } - - return need_reload; } /* Intercept request for descriptor and check if we'd have to reload */ static int mmv_desc(pmID pmid, pmDesc *desc, pmdaExt *ep) { - if (mmv_reload_maybe()) - return PM_ERR_PMDAREADY; + mmv_reload_maybe(ep); return pmdaDesc(pmid, desc, ep); } @@ -612,9 +559,8 @@ mmv_text(int ident, int type, char **buffer, pmdaExt *ep) if (type & PM_TEXT_INDOM) return PM_ERR_TEXT; - if (mmv_reload_maybe()) - return PM_ERR_PMDAREADY; - else if (pmid_cluster(ident) == 0) + mmv_reload_maybe(ep); + if (pmid_cluster(ident) == 0) return pmdaText(ident, type, buffer, ep); else { mmv_disk_metric_t * m; @@ -643,16 +589,14 @@ static int mmv_instance(pmInDom indom, int inst, char *name, __pmInResult **result, pmdaExt *ep) { - if (mmv_reload_maybe()) - return PM_ERR_PMDAREADY; + mmv_reload_maybe(ep); return pmdaInstance(indom, inst, name, result, ep); } static int mmv_fetch(int numpmid, pmID pmidlist[], pmResult **resp, pmdaExt *pmda) { - if (mmv_reload_maybe()) - return PM_ERR_PMDAREADY; + mmv_reload_maybe(pmda); return pmdaFetch(numpmid, pmidlist, resp, pmda); } @@ -661,8 +605,7 @@ mmv_store(pmResult *result, pmdaExt *ep) { int i, m; - if (mmv_reload_maybe()) - return PM_ERR_PMDAREADY; + mmv_reload_maybe(ep); for (i = 0; i < result->numpmid; i++) { pmValueSet * vsp = result->vset[i]; @@ -692,6 +635,219 @@ mmv_store(pmResult *result, pmdaExt *ep) return 0; } +static __pmnsNode * +mmv_lookup_node(__pmnsNode *node, 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 mmv_lookup_node(node->first, name + length + 1); + } + node = node->next; + } + return NULL; +} + +static int +mmv_pmid(char *name, pmID *pmid, pmdaExt *pmda) +{ + __pmnsNode *node; + + mmv_reload_maybe(pmda); + if ((node = mmv_lookup_node(pmns->root->first, name)) == NULL) + return PM_ERR_NAME; + if (NONLEAF(node)) + return PM_ERR_NAME; + *pmid = node->pmid; + return 0; +} + +static char * +mmv_absolute_name(__pmnsNode *node, char *buffer) +{ + if (node && node->parent) { + buffer = mmv_absolute_name(node->parent, buffer); + strcpy(buffer, node->name); + buffer += strlen(node->name); + *buffer++ = '.'; + } + return buffer; +} + +static int +mmv_name(pmID pmid, char ***nameset, pmdaExt *pmda) +{ + __pmnsNode *hashchain, *node, *parent; + int nmatch = 0, length = 0; + char *p, **list; + + mmv_reload_maybe(pmda); + 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 -errno; + + p = (char *)&list[nmatch]; + nmatch = 0; + for (node = hashchain; node != NULL; node = node->hash) { + if (node->pmid == pmid) { + list[nmatch++] = p; + p = mmv_absolute_name(node, p); + *(p-1) = '\0'; /* overwrite final '.' */ + } + } + + *nameset = list; + return nmatch; +} + +static int +mmv_children_relative(__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; + length += nmatch * sizeof(char *); /* pointers to names */ + if ((list = (char **)malloc(length)) == NULL) + return -errno; + if ((leaf = (int *)malloc(nmatch * sizeof(int*))) == NULL) { + free(list); + return -errno; + } + 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 +mmv_children_getsize(__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)) { + mmv_children_getsize(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 * +mmv_children_getlist(__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 = mmv_children_getlist(node->first, 1, &count, p, list, leaf); + start = p; + continue; + } + leaf[count] = PMNS_LEAF_STATUS; + list[count] = start; + p = mmv_absolute_name(node, p); + *(p-1) = '\0'; /* overwrite final '.' */ + start = p; + count++; + if (!kids) + break; + } + *nmetrics = count; + return p; +} + +static int +mmv_children_absolute(__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; + } + mmv_children_getsize(node, descend, &length, &nmetrics); + + length += nmetrics * sizeof(char *); /* pointers to names */ + if ((list = (char **)malloc(length)) == NULL) + return -errno; + if ((leaf = (int *)malloc(nmetrics * sizeof(int*))) == NULL) { + free(list); + return -errno; + } + + p = (char *)&list[nmetrics]; + nmetrics = 0; /* start at the start */ + mmv_children_getlist(node, descend, &nmetrics, p, list, leaf); + + *offspring = list; + *status = leaf; + return nmetrics; +} + +static int +mmv_children(char *name, int traverse, char ***offspring, int **status, pmdaExt *pmda) +{ + __pmnsNode *node; + + mmv_reload_maybe(pmda); + if (!pmns) + return PM_ERR_NAME; + + __pmNotifyErr(LOG_INFO, "%s: children of %s (traverse=%d)", pmProgname, name, traverse); + + if ((node = mmv_lookup_node(pmns->root->first, name)) == NULL) + return PM_ERR_NAME; + + if (traverse == 0) + return mmv_children_relative(node->first, offspring, status); + return mmv_children_absolute(node, offspring, status); +} void mmv_init(pmdaInterface *dp) @@ -701,7 +857,7 @@ mmv_init(pmdaInterface *dp) if (isDSO) { snprintf(mypath, sizeof(mypath), "%s%c" "mmv" "%c" "help", pmGetConfig("PCP_PMDAS_DIR"), sep, sep); - pmdaDSO(dp, PMDA_INTERFACE_3, "MMV DSO", mypath); + pmdaDSO(dp, PMDA_INTERFACE_4, "mmv", mypath); } pcptmpdir = pmGetConfig("PCP_TMP_DIR"); @@ -729,11 +885,14 @@ mmv_init(pmdaInterface *dp) exit(0); } - dp->version.two.fetch = mmv_fetch; - dp->version.two.store = mmv_store; - dp->version.two.desc = mmv_desc; - dp->version.two.text = mmv_text; - dp->version.two.instance = mmv_instance; + dp->version.four.fetch = mmv_fetch; + dp->version.four.store = mmv_store; + dp->version.four.desc = mmv_desc; + dp->version.four.text = mmv_text; + dp->version.four.instance = mmv_instance; + dp->version.four.pmid = mmv_pmid; + dp->version.four.name = mmv_name; + dp->version.four.children = mmv_children; pmdaSetFetchCallBack(dp, mmv_fetchCallBack); @@ -762,6 +921,8 @@ main(int argc, char **argv) int err = 0; int sep = __pmPathSeparator(); char logfile[32]; + char p[] = "mmv", *prefix = p; + pmdaInterface dispatch = { 0 }; isDSO = 0; __pmSetProgname(argv[0]); @@ -770,7 +931,7 @@ main(int argc, char **argv) snprintf(mypath, sizeof(mypath), "%s%c" "%s%c" "help", pmGetConfig("PCP_PMDAS_DIR"), sep, prefix, sep); snprintf(logfile, sizeof(logfile), "%s.log", prefix); - pmdaDaemon(&dispatch, PMDA_INTERFACE_3, pmProgname, MMV, logfile, mypath); + pmdaDaemon(&dispatch, PMDA_INTERFACE_4, pmProgname, MMV, logfile, mypath); if ((pmdaGetOpt(argc, argv, "D:d:l:?", &dispatch, &err) != EOF) || err || argc != optind) diff --git a/src/pmdas/mmv/pmns b/src/pmdas/mmv/pmns index 38241f0..4c3cb82 100644 --- a/src/pmdas/mmv/pmns +++ b/src/pmdas/mmv/pmns @@ -1,8 +1,5 @@ /* - * Metrics for MMV PMDA - * + * Metrics for MMV PMDA - all dynamic! */ -mmv { - reload MMV:0:0 -} +mmv MMV:*:*