diff options
Diffstat (limited to 'src/libpcp/src/pmns.c')
-rw-r--r-- | src/libpcp/src/pmns.c | 2559 |
1 files changed, 2559 insertions, 0 deletions
diff --git a/src/libpcp/src/pmns.c b/src/libpcp/src/pmns.c new file mode 100644 index 0000000..7602922 --- /dev/null +++ b/src/libpcp/src/pmns.c @@ -0,0 +1,2559 @@ +/* + * Copyright (c) 2012-2014 Red Hat. + * Copyright (c) 1995-2001 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. + * + * Thread-safe notes + * + * locerr - no serious side-effects, most unlikely to be used, and + * repeated calls are likely to produce the same result, so don't bother + * to make thread-safe + */ + +#include <sys/stat.h> +#include <stddef.h> +#include <assert.h> +#include <ctype.h> +#include "pmapi.h" +#include "impl.h" +#include "pmda.h" +#include "internal.h" +#ifdef HAVE_STRINGS_H +#include <strings.h> +#endif + +/* token types */ +#define NAME 1 +#define PATH 2 +#define PMID 3 +#define LBRACE 4 +#define RBRACE 5 +#define BOGUS 10 + +#define UNKNOWN_MARK_STATE -1 /* tree not all marked the same way */ +/* + * Note: bit masks below are designed to clear and set the "flag" field + * of a __pmID_int (i.e. a PMID) + */ +#define PMID_MASK 0x7fffffff /* 31 bits of PMID */ +#define MARK_BIT 0x80000000 /* mark bit */ + + +static int lineno; +static char linebuf[256]; +static char *linep; +static char fname[256]; +static char tokbuf[256]; +static pmID tokpmid; +static int seenpmid; + +static __pmnsNode *seen; /* list of pass-1 subtree nodes */ + +/* Last modification time for loading main_pmns file. */ +#if defined(HAVE_STAT_TIMESTRUC) +static timestruc_t last_mtim; +#elif defined(HAVE_STAT_TIMESPEC) +static struct timespec last_mtim; +#elif defined(HAVE_STAT_TIMESPEC_T) +static timespec_t last_mtim; +#elif defined(HAVE_STAT_TIME_T) +static time_t last_mtim; +#else +!bozo! +#endif + +/* The curr_pmns points to PMNS to use for API ops. + * Curr_pmns will point to either the main_pmns or + * a pmns from a version 2 archive context. + */ +static __pmnsTree *curr_pmns; + +/* The main_pmns points to the loaded PMNS (not from archive). */ +static __pmnsTree *main_pmns; + + +/* == 1 if PMNS loaded and __pmExportPMNS has been called */ +static int export; + +static int havePmLoadCall; +static int useExtPMNS; /* set by __pmUsePMNS() */ + +static int load(const char *filename, int dupok); +static __pmnsNode *locate(const char *name, __pmnsNode *root); + + +/* + * Set current pmns to an externally supplied PMNS. + * Useful for testing the API routines during debugging. + */ +void +__pmUsePMNS(__pmnsTree *t) +{ + PM_INIT_LOCKS(); + PM_LOCK(__pmLock_libpcp); + useExtPMNS = 1; + curr_pmns = t; + PM_UNLOCK(__pmLock_libpcp); +} + +static char * +pmPMNSLocationStr(int location) +{ + if (location < 0) { + /* see thread-safe note above */ + static char locerr[PM_MAXERRMSGLEN]; + return pmErrStr_r(location, locerr, sizeof(locerr)); + } + + switch(location) { + case PMNS_LOCAL: return "Local"; + case PMNS_REMOTE: return "Remote"; + case PMNS_ARCHIVE: return "Archive"; + } + return "Internal Error"; +} + + +static int +LoadDefault(char *reason_msg) +{ + if (main_pmns == NULL) { +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_PMNS) { + fprintf(stderr, + "pmGetPMNSLocation: Loading local PMNS for %s PMAPI context\n", + reason_msg); + } +#endif + if (load(PM_NS_DEFAULT, 0) < 0) + return PM_ERR_NOPMNS; + else + return PMNS_LOCAL; + } + return PMNS_LOCAL; +} + +/* + * Return the pmns_location. Possibly load the default PMNS. + */ +int +pmGetPMNSLocation(void) +{ + int pmns_location = PM_ERR_NOPMNS; + int n; + int sts; + + PM_INIT_LOCKS(); + PM_LOCK(__pmLock_libpcp); + if (useExtPMNS) { + PM_UNLOCK(__pmLock_libpcp); + pmns_location = PMNS_LOCAL; + goto done; + } + PM_UNLOCK(__pmLock_libpcp); + + /* + * Determine if we are to use PDUs or local PMNS file. + * Load PMNS if necessary. + */ + if (!havePmLoadCall) { + __pmContext *ctxp; + int version; + + if ((n = pmWhichContext()) >= 0 && (ctxp = __pmHandleToPtr(n)) != NULL) { + switch(ctxp->c_type) { + case PM_CONTEXT_HOST: + if (ctxp->c_pmcd->pc_fd == -1) { + pmns_location = PM_ERR_IPC; + goto done; + } + if ((sts = version = __pmVersionIPC(ctxp->c_pmcd->pc_fd)) < 0) { + char errmsg[PM_MAXERRMSGLEN]; + __pmNotifyErr(LOG_ERR, + "pmGetPMNSLocation: version lookup failed " + "(context=%d, fd=%d): %s", + n, ctxp->c_pmcd->pc_fd, pmErrStr_r(sts, errmsg, sizeof(errmsg))); + pmns_location = PM_ERR_NOPMNS; + } + else if (version == PDU_VERSION2) { + pmns_location = PMNS_REMOTE; + } + else { + __pmNotifyErr(LOG_ERR, + "pmGetPMNSLocation: bad host PDU version " + "(context=%d, fd=%d, ver=%d)", + n, ctxp->c_pmcd->pc_fd, version); + pmns_location = PM_ERR_NOPMNS; + } + break; + + case PM_CONTEXT_LOCAL: + if (PM_MULTIPLE_THREADS(PM_SCOPE_DSO_PMDA)) + /* Local context requires single-threaded applications */ + pmns_location = PM_ERR_THREAD; + else + pmns_location = LoadDefault("local"); + break; + + case PM_CONTEXT_ARCHIVE: + version = ctxp->c_archctl->ac_log->l_label.ill_magic & 0xff; + if (version == PM_LOG_VERS02) { + pmns_location = PMNS_ARCHIVE; + PM_LOCK(__pmLock_libpcp); + curr_pmns = ctxp->c_archctl->ac_log->l_pmns; + PM_UNLOCK(__pmLock_libpcp); + } + else { + __pmNotifyErr(LOG_ERR, "pmGetPMNSLocation: bad archive " + "version (context=%d, fd=%d, ver=%d)", + n, ctxp->c_pmcd->pc_fd, version); + pmns_location = PM_ERR_NOPMNS; + } + break; + + default: + __pmNotifyErr(LOG_ERR, "pmGetPMNSLocation: bogus context " + "type: %d", ctxp->c_type); + pmns_location = PM_ERR_NOPMNS; + break; + } + PM_UNLOCK(ctxp->c_lock); + } + else { + pmns_location = PM_ERR_NOPMNS; /* no context for client */ + } + } + else { /* have explicit external load call */ + if (main_pmns == NULL) + pmns_location = PM_ERR_NOPMNS; + else + pmns_location = PMNS_LOCAL; + } + + PM_LOCK(__pmLock_libpcp); +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_PMNS) { + static int last_pmns_location = -1; + + if (pmns_location != last_pmns_location) { + fprintf(stderr, "pmGetPMNSLocation() -> %s\n", + pmPMNSLocationStr(pmns_location)); + last_pmns_location = pmns_location; + } + } +#endif + + /* fix up curr_pmns for API ops */ + if (pmns_location == PMNS_LOCAL) + curr_pmns = main_pmns; + PM_UNLOCK(__pmLock_libpcp); + +done: + return pmns_location; +} + +/* + * Our own PMNS locator. Don't distinguish between ARCHIVE or LOCAL. + */ +static int +GetLocation(void) +{ + int loc = pmGetPMNSLocation(); + + if (loc == PMNS_ARCHIVE) + return PMNS_LOCAL; + return loc; +} + +/* + * For debugging, call via __pmDumpNameSpace() or __pmDumpNameNode() + * + * verbosity is 0 (name), 1 (names and pmids) or 2 (names, pmids and + * linked-list structures) + */ +static void +dumptree(FILE *f, int level, __pmnsNode *rp, int verbosity) +{ + int i; + __pmID_int *pp; + + if (rp != NULL) { + if (verbosity > 1) + fprintf(f, "" PRINTF_P_PFX "%p", rp); + for (i = 0; i < level; i++) { + fprintf(f, " "); + } + fprintf(f, " %-16.16s", rp->name); + pp = (__pmID_int *)&rp->pmid; + if (verbosity > 0 && rp->first == NULL) + fprintf(f, " %d %d.%d.%d 0x%08x", rp->pmid, + pp->domain, pp->cluster, pp->item, + rp->pmid); + if (verbosity > 1) { + fprintf(f, "\t[first: "); + if (rp->first) fprintf(f, "" PRINTF_P_PFX "%p", rp->first); + else fprintf(f, "<null>"); + fprintf(f, " next: "); + if (rp->next) fprintf(f, "" PRINTF_P_PFX "%p", rp->next); + else fprintf(f, "<null>"); + fprintf(f, " parent: "); + if (rp->parent) fprintf(f, "" PRINTF_P_PFX "%p", rp->parent); + else fprintf(f, "<null>"); + fprintf(f, " hash: "); + if (rp->hash) fprintf(f, "" PRINTF_P_PFX "%p", rp->hash); + else fprintf(f, "<null>"); + } + fputc('\n', f); + dumptree(f, level+1, rp->first, verbosity); + dumptree(f, level, rp->next, verbosity); + } +} + +static void +err(char *s) +{ + if (lineno > 0) + pmprintf("[%s:%d] ", fname, lineno); + pmprintf("Error Parsing ASCII PMNS: %s\n", s); + if (lineno > 0) { + char *p; + pmprintf(" %s", linebuf); + for (p = linebuf; *p; p++) + ; + if (p[-1] != '\n') + pmprintf("\n"); + if (linep) { + p = linebuf; + for (p = linebuf; p < linep; p++) { + if (!isspace((int)*p)) + *p = ' '; + } + *p++ = '^'; + *p++ = '\n'; + *p = '\0'; + pmprintf(" %s", linebuf); + } + } + pmflush(); +} + +/* + * lexical analyser for loading the ASCII pmns + */ +static int +lex(int reset) +{ + static int first = 1; + static FILE *fin; + static char *lp; + char *tp; + int colon; + int type; + int d, c, i; + __pmID_int pmid_int; + + if (reset) { + /* reset! */ + linep = NULL; + first = 1; + return 0; + } + + if (first) { + char *alt; + char cmd[80+MAXPATHLEN]; + + first = 0; + if ((alt = getenv("PCP_ALT_CPP")) != NULL) { + /* $PCP_ALT_CPP used in the build before pmcpp installed */ + snprintf(cmd, sizeof(cmd), "%s %s", alt, fname); + } + else { + /* the normal case ... */ + int sep = __pmPathSeparator(); + char *bin_dir = pmGetConfig("PCP_BINADM_DIR"); + snprintf(cmd, sizeof(cmd), "%s%c%s %s", bin_dir, sep, "pmcpp" EXEC_SUFFIX, fname); + } + + fin = popen(cmd, "r"); + if (fin == NULL) + return -oserror(); + + lp = linebuf; + *lp = '\0'; + } + + while (*lp && isspace((int)*lp)) lp++; + + while (*lp == '\0') { + for ( ; ; ) { + char *p; + char *q; + int inspace = 0; + + if (fgets(linebuf, sizeof(linebuf), fin) == NULL) { + if (pclose(fin) != 0) { + lineno = -1; /* We're outside of line counting range now */ + err("pmcpp returned non-zero exit status"); + return PM_ERR_PMNS; + } else { + return 0; + } + } + for (q = p = linebuf; *p; p++) { + if (isspace((int)*p)) { + if (!inspace) { + if (q > linebuf && q[-1] != ':') + *q++ = *p; + inspace = 1; + } + } + else if (*p == ':') { + if (inspace) { + q--; + inspace = 0; + } + *q++ = *p; + } + else { + *q++ = *p; + inspace = 0; + } + } + if (p[-1] != '\n') { + err("Absurdly long line, cannot recover"); + return PM_ERR_PMNS; + } + *q = '\0'; + if (linebuf[0] == '#') { + /* pmcpp line number control line */ + if (sscanf(linebuf, "# %d \"%s", &lineno, fname) != 2) { + err("Illegal line number control number"); + return PM_ERR_PMNS; + } + --lineno; + for (p = fname; *p; p++) + ; + *--p = '\0'; + continue; + } + else + lineno++; + lp = linebuf; + while (*lp && isspace((int)*lp)) lp++; + break; + } + } + + linep = lp; + tp = tokbuf; + while (!isspace((int)*lp)) + *tp++ = *lp++; + *tp = '\0'; + + if (tokbuf[0] == '{' && tokbuf[1] == '\0') return LBRACE; + else if (tokbuf[0] == '}' && tokbuf[1] == '\0') return RBRACE; + else if (isalpha((int)tokbuf[0])) { + type = NAME; + for (tp = &tokbuf[1]; *tp; tp++) { + if (*tp == '.') + type = PATH; + else if (!isalpha((int)*tp) && !isdigit((int)*tp) && *tp != '_') + break; + } + if (*tp == '\0') return type; + } + colon = 0; + for (tp = tokbuf; *tp; tp++) { + if (*tp == ':') { + if (++colon > 3) return BOGUS; + } + else if (!isdigit((int)*tp) && *tp != '*') return BOGUS; + } + + /* + * Internal PMID format + * domain 9 bits + * cluster 12 bits + * item 10 bits + */ + if (sscanf(tokbuf, "%d:%d:%d", &d, &c, &i) == 3) { + if (d > 510) { + err("Illegal domain field in PMID"); + return BOGUS; + } + else if (c > 4095) { + err("Illegal cluster field in PMID"); + return BOGUS; + } + else if (i > 1023) { + err("Illegal item field in PMID"); + return BOGUS; + } + pmid_int.flag = 0; + pmid_int.domain = d; + pmid_int.cluster = c; + pmid_int.item = i; + } + else { + for (tp = tokbuf; *tp; tp++) { + if (*tp == ':') { + if (strcmp("*:*", ++tp) != 0) { + err("Illegal PMID"); + return BOGUS; + } + break; + } + } + if (sscanf(tokbuf, "%d:", &d) != 1) { + err("Illegal PMID"); + return BOGUS; + } + if (d > 510) { + err("Illegal domain field in dynamic PMID"); + return BOGUS; + } + else { + /* + * this node is the base of a dynamic subtree in the PMNS + * ... identified by setting the domain field to the reserved + * value DYNAMIC_PMID and storing the real domain of the PMDA + * that can enumerate the subtree in the cluster field, while + * the item field is not used (and set to zero) + */ + pmid_int.flag = 0; + pmid_int.domain = DYNAMIC_PMID; + pmid_int.cluster = d; + pmid_int.item = 0; + } + } + tokpmid = *(pmID *)&pmid_int; + + return PMID; +} + +/* + * Remove the named node from the seen list and return it. + * The seen-list is a list of subtrees from pass 1. + */ + +static __pmnsNode * +findseen(char *name) +{ + __pmnsNode *np; + __pmnsNode *lnp; /* last np */ + + for (np = seen, lnp = NULL; np != NULL; lnp = np, np = np->next) { + if (strcmp(np->name, name) == 0) { + if (np == seen) + seen = np->next; + else + lnp->next = np->next; + np->next = NULL; + return np; + } + } + return NULL; +} + +/* + * Attach the subtrees from pass-1 to form a whole + * connected tree. + */ +static int +attach(char *base, __pmnsNode *rp) +{ + int i; + __pmnsNode *np; + __pmnsNode *xp; + char *path; + + if (rp != NULL) { + for (np = rp->first; np != NULL; np = np->next) { + if (np->pmid == PM_ID_NULL) { + /* non-terminal node ... */ + if (*base == '\0') { + if ((path = (char *)malloc(strlen(np->name)+1)) == NULL) + return -oserror(); + strcpy(path, np->name); + } + else { + if ((path = (char *)malloc(strlen(base)+strlen(np->name)+2)) == NULL) + return -oserror(); + strcpy(path, base); + strcat(path, "."); + strcat(path, np->name); + } + if ((xp = findseen(path)) == NULL) { + snprintf(linebuf, sizeof(linebuf), "Cannot find definition for non-terminal node \"%s\" in name space", + path); + err(linebuf); + free(path); + return PM_ERR_PMNS; + } + np->first = xp->first; + /* node xp and name no longer needed */ + free(xp->name); + free(xp); + seenpmid--; + i = attach(path, np); + free(path); + if (i != 0) + return i; + } + } + } + return 0; +} + +/* + * Create a fullpath name by walking from the current + * tree node up to the root. + */ +static int +backname(__pmnsNode *np, char **name) +{ + __pmnsNode *xp; + char *p; + int nch; + + nch = 0; + xp = np; + while (xp->parent != NULL) { + nch += (int)strlen(xp->name)+1; + xp = xp->parent; + } + + if ((p = (char *)malloc(nch)) == NULL) + return -oserror(); + + p[--nch] = '\0'; + xp = np; + while (xp->parent != NULL) { + int xl; + + xl = (int)strlen(xp->name); + nch -= xl; + strncpy(&p[nch], xp->name, xl); + xp = xp->parent; + if (xp->parent == NULL) + break; + else + p[--nch] = '.'; + } + *name = p; + + return 0; +} + +/* + * Fixup the parent pointers of the tree. + * Fill in the hash table with nodes from the tree. + * Hashing is done on pmid. + */ +static int +backlink(__pmnsTree *tree, __pmnsNode *root, int dupok) +{ + __pmnsNode *np; + int status; + + for (np = root->first; np != NULL; np = np->next) { + np->parent = root; + if (np->pmid != PM_ID_NULL) { + int i; + __pmnsNode *xp; + i = np->pmid % tree->htabsize; + for (xp = tree->htab[i]; xp != NULL; xp = xp->hash) { + if (xp->pmid == np->pmid && !dupok && + (pmid_domain(np->pmid) != DYNAMIC_PMID || pmid_item(np->pmid) != 0)) { + char *nn, *xn; + char strbuf[20]; + backname(np, &nn); + backname(xp, &xn); + snprintf(linebuf, sizeof(linebuf), "Duplicate metric id (%s) in name space for metrics \"%s\" and \"%s\"\n", + pmIDStr_r(np->pmid, strbuf, sizeof(strbuf)), nn, xn); + err(linebuf); + free(nn); + free(xn); + return PM_ERR_PMNS; + } + } + np->hash = tree->htab[i]; + tree->htab[i] = np; + } + if ((status = backlink(tree, np, dupok))) + return status; + } + return 0; +} + +/* + * Build up the whole tree by attaching the subtrees + * from the seen list. + * Create the hash table keyed on pmid. + * + */ +static int +pass2(int dupok) +{ + __pmnsNode *np; + int status; + + lineno = -1; + + main_pmns = (__pmnsTree*)malloc(sizeof(*main_pmns)); + if (main_pmns == NULL) { + return -oserror(); + } + + /* Get the root subtree out of the seen list */ + if ((main_pmns->root = findseen("root")) == NULL) { + err("No name space entry for \"root\""); + return PM_ERR_PMNS; + } + + if (findseen("root") != NULL) { + err("Multiple name space entries for \"root\""); + return PM_ERR_PMNS; + } + + /* Build up main tree from subtrees in seen-list */ + if ((status = attach("", main_pmns->root))) + return status; + + /* Make sure all subtrees have been used in the main tree */ + for (np = seen; np != NULL; np = np->next) { + snprintf(linebuf, sizeof(linebuf), "Disconnected subtree (\"%s\") in name space", np->name); + err(linebuf); + status = PM_ERR_PMNS; + } + if (status) + return status; + + main_pmns->symbol = NULL; + main_pmns->contiguous = 0; + main_pmns->mark_state = UNKNOWN_MARK_STATE; + + return __pmFixPMNSHashTab(main_pmns, seenpmid, dupok); +} + + +/* + * clear/set the "mark" bit used by pmTrimNameSpace, for all pmids + */ +static void +mark_all(__pmnsTree *pmns, int bit) +{ + int i; + __pmnsNode *np; + __pmnsNode *pp; + + if (pmns->mark_state == bit) + return; + + pmns->mark_state = bit; + for (i = 0; i < pmns->htabsize; i++) { + for (np = pmns->htab[i]; np != NULL; np = np->hash) { + for (pp = np ; pp != NULL; pp = pp->parent) { + if (bit) + pp->pmid |= MARK_BIT; + else + pp->pmid &= ~MARK_BIT; + } + } + } +} + +/* + * clear/set the "mark" bit used by pmTrimNameSpace, for one pmid, and + * for all parent nodes on the path to the root of the PMNS + */ +static void +mark_one(__pmnsTree *pmns, pmID pmid, int bit) +{ + __pmnsNode *np; + + if (pmns->mark_state == bit) + return; + + pmns->mark_state = UNKNOWN_MARK_STATE; + for (np = pmns->htab[pmid % pmns->htabsize]; np != NULL; np = np->hash) { + if ((np->pmid & PMID_MASK) == (pmid & PMID_MASK)) { + for ( ; np != NULL; np = np->parent) { + if (bit) + np->pmid |= MARK_BIT; + else + np->pmid &= ~MARK_BIT; + } + return; + } + } +} + + +/* + * Create a new empty PMNS for Adding nodes to. + * Use with __pmAddPMNSNode() and __pmFixPMNSHashTab() + */ +int +__pmNewPMNS(__pmnsTree **pmns) +{ + __pmnsTree *t = NULL; + __pmnsNode *np = NULL; + + t = (__pmnsTree*)malloc(sizeof(*main_pmns)); + if (t == NULL) + return -oserror(); + + /* Insert the "root" node first */ + if ((np = (__pmnsNode *)malloc(sizeof(*np))) == NULL) { + free(t); + return -oserror(); + } + np->pmid = PM_ID_NULL; + np->parent = np->first = np->hash = np->next = NULL; + np->name = strdup("root"); + if (np->name == NULL) { + free(t); + free(np); + return -oserror(); + } + + t->root = np; + t->htab = NULL; + t->htabsize = 0; + t->symbol = NULL; + t->contiguous = 0; + t->mark_state = UNKNOWN_MARK_STATE; + + *pmns = t; + return 0; +} + +/* + * Go through the tree and build a hash table. + * Fix up parent links while we're there. + * Unmark all nodes. + */ +int +__pmFixPMNSHashTab(__pmnsTree *tree, int numpmid, int dupok) +{ + int sts; + int htabsize = numpmid/5; + + /* + * make the average hash list no longer than 5, and the number + * of hash table entries not a multiple of 2, 3 or 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) + return -oserror(); + + PM_INIT_LOCKS(); + PM_LOCK(__pmLock_libpcp); + if ((sts = backlink(tree, tree->root, dupok)) < 0) { + PM_UNLOCK(__pmLock_libpcp); + return sts; + } + mark_all(tree, 0); + PM_UNLOCK(__pmLock_libpcp); + return 0; +} + + +/* + * Add a new node for fullpath, name, with pmid. + * Does NOT update the hash table; + * need to call __pmFixPMNSHashTab() for that. + * Recursive routine. + */ + +static int +AddPMNSNode(__pmnsNode *root, int pmid, const char *name) +{ + __pmnsNode *np = NULL; + const char *tail; + int nch; + + /* Traverse until '.' or '\0' */ + for (tail = name; *tail && *tail != '.'; tail++) + ; + + nch = (int)(tail - name); + + /* Compare name with all the child nodes */ + for (np = root->first; np != NULL; np = np->next) { + if (strncmp(name, np->name, (int)nch) == 0 && np->name[(int)nch] == '\0') + break; + } + + if (np == NULL) { /* no match with child */ + __pmnsNode *parent_np = root; + const char *name_p = name; + int is_first = 1; + + /* create nodes until reach leaf */ + + for ( ; ; ) { + if ((np = (__pmnsNode *)malloc(sizeof(*np))) == NULL) + return -oserror(); + + /* fixup name */ + if ((np->name = (char *)malloc(nch+1)) == NULL) { + free(np); + return -oserror(); + } + strncpy(np->name, name_p, nch); + np->name[nch] = '\0'; + + /* fixup some links */ + np->first = np->hash = np->next = NULL; + np->parent = parent_np; + if (is_first) { + is_first = 0; + if (root->first != NULL) { + /* chuck new node at front of list */ + np->next = root->first; + } + } + parent_np->first = np; + + /* at this stage, assume np is a non-leaf */ + np->pmid = PM_ID_NULL; + + parent_np = np; + if (*tail == '\0') + break; + name_p += nch+1; /* skip over node + dot */ + for (tail = name_p; *tail && *tail != '.'; tail++) + ; + nch = (int)(tail - name_p); + } + + np->pmid = pmid; /* set pmid of leaf node */ + return 0; + } + else if (*tail == '\0') { /* matched with whole path */ + if (np->pmid != pmid) + return PM_ERR_PMID; + else + return 0; + } + else { + return AddPMNSNode(np, pmid, tail+1); /* try matching with rest of pathname */ + } + +} + + +/* + * Add a new node for fullpath, name, with pmid. + * NOTE: Need to call __pmFixPMNSHashTab() to update hash table + * when have finished adding nodes. + */ +int +__pmAddPMNSNode(__pmnsTree *tree, int pmid, const char *name) +{ + if (tree->contiguous) { + /* Cannot add node to contiguously allocated tree! */ + return -EINVAL; + } + + return AddPMNSNode(tree->root, pmid, name); +} + +/* + * fsa for parser + * + * old token new + * 0 NAME 1 + * 0 PATH 1 + * 1 LBRACE 2 + * 2 NAME 3 + * 2 RBRACE 0 + * 3 NAME 3 + * 3 PMID 2 + * 3 RBRACE 0 + */ +static int +loadascii(int dupok) +{ + int state = 0; + int type; + __pmnsNode *np = NULL; /* pander to gcc */ + +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_PMNS) + fprintf(stderr, "loadascii(file=%s)\n", fname); +#endif + + + /* do some resets */ + lex(1); /* reset analyzer */ + seen = NULL; /* make seen-list empty */ + seenpmid = 0; + + + if (access(fname, R_OK) == -1) { + snprintf(linebuf, sizeof(linebuf), "Cannot open \"%s\"", fname); + err(linebuf); + return -oserror(); + } + lineno = 1; + + while ((type = lex(0)) > 0) { + switch (state) { + + case 0: + if (type != NAME && type != PATH) { + err("Expected NAME or PATH"); + return PM_ERR_PMNS; + } + state = 1; + break; + + case 1: + if (type != LBRACE) { + err("{ expected"); + return PM_ERR_PMNS; + } + state = 2; + break; + + case 2: + if (type == NAME) { + state = 3; + } + else if (type == RBRACE) { + state = 0; + } + else { + err("Expected NAME or }"); + return PM_ERR_PMNS; + } + break; + + case 3: + if (type == NAME) { + state = 3; + } + else if (type == PMID) { + np->pmid = tokpmid; + state = 2; +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_PMNS) { + char strbuf[20]; + fprintf(stderr, "pmLoadNameSpace: %s -> %s\n", + np->name, pmIDStr_r(np->pmid, strbuf, sizeof(strbuf))); + } +#endif + } + else if (type == RBRACE) { + state = 0; + } + else { + err("Expected NAME, PMID or }"); + return PM_ERR_PMNS; + } + break; + + } + + if (state == 1 || state == 3) { + if ((np = (__pmnsNode *)malloc(sizeof(*np))) == NULL) + return -oserror(); + seenpmid++; + if ((np->name = (char *)malloc(strlen(tokbuf)+1)) == NULL) { + free(np); + return -oserror(); + } + strcpy(np->name, tokbuf); + np->first = np->hash = np->next = np->parent = NULL; + np->pmid = PM_ID_NULL; + if (state == 1) { + np->next = seen; + seen = np; + } + else { + if (seen->hash) + seen->hash->next = np; + else + seen->first = np; + seen->hash = np; + } + } + else if (state == 0) { + if (seen) { + __pmnsNode *xp; + + for (np = seen->first; np != NULL; np = np->next) { + for (xp = np->next; xp != NULL; xp = xp->next) { + if (strcmp(xp->name, np->name) == 0) { + snprintf(linebuf, sizeof(linebuf), "Duplicate name \"%s\" in subtree for \"%s\"\n", + np->name, seen->name); + err(linebuf); + return PM_ERR_PMNS; + } + } + } + } + } + } + + if (type == 0) + type = pass2(dupok); + + + if (type == 0) { +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_PMNS) + fprintf(stderr, "Loaded ASCII PMNS\n"); +#endif + } + + return type; +} + +static const char * +getfname(const char *filename) +{ + /* + * 0xffffffff is there to maintain backwards compatibility with PCP 1.0 + */ + if (filename == PM_NS_DEFAULT || (__psint_t)filename == 0xffffffff) { + char *def_pmns; + + def_pmns = getenv("PMNS_DEFAULT"); + if (def_pmns != NULL) { + /* get default PMNS name from environment */ + return def_pmns; + } + else { + static char repname[MAXPATHLEN]; + int sep = __pmPathSeparator(); + snprintf(repname, sizeof(repname), "%s%c" "pmns" "%c" "root", + pmGetConfig("PCP_VAR_DIR"), sep, sep); + return repname; + } + } + return filename; +} + +int +__pmHasPMNSFileChanged(const char *filename) +{ + const char *f; + int sts; + + PM_INIT_LOCKS(); + PM_LOCK(__pmLock_libpcp); + + f = getfname(filename); + if (f == NULL) { + /* error encountered -> must have changed :) */ + sts = 1; + goto done; + } + + /* if still using same filename ... */ + if (strcmp(f, fname) == 0) { + struct stat statbuf; + + if (stat(f, &statbuf) == 0) { + /* If the modification times have changed */ +#if defined(HAVE_ST_MTIME_WITH_E) && defined(HAVE_STAT_TIME_T) +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_PMNS) { + fprintf(stderr, + "__pmHasPMNSFileChanged(%s) -> %s last=%d now=%d\n", + filename == PM_NS_DEFAULT || + (__psint_t)filename == 0xffffffff ? + "PM_NS_DEFAULT" : filename, + f, (int)last_mtim, (int)statbuf.st_mtime); + } +#endif + sts = (statbuf.st_mtime == last_mtim) ? 0 : 1; + goto done; +#elif defined(HAVE_ST_MTIME_WITH_SPEC) +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_PMNS) { + fprintf(stderr, + "__pmHasPMNSFileChanged(%s) -> %s last=%d.%09ld now=%d.%09ld\n", + filename == PM_NS_DEFAULT || + (__psint_t)filename == 0xffffffff ? + "PM_NS_DEFAULT" : filename, + f, (int)last_mtim.tv_sec, last_mtim.tv_nsec, + (int)statbuf.st_mtimespec.tv_sec, + statbuf.st_mtimespec.tv_nsec); + } +#endif + sts = (statbuf.st_mtimespec.tv_sec == last_mtim.tv_sec && + statbuf.st_mtimespec.tv_nsec == last_mtim.tv_nsec) ? 0 : 1; + goto done; +#elif defined(HAVE_STAT_TIMESTRUC) || defined(HAVE_STAT_TIMESPEC) || defined(HAVE_STAT_TIMESPEC_T) +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_PMNS) { + fprintf(stderr, + "__pmHasPMNSFileChanged(%s) -> %s last=%d.%09ld now=%d.%09ld\n", + filename == PM_NS_DEFAULT || + (__psint_t)filename == 0xffffffff ? + "PM_NS_DEFAULT" : filename, + f, (int)last_mtim.tv_sec, last_mtim.tv_nsec, + (int)statbuf.st_mtim.tv_sec, statbuf.st_mtim.tv_nsec); + } +#endif + sts = (statbuf.st_mtim.tv_sec == last_mtim.tv_sec && + (statbuf.st_mtim.tv_nsec == last_mtim.tv_nsec)) ? 0 : 1; + goto done; +#else +!bozo! +#endif + } + else { + /* error encountered -> must have changed */ + sts = 1; + goto done; + } + } + /* different filenames at least */ + sts = 1; + +done: + PM_UNLOCK(__pmLock_libpcp); + return sts; +} + +static int +load(const char *filename, int dupok) +{ + int i = 0; + + if (main_pmns != NULL) { + if (export) { + export = 0; + + /* + * drop the loaded PMNS ... huge memory leak, but it is + * assumed the caller has saved the previous PMNS after calling + * __pmExportPMNS() ... only user of this service is pmnsmerge + */ + main_pmns = NULL; + } + else { + return PM_ERR_DUPPMNS; + } + } + + strcpy(fname, getfname(filename)); + +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_PMNS) + fprintf(stderr, "load(name=%s, dupok=%d) lic case=%d fname=%s\n", + filename, dupok, i, fname); +#endif + + /* Note modification time of pmns file */ + { + struct stat statbuf; + + if (stat(fname, &statbuf) == 0) { +#if defined(HAVE_ST_MTIME_WITH_E) + last_mtim = statbuf.st_mtime; /* possible struct assignment */ +#elif defined(HAVE_ST_MTIME_WITH_SPEC) + last_mtim = statbuf.st_mtimespec; /* possible struct assignment */ +#else + last_mtim = statbuf.st_mtim; /* possible struct assignment */ +#endif + } + } + + /* + * load ASCII PMNS + */ + return loadascii(dupok); +} + +/* + * just for pmnsmerge to use + */ +__pmnsTree* +__pmExportPMNS(void) +{ + PM_INIT_LOCKS(); + PM_LOCK(__pmLock_libpcp); + export = 1; + PM_UNLOCK(__pmLock_libpcp); + + /* + * Warning: this is _not_ thread-safe, and cannot be guarded/protected + */ + return main_pmns; +} + +/* + * Find and return the named node in the tree, root. + */ +static __pmnsNode * +locate(const char *name, __pmnsNode *root) +{ + const char *tail; + ptrdiff_t nch; + __pmnsNode *np; + + /* Traverse until '.' or '\0' */ + for (tail = name; *tail && *tail != '.'; tail++) + ; + + nch = tail - name; + + /* Compare name with all the child nodes */ + for (np = root->first; np != NULL; np = np->next) { + if (strncmp(name, np->name, (int)nch) == 0 && np->name[(int)nch] == '\0' && + (np->pmid & MARK_BIT) == 0) + break; + } + + if (np == NULL) /* no match with child */ + return NULL; + else if (*tail == '\0') /* matched with whole path */ + return np; + else + return locate(tail+1, np); /* try matching with rest of pathname */ +} + +/* + * PMAPI routines from here down + */ + +/* + * As of PCP 3.6, there is _only_ the ASCII version of the PMNS + */ +int +pmLoadNameSpace(const char *filename) +{ + return pmLoadASCIINameSpace(filename, 0); +} + +int +pmLoadASCIINameSpace(const char *filename, int dupok) +{ + int sts; + + PM_INIT_LOCKS(); + PM_LOCK(__pmLock_libpcp); + havePmLoadCall = 1; + sts = load(filename, dupok); + PM_UNLOCK(__pmLock_libpcp); + return sts; +} + +/* + * Assume that each node has been malloc'ed separately. + * This is the case for an ASCII loaded PMNS. + * Traverse entire tree and free each node. + */ +static void +FreeTraversePMNS(__pmnsNode *this) +{ + __pmnsNode *np, *next; + + if (this == NULL) + return; + + /* Free child sub-trees */ + for (np = this->first; np != NULL; np = next) { + next = np->next; + FreeTraversePMNS(np); + } + + free(this->name); + free(this); +} + +void +__pmFreePMNS(__pmnsTree *pmns) +{ + if (pmns != NULL) { + if (pmns->contiguous) { + free(pmns->root); + free(pmns->htab); + free(pmns->symbol); + } + else { + free(pmns->htab); + FreeTraversePMNS(pmns->root); + } + + free(pmns); + } +} + +void +pmUnloadNameSpace(void) +{ + PM_INIT_LOCKS(); + PM_LOCK(__pmLock_libpcp); + havePmLoadCall = 0; + __pmFreePMNS(main_pmns); + main_pmns = NULL; + PM_UNLOCK(__pmLock_libpcp); +} + +int +pmLookupName(int numpmid, char *namelist[], pmID pmidlist[]) +{ + int pmns_location = GetLocation(); + int sts = 0; + __pmContext *ctxp; + int c_type; + int lsts; + int ctx; + int i; + int nfail = 0; + + if (numpmid < 1) { +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_PMNS) { + fprintf(stderr, "pmLookupName(%d, ...) bad numpmid!\n", numpmid); + } +#endif + return PM_ERR_TOOSMALL; + } + + PM_INIT_LOCKS(); + + ctx = lsts = pmWhichContext(); + if (lsts >= 0) { + ctxp = __pmHandleToPtr(ctx); + c_type = ctxp->c_type; + } + else { + ctxp = NULL; + /* + * set c_type to be NONE of PM_CONTEXT_HOST, PM_CONTEXT_ARCHIVE + * nor PM_CONTEXT_LOCAL + */ + c_type = 0; + } + if (ctxp != NULL && c_type == PM_CONTEXT_LOCAL && PM_MULTIPLE_THREADS(PM_SCOPE_DSO_PMDA)) { + /* Local context requires single-threaded applications */ + PM_UNLOCK(ctxp->c_lock); + return PM_ERR_THREAD; + } + + /* + * Guarantee that derived metrics preparation is done, for all possible + * paths through this routine which might end at "Try derived metrics.." + * below. + */ + memset(pmidlist, PM_ID_NULL, numpmid * sizeof(pmID)); + + if (pmns_location < 0) { + if (ctxp != NULL) + PM_UNLOCK(ctxp->c_lock); + sts = pmns_location; + /* only hope is derived metrics ... set up for this */ + nfail += numpmid; + } + else if (pmns_location == PMNS_LOCAL) { + char *xname; + char *xp; + __pmnsNode *np; + + if (ctxp != NULL) + PM_UNLOCK(ctxp->c_lock); + for (i = 0; i < numpmid; i++) { + /* + * if we locate the name and it is a leaf in the PMNS + * this is good + */ + PM_LOCK(__pmLock_libpcp); + np = locate(namelist[i], curr_pmns->root); + PM_UNLOCK(__pmLock_libpcp); + if (np != NULL ) { + if (np->first == NULL) + pmidlist[i] = np->pmid; + else { + sts = PM_ERR_NONLEAF; + nfail++; + } + continue; + } + nfail++; + /* + * did not match name in PMNS ... try for prefix matching + * the name to the root of a dynamic subtree of the PMNS, + * or possibly we're using a local context and then we may + * be able to ship request to PMDA + */ + xname = strdup(namelist[i]); + if (xname == NULL) { + __pmNoMem("pmLookupName", strlen(namelist[i])+1, PM_RECOV_ERR); + sts = -oserror(); + continue; + } + while ((xp = rindex(xname, '.')) != NULL) { + *xp = '\0'; + lsts = 0; + PM_LOCK(__pmLock_libpcp); + np = locate(xname, curr_pmns->root); + PM_UNLOCK(__pmLock_libpcp); + if (np != NULL && np->first == NULL && + pmid_domain(np->pmid) == DYNAMIC_PMID && + pmid_item(np->pmid) == 0) { + /* root of dynamic subtree */ + if (c_type == PM_CONTEXT_LOCAL) { + /* have PM_CONTEXT_LOCAL ... ship request to PMDA */ + int domain = ((__pmID_int *)&np->pmid)->cluster; + __pmDSO *dp; + if ((dp = __pmLookupDSO(domain)) == NULL) { + if (sts >= 0) sts = PM_ERR_NOAGENT; + break; + } + if (dp->dispatch.comm.pmda_interface >= PMDA_INTERFACE_5) + dp->dispatch.version.four.ext->e_context = ctx; + if (dp->dispatch.comm.pmda_interface >= PMDA_INTERFACE_4) { + lsts = dp->dispatch.version.four.pmid(namelist[i], &pmidlist[i], dp->dispatch.version.four.ext); + if (lsts >= 0) + nfail--; + + break; + } + } + else { + /* No PM_LOCAL_CONTEXT, use PMID from PMNS */ + pmidlist[i] = np->pmid; + nfail--; + break; + } + } + } + free(xname); + } + + sts = (sts == 0 ? numpmid - nfail : sts); + +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_PMNS) { + int i; + char strbuf[20]; + fprintf(stderr, "pmLookupName(%d, ...) using local PMNS returns %d and ...\n", + numpmid, sts); + for (i = 0; i < numpmid; i++) { + fprintf(stderr, " name[%d]: \"%s\"", i, namelist[i]); + if (sts >= 0) + fprintf(stderr, " PMID: 0x%x %s", + pmidlist[i], pmIDStr_r(pmidlist[i], strbuf, sizeof(strbuf))); + fputc('\n', stderr); + } + } +#endif + } + else { + /* + * PMNS_REMOTE so there must be a current host context + */ + assert(c_type == PM_CONTEXT_HOST); +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_PMNS) { + fprintf(stderr, "pmLookupName: request_names ->"); + for (i = 0; i < numpmid; i++) + fprintf(stderr, " [%d] %s", i, namelist[i]); + fputc('\n', stderr); + } +#endif + PM_LOCK(ctxp->c_pmcd->pc_lock); + sts = __pmSendNameList(ctxp->c_pmcd->pc_fd, __pmPtrToHandle(ctxp), + numpmid, namelist, NULL); + if (sts < 0) + sts = __pmMapErrno(sts); + else { + __pmPDU *pb; + int pinpdu; + + pinpdu = sts = __pmGetPDU(ctxp->c_pmcd->pc_fd, ANY_SIZE, + ctxp->c_pmcd->pc_tout_sec, &pb); + if (sts == PDU_PMNS_IDS) { + /* Note: + * pmLookupName may return an error even though + * it has a valid list of ids. + * This is why we need op_status. + */ + int op_status; + sts = __pmDecodeIDList(pb, numpmid, pmidlist, &op_status); + if (sts >= 0) + sts = op_status; + } + else if (sts == PDU_ERROR) { + __pmDecodeError(pb, &sts); + } + else if (sts != PM_ERR_TIMEOUT) { + sts = PM_ERR_IPC; + } + if (pinpdu > 0) + __pmUnpinPDUBuf(pb); + if (sts >= 0) + nfail = numpmid - sts; +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_PMNS) { + char strbuf[20]; + char errmsg[PM_MAXERRMSGLEN]; + fprintf(stderr, "pmLookupName: receive_names <-"); + if (sts >= 0) { + for (i = 0; i < numpmid; i++) + fprintf(stderr, " [%d] %s", i, pmIDStr_r(pmidlist[i], strbuf, sizeof(strbuf))); + fputc('\n', stderr); + } + else + fprintf(stderr, " %s\n", pmErrStr_r(sts, errmsg, sizeof(errmsg))); + } +#endif + } + PM_UNLOCK(ctxp->c_pmcd->pc_lock); + PM_UNLOCK(ctxp->c_lock); + } + + if (sts < 0 || nfail > 0) { + /* + * Try derived metrics for any remaining unknown pmids. + * The return status is a little tricky ... prefer the status + * from above unless all of the remaining unknown PMIDs are + * resolved by __dmgetpmid() in which case success (numpmid) + * is the right return status + */ + nfail = 0; + for (i = 0; i < numpmid; i++) { + if (pmidlist[i] == PM_ID_NULL) { + lsts = __dmgetpmid(namelist[i], &pmidlist[i]); + if (lsts < 0) { + nfail++; + } +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_DERIVE) { + char strbuf[20]; + char errmsg[PM_MAXERRMSGLEN]; + fprintf(stderr, "__dmgetpmid: metric \"%s\" -> ", namelist[i]); + if (lsts < 0) + fprintf(stderr, "%s\n", pmErrStr_r(lsts, errmsg, sizeof(errmsg))); + else + fprintf(stderr, "PMID %s\n", pmIDStr_r(pmidlist[i], strbuf, sizeof(strbuf))); + } +#endif + } + } + if (nfail == 0) + sts = numpmid; + } + + /* + * special case for a single metric, PM_ERR_NAME is more helpful than + * returning 0 and having one PM_ID_NULL pmid + */ + if (sts == 0 && numpmid == 1) + sts = PM_ERR_NAME; + +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_PMNS) { + fprintf(stderr, "pmLookupName(%d, ...) -> ", numpmid); + if (sts < 0) { + char errmsg[PM_MAXERRMSGLEN]; + fprintf(stderr, "%s\n", pmErrStr_r(sts, errmsg, sizeof(errmsg))); + } + else + fprintf(stderr, "%d\n", sts); + } +#endif + + return sts; +} + +static int +GetChildrenStatusRemote(__pmContext *ctxp, const char *name, + char ***offspring, int **statuslist) +{ + int n; + + PM_LOCK(ctxp->c_pmcd->pc_lock); + n = __pmSendChildReq(ctxp->c_pmcd->pc_fd, __pmPtrToHandle(ctxp), + name, statuslist == NULL ? 0 : 1); + if (n < 0) + n = __pmMapErrno(n); + else { + __pmPDU *pb; + int pinpdu; + + pinpdu = n = __pmGetPDU(ctxp->c_pmcd->pc_fd, ANY_SIZE, + ctxp->c_pmcd->pc_tout_sec, &pb); + if (n == PDU_PMNS_NAMES) { + int numnames; + n = __pmDecodeNameList(pb, &numnames, offspring, statuslist); + if (n >= 0) + n = numnames; + } + else if (n == PDU_ERROR) + __pmDecodeError(pb, &n); + else if (n != PM_ERR_TIMEOUT) + n = PM_ERR_IPC; + if (pinpdu > 0) + __pmUnpinPDUBuf(pb); + } + PM_UNLOCK(ctxp->c_pmcd->pc_lock); + + return n; +} + +static void +stitch_list(int *num, char ***offspring, int **statuslist, int x_num, char **x_offspring, int *x_statuslist) +{ + /* + * so this gets tricky ... need to stitch the additional metrics + * (derived metrics or dynamic metrics) at the end of the existing + * metrics (if any) after removing any duplicates (!) ... and honour + * the bizarre pmGetChildren contract in terms of malloc'ing the + * result arrays + */ + int n_num; + char **n_offspring; + int *n_statuslist = NULL; + int i; + int j; + char *q; + size_t need; + + if (x_num == 0) { + /* nothing to do */ + return; + } + if (*num > 0) { + /* appending */ + n_num = *num + x_num; + } + else { + /* initializing */ + n_num = x_num; + } + + for (i = 0; i < x_num; i++) { + for (j = 0; j < *num; j++) { + if (strcmp(x_offspring[i], (*offspring)[j]) == 0) { + /* duplicate ... bugger */ + n_num--; + free(x_offspring[i]); + x_offspring[i] = NULL; + break; + } + } + } + + need = n_num*sizeof(char *); + for (j = 0; j < *num; j++) { + need += strlen((*offspring)[j]) + 1; + } + for (i = 0; i < x_num; i++) { + if (x_offspring[i] != NULL) { + need += strlen(x_offspring[i]) + 1; + } + } + if ((n_offspring = (char **)malloc(need)) == NULL) { + __pmNoMem("pmGetChildrenStatus: n_offspring", need, PM_FATAL_ERR); + /*NOTREACHED*/ + } + if (statuslist != NULL) { + if ((n_statuslist = (int *)malloc(n_num*sizeof(n_statuslist[0]))) == NULL) { + __pmNoMem("pmGetChildrenStatus: n_statuslist", n_num*sizeof(n_statuslist[0]), PM_FATAL_ERR); + /*NOTREACHED*/ + } + } + q = (char *)&n_offspring[n_num]; + for (j = 0; j < *num; j++) { + n_offspring[j] = q; + strcpy(q, (*offspring)[j]); + q += strlen(n_offspring[j]) + 1; + if (statuslist != NULL) + n_statuslist[j] = (*statuslist)[j]; + } + for (i = 0; i < x_num; i++) { + if (x_offspring[i] != NULL) { + n_offspring[j] = q; + strcpy(q, x_offspring[i]); + q += strlen(n_offspring[j]) + 1; + if (statuslist != NULL) + n_statuslist[j] = x_statuslist[i]; + j++; + } + } + if (*num > 0) { + free(*offspring); + if (statuslist != NULL) + free(*statuslist); + } + *num = n_num; + if (statuslist != NULL) + *statuslist = n_statuslist; + *offspring = n_offspring; +} + +/* + * It is allowable to pass in a statuslist arg of NULL. It is therefore + * important to check that this is not NULL before accessing it. + */ +int +pmGetChildrenStatus(const char *name, char ***offspring, int **statuslist) +{ + int *status = NULL; + int pmns_location = GetLocation(); + int num; + int dm_num; + char **dm_offspring; + int *dm_statuslist; + int sts; + int ctx; + __pmContext *ctxp; + + if (pmns_location < 0) + return pmns_location; + + if (name == NULL) + return PM_ERR_NAME; + + PM_INIT_LOCKS(); + + ctx = sts = pmWhichContext(); + if (sts >= 0) + ctxp = __pmHandleToPtr(sts); + else + ctxp = NULL; + + if (pmns_location == PMNS_LOCAL) { + __pmnsNode *np; + __pmnsNode *tnp; + int i; + int need; + char **result; + char *p; + +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_PMNS) { + fprintf(stderr, "pmGetChildren(name=\"%s\") [local]\n", name); + } +#endif + + /* avoids ambiguity, for errors and leaf nodes */ + *offspring = NULL; + num = 0; + if (statuslist) + *statuslist = NULL; + + PM_INIT_LOCKS(); + PM_LOCK(__pmLock_libpcp); + if (*name == '\0') + np = curr_pmns->root; /* use "" to name the root of the PMNS */ + else + np = locate(name, curr_pmns->root); + if (np == NULL) { + if (ctxp != NULL && ctxp->c_type == PM_CONTEXT_LOCAL) { + /* + * No match in PMNS and using PM_CONTEXT_LOCAL so for + * dynamic metrics, need to consider prefix matches back to + * the root on the PMNS to find a possible root of a dynamic + * subtree, and hence the domain of the responsible PMDA + */ + char *xname = strdup(name); + char *xp; + if (xname == NULL) { + __pmNoMem("pmGetChildrenStatus", strlen(name)+1, PM_RECOV_ERR); + num = -oserror(); + PM_UNLOCK(__pmLock_libpcp); + goto report; + } + while ((xp = rindex(xname, '.')) != NULL) { + *xp = '\0'; + np = locate(xname, curr_pmns->root); + if (np != NULL && np->first == NULL && + pmid_domain(np->pmid) == DYNAMIC_PMID && + pmid_item(np->pmid) == 0) { + int domain = ((__pmID_int *)&np->pmid)->cluster; + __pmDSO *dp; + if ((dp = __pmLookupDSO(domain)) == NULL) { + num = PM_ERR_NOAGENT; + free(xname); + PM_UNLOCK(__pmLock_libpcp); + goto check; + } + if (dp->dispatch.comm.pmda_interface >= PMDA_INTERFACE_5) + dp->dispatch.version.four.ext->e_context = ctx; + if (dp->dispatch.comm.pmda_interface >= PMDA_INTERFACE_4) { + char **x_offspring = NULL; + int *x_statuslist = NULL; + int x_num; + x_num = dp->dispatch.version.four.children( + name, 0, &x_offspring, &x_statuslist, + dp->dispatch.version.four.ext); + if (x_num < 0) + num = x_num; + else if (x_num > 0) { + stitch_list(&num, offspring, statuslist, + x_num, x_offspring, x_statuslist); + free(x_offspring); + free(x_statuslist); + } + free(xname); + PM_UNLOCK(__pmLock_libpcp); + goto check; + } + else { + /* Not PMDA_INTERFACE_4 or later */ + num = PM_ERR_NAME; + free(xname); + PM_UNLOCK(__pmLock_libpcp); + goto check; + } + } + } + free(xname); + } + num = PM_ERR_NAME; + PM_UNLOCK(__pmLock_libpcp); + goto check; + } + PM_UNLOCK(__pmLock_libpcp); + + if (np != NULL && np->first == NULL) { + /* + * this is a leaf node ... if it is the root of a dynamic + * subtree of the PMNS and we have an existing context + * of type PM_CONTEXT_LOCAL than we should chase the + * relevant PMDA to provide the details + */ + if (pmid_domain(np->pmid) == DYNAMIC_PMID && + pmid_item(np->pmid) == 0) { + if (ctxp != NULL && ctxp->c_type == PM_CONTEXT_LOCAL) { + int domain = ((__pmID_int *)&np->pmid)->cluster; + __pmDSO *dp; + if ((dp = __pmLookupDSO(domain)) == NULL) { + num = PM_ERR_NOAGENT; + goto check; + } + if (dp->dispatch.comm.pmda_interface >= PMDA_INTERFACE_5) + dp->dispatch.version.four.ext->e_context = ctx; + if (dp->dispatch.comm.pmda_interface >= PMDA_INTERFACE_4) { + char **x_offspring = NULL; + int *x_statuslist = NULL; + int x_num; + x_num = dp->dispatch.version.four.children(name, 0, + &x_offspring, &x_statuslist, + dp->dispatch.version.four.ext); + if (x_num < 0) + num = x_num; + else if (x_num > 0) { + stitch_list(&num, offspring, statuslist, + x_num, x_offspring, x_statuslist); + free(x_offspring); + free(x_statuslist); + } + goto check; + } + else { + /* Not PMDA_INTERFACE_4 or later */ + num = PM_ERR_NAME; + goto check; + } + } + } + num = 0; + goto check; + } + + need = 0; + num = 0; + + if (np != NULL) { + for (i = 0, tnp = np->first; tnp != NULL; tnp = tnp->next, i++) { + if ((tnp->pmid & MARK_BIT) == 0) { + num++; + need += sizeof(**offspring) + strlen(tnp->name) + 1; + } + } + } + + if ((result = (char **)malloc(need)) == NULL) { + num = -oserror(); + goto report; + } + + if (statuslist != NULL) { + if ((status = (int *)malloc(num*sizeof(int))) == NULL) { + num = -oserror(); + free(result); + goto report; + } + } + + p = (char *)&result[num]; + + if (np != NULL) { + for (i = 0, tnp = np->first; tnp != NULL; tnp = tnp->next) { + if ((tnp->pmid & MARK_BIT) == 0) { + result[i] = p; + /* + * a name at the root of a dynamic metrics subtree + * needs some special handling ... they will have a + * "special" PMID, but need the status set to indicate + * they are not a leaf node of the PMNS + */ + if (statuslist != NULL) { + if (pmid_domain(tnp->pmid) == DYNAMIC_PMID && + pmid_item(tnp->pmid) == 0) { + status[i] = PMNS_NONLEAF_STATUS; + } + else + /* node has children? */ + status[i] = (tnp->first == NULL ? PMNS_LEAF_STATUS : PMNS_NONLEAF_STATUS); + } + strcpy(result[i], tnp->name); + p += strlen(tnp->name) + 1; + i++; + } + } + } + + *offspring = result; + if (statuslist != NULL) + *statuslist = status; + } + else { + /* + * PMNS_REMOTE so there must be a current host context + */ + assert(ctxp != NULL && ctxp->c_type == PM_CONTEXT_HOST); + num = GetChildrenStatusRemote(ctxp, name, offspring, statuslist); + } + +check: + if (ctxp != NULL) + PM_UNLOCK(ctxp->c_lock); + /* + * see if there are derived metrics that qualify + */ + dm_num = __dmchildren(name, &dm_offspring, &dm_statuslist); +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_DERIVE) { + char errmsg[PM_MAXERRMSGLEN]; + if (num < 0) + fprintf(stderr, "pmGetChildren(name=\"%s\") no regular children (%s)", name, pmErrStr_r(num, errmsg, sizeof(errmsg))); + else + fprintf(stderr, "pmGetChildren(name=\"%s\") %d regular children", name, num); + if (dm_num < 0) + fprintf(stderr, ", no derived children (%s)\n", pmErrStr_r(dm_num, errmsg, sizeof(errmsg))); + else if (dm_num == 0) + fprintf(stderr, ", derived leaf\n"); + else + fprintf(stderr, ", %d derived children\n", dm_num); + } +#endif + if (dm_num > 0) { + stitch_list(&num, offspring, statuslist, dm_num, dm_offspring, dm_statuslist); + free(dm_offspring); + free(dm_statuslist); + } + else if (dm_num == 0 && num < 0) { + /* leaf node and derived metric */ + num = 0; + } + +report: +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_PMNS) { + fprintf(stderr, "pmGetChildren(name=\"%s\") -> ", name); + if (num == 0) + fprintf(stderr, "leaf\n"); + else if (num > 0) { + if (statuslist != NULL) + __pmDumpNameAndStatusList(stderr, num, *offspring, *statuslist); + else + __pmDumpNameList(stderr, num, *offspring); + } + else { + char errmsg[PM_MAXERRMSGLEN]; + fprintf(stderr, "%s\n", pmErrStr_r(num, errmsg, sizeof(errmsg))); + } + } +#endif + + return num; +} + +int +pmGetChildren(const char *name, char ***offspring) +{ + return pmGetChildrenStatus(name, offspring, NULL); +} + +static int +request_namebypmid(__pmContext *ctxp, pmID pmid) +{ + int n; + + n = __pmSendIDList(ctxp->c_pmcd->pc_fd, __pmPtrToHandle(ctxp), 1, &pmid, 0); + if (n < 0) + n = __pmMapErrno(n); + return n; +} + +static int +receive_namesbyid(__pmContext *ctxp, char ***namelist) +{ + int n; + __pmPDU *pb; + int pinpdu; + + pinpdu = n = __pmGetPDU(ctxp->c_pmcd->pc_fd, ANY_SIZE, + ctxp->c_pmcd->pc_tout_sec, &pb); + + if (n == PDU_PMNS_NAMES) { + int numnames; + + n = __pmDecodeNameList(pb, &numnames, namelist, NULL); + if (n >= 0) + n = numnames; + } + else if (n == PDU_ERROR) + __pmDecodeError(pb, &n); + else if (n != PM_ERR_TIMEOUT) + n = PM_ERR_IPC; + + if (pinpdu > 0) + __pmUnpinPDUBuf(pb); + + return n; +} + +static int +receive_a_name(__pmContext *ctxp, char **name) +{ + int n; + char **namelist; + + if ((n = receive_namesbyid(ctxp, &namelist)) >= 0) { + char *newname = strdup(namelist[0]); + free(namelist); + if (newname == NULL) { + n = -oserror(); + } else { + *name = newname; + n = 0; + } + } + + return n; +} + +int +pmNameID(pmID pmid, char **name) +{ + int pmns_location = GetLocation(); + + if (pmns_location < 0) + return pmns_location; + + PM_INIT_LOCKS(); + + if (pmns_location == PMNS_LOCAL) { + __pmnsNode *np; + PM_LOCK(__pmLock_libpcp); + for (np = curr_pmns->htab[pmid % curr_pmns->htabsize]; np != NULL; np = np->hash) { + if (np->pmid == pmid) { + int sts; + if (pmid_domain(np->pmid) != DYNAMIC_PMID || + pmid_item(np->pmid) != 0) + sts = backname(np, name); + else + sts = PM_ERR_PMID; + PM_UNLOCK(__pmLock_libpcp); + return sts; + } + } + PM_UNLOCK(__pmLock_libpcp); + /* not found so far, try derived metrics ... */ + return __dmgetname(pmid, name); + } + + else { + /* assume PMNS_REMOTE */ + int n; + __pmContext *ctxp; + + /* As we have PMNS_REMOTE there must be a current host context */ + if ((n = pmWhichContext()) < 0 || (ctxp = __pmHandleToPtr(n)) == NULL) + return PM_ERR_NOCONTEXT; + PM_LOCK(ctxp->c_pmcd->pc_lock); + if ((n = request_namebypmid(ctxp, pmid)) >= 0) { + n = receive_a_name(ctxp, name); + } + PM_UNLOCK(ctxp->c_pmcd->pc_lock); + PM_UNLOCK(ctxp->c_lock); + if (n >= 0) return n; + return __dmgetname(pmid, name); + } +} + +int +pmNameAll(pmID pmid, char ***namelist) +{ + int pmns_location = GetLocation(); + char **tmp = NULL; + int n = 0; + int len = 0; + char *sp; + + if (pmns_location < 0) + return pmns_location; + + PM_INIT_LOCKS(); + + if (pmns_location == PMNS_LOCAL) { + __pmnsNode *np; + int sts = 0; + int i; + + if (pmid_domain(pmid) == DYNAMIC_PMID && pmid_item(pmid) == 0) { + /* + * pmid is for the root of a dynamic subtree in the PMNS ... + * there is no matching leaf name + */ + return PM_ERR_PMID; + } + PM_LOCK(__pmLock_libpcp); + for (np = curr_pmns->htab[pmid % curr_pmns->htabsize]; np != NULL; np = np->hash) { + if (np->pmid == pmid) { + n++; + if ((tmp = (char **)realloc(tmp, n * sizeof(tmp[0]))) == NULL) { + sts = -oserror(); + break; + } + if ((sts = backname(np, &tmp[n-1])) < 0) { + /* error, ... free any partial allocations */ + for (i = n-2; i >= 0; i--) + free(tmp[i]); + free(tmp); + break; + } + len += strlen(tmp[n-1])+1; + } + } + PM_UNLOCK(__pmLock_libpcp); + if (sts < 0) + return sts; + + if (n == 0) + goto try_derive; + + len += n * sizeof(tmp[0]); + if ((tmp = (char **)realloc(tmp, len)) == NULL) + return -oserror(); + + sp = (char *)&tmp[n]; + for (i = 0; i < n; i++) { + strcpy(sp, tmp[i]); + free(tmp[i]); + tmp[i] = sp; + sp += strlen(sp)+1; + } + + *namelist = tmp; + return n; + } + + else { + /* assume PMNS_REMOTE */ + int n; + __pmContext *ctxp; + + /* As we have PMNS_REMOTE there must be a current host context */ + if ((n = pmWhichContext()) < 0 || (ctxp = __pmHandleToPtr(n)) == NULL) + return PM_ERR_NOCONTEXT; + PM_LOCK(ctxp->c_pmcd->pc_lock); + if ((n = request_namebypmid (ctxp, pmid)) >= 0) { + n = receive_namesbyid (ctxp, namelist); + } + PM_UNLOCK(ctxp->c_pmcd->pc_lock); + PM_UNLOCK(ctxp->c_lock); + if (n == 0) + goto try_derive; + return n; + } + +try_derive: + if ((tmp = (char **)malloc(sizeof(tmp[0]))) == NULL) + return -oserror(); + n = __dmgetname(pmid, tmp); + if (n < 0) { + free(tmp); + return n; + } + len = sizeof(tmp[0]) + strlen(tmp[0])+1; + if ((tmp = (char **)realloc(tmp, len)) == NULL) + return -oserror(); + sp = (char *)&tmp[1]; + strcpy(sp, tmp[0]); + free(tmp[0]); + tmp[0] = sp; + *namelist = tmp; + return 1; +} + + +/* + * generic depth-first recursive descent of the PMNS + */ +static int +TraversePMNS_local(const char *name, void(*func)(const char *), void(*func_r)(const char *, void *), void *closure) +{ + int sts = 0; + int nchildren; + char **enfants; + + if ((nchildren = pmGetChildren(name, &enfants)) < 0) + return nchildren; + + if (nchildren > 0) { + int j; + char *newname; + + for (j = 0; j < nchildren; j++) { + size_t size = strlen(name) + 1 + strlen(enfants[j]) + 1; + if ((newname = (char *)malloc(size)) == NULL) + __pmNoMem("pmTraversePMNS", size, PM_FATAL_ERR); + if (*name == '\0') + strcpy(newname, enfants[j]); + else { + strcpy(newname, name); + strcat(newname, "."); + strcat(newname, enfants[j]); + } + sts = TraversePMNS_local(newname, func, func_r, closure); + free(newname); + if (sts < 0) + break; + } + free(enfants); + } + else { + /* leaf node, name is full name of a metric */ + if (func_r == NULL) + (*func)(name); + else + (*func_r)(name, closure); + } + + return sts; +} + +static int +TraversePMNS(const char *name, void(*func)(const char *), void(*func_r)(const char *, void *), void *closure) +{ + int sts; + int pmns_location = GetLocation(); + + if (pmns_location < 0) + return pmns_location; + + if (name == NULL) + return PM_ERR_NAME; + + PM_INIT_LOCKS(); + + if (pmns_location == PMNS_LOCAL) { + PM_LOCK(__pmLock_libpcp); + sts = TraversePMNS_local(name, func, func_r, closure); + PM_UNLOCK(__pmLock_libpcp); + } + else { + __pmPDU *pb; + __pmContext *ctxp; + + /* As we have PMNS_REMOTE there must be a current host context */ + if ((sts = pmWhichContext()) < 0 || (ctxp = __pmHandleToPtr(sts)) == NULL) + return PM_ERR_NOCONTEXT; + PM_LOCK(ctxp->c_pmcd->pc_lock); + sts = __pmSendTraversePMNSReq(ctxp->c_pmcd->pc_fd, __pmPtrToHandle(ctxp), name); + if (sts < 0) { + sts = __pmMapErrno(sts); + PM_UNLOCK(ctxp->c_pmcd->pc_lock); + PM_UNLOCK(ctxp->c_lock); + } + else { + int numnames; + int i; + int xtra; + char **namelist; + int pinpdu; + + pinpdu = sts = __pmGetPDU(ctxp->c_pmcd->pc_fd, ANY_SIZE, + TIMEOUT_DEFAULT, &pb); + PM_UNLOCK(ctxp->c_pmcd->pc_lock); + PM_UNLOCK(ctxp->c_lock); + if (sts == PDU_PMNS_NAMES) { + sts = __pmDecodeNameList(pb, &numnames, + &namelist, NULL); + if (sts > 0) { + for (i=0; i<numnames; i++) { + /* + * Do not process anonymous metrics here, we'll + * pick them up with the derived metrics later on + */ + if (strncmp(namelist[i], "anon.", 5) != 0) { + if (func_r == NULL) + (*func)(namelist[i]); + else + (*func_r)(namelist[i], closure); + } + } + numnames = sts; + free(namelist); + } + else { + __pmUnpinPDUBuf(pb); + return sts; + } + } + else if (sts == PDU_ERROR) { + __pmDecodeError(pb, &sts); + if (sts != PM_ERR_NAME) { + __pmUnpinPDUBuf(pb); + return sts; + } + numnames = 0; + } + else { + if (pinpdu > 0) + __pmUnpinPDUBuf(pb); + return (sts == PM_ERR_TIMEOUT) ? sts : PM_ERR_IPC; + } + + if (pinpdu > 0) + __pmUnpinPDUBuf(pb); + + /* + * add any derived metrics that have "name" as + * their prefix + */ + xtra = __dmtraverse(name, &namelist); + if (xtra > 0) { + sts = 0; + for (i=0; i<xtra; i++) { + if (func_r == NULL) + (*func)(namelist[i]); + else + (*func_r)(namelist[i], closure); + } + numnames += xtra; + free(namelist); + } + + if (sts > 0) + return numnames; + } + } + return sts; +} + +int +pmTraversePMNS(const char *name, void(*func)(const char *)) +{ + return TraversePMNS(name, func, NULL, NULL); +} + +int +pmTraversePMNS_r(const char *name, void(*func)(const char *, void *), void *closure) +{ + return TraversePMNS(name, NULL, func, closure); +} + +int +pmTrimNameSpace(void) +{ + int i; + __pmContext *ctxp; + __pmHashCtl *hcp; + __pmHashNode *hp; + int pmns_location = GetLocation(); + + if (pmns_location < 0) + return pmns_location; + else if (pmns_location == PMNS_REMOTE) + return 0; + + /* for PMNS_LOCAL ... */ + PM_INIT_LOCKS(); + + if ((ctxp = __pmHandleToPtr(pmWhichContext())) == NULL) + return PM_ERR_NOCONTEXT; + + if (ctxp->c_type != PM_CONTEXT_ARCHIVE) { + /* unset all of the marks */ + PM_LOCK(__pmLock_libpcp); + mark_all(curr_pmns, 0); + PM_UNLOCK(__pmLock_libpcp); + PM_UNLOCK(ctxp->c_lock); + return 0; + } + + /* Don't do any trimming for archives. + * Exception: if an explicit load PMNS call was made. + */ + PM_LOCK(__pmLock_libpcp); + if (havePmLoadCall) { + /* + * (1) set all of the marks, and + * (2) clear the marks for those metrics defined in the archive + */ + mark_all(curr_pmns, 1); + hcp = &ctxp->c_archctl->ac_log->l_hashpmid; + + for (i = 0; i < hcp->hsize; i++) { + for (hp = hcp->hash[i]; hp != NULL; hp = hp->next) { + mark_one(curr_pmns, (pmID)hp->key, 0); + } + } + } + PM_UNLOCK(__pmLock_libpcp); + PM_UNLOCK(ctxp->c_lock); + + return 0; +} + +void +__pmDumpNameSpace(FILE *f, int verbosity) +{ + int pmns_location = GetLocation(); + + if (pmns_location < 0) + fprintf(f, "__pmDumpNameSpace: Unable to determine PMNS location\n"); + else if (pmns_location == PMNS_REMOTE) + fprintf(f, "__pmDumpNameSpace: Name Space is remote !\n"); + + PM_INIT_LOCKS(); + PM_LOCK(__pmLock_libpcp); + dumptree(f, 0, curr_pmns->root, verbosity); + PM_UNLOCK(__pmLock_libpcp); +} + +void +__pmDumpNameNode(FILE *f, __pmnsNode *node, int verbosity) +{ + dumptree(f, 0, node, verbosity); +} |