1    	/*
2    	 * Copyright (c) 1995-2001 Silicon Graphics, Inc.  All Rights Reserved.
3    	 * 
4    	 * This library is free software; you can redistribute it and/or modify it
5    	 * under the terms of the GNU Lesser General Public License as published
6    	 * by the Free Software Foundation; either version 2.1 of the License, or
7    	 * (at your option) any later version.
8    	 * 
9    	 * This library is distributed in the hope that it will be useful, but
10   	 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11   	 * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
12   	 * License for more details.
13   	 */
14   	
15   	#include <sys/stat.h>
16   	#include <stddef.h>
17   	#include <assert.h>
18   	#include <ctype.h>
19   	#include "pmapi.h"
20   	#include "impl.h"
21   	#include "pmda.h"
22   	#ifdef HAVE_STRINGS_H
23   	#include <strings.h>
24   	#endif
25   	
26   	/* token types */
27   	#define NAME	1
28   	#define PATH	2
29   	#define PMID	3
30   	#define LBRACE	4
31   	#define RBRACE	5
32   	#define BOGUS	10
33   	
34   	#define UNKNOWN_MARK_STATE -1           /* tree not all marked the same way */
35   	/*
36   	 * Note: bit masks below are designed to clear and set the "flag" field
37   	 *       of a __pmID_int (i.e. a PMID)
38   	 */
39   	#define PMID_MASK	0x7fffffff	/* 31 bits of PMID */
40   	#define MARK_BIT	0x80000000	/* mark bit */
41   	
42   	
43   	static int	lineno;
44   	static char	linebuf[256];
45   	static char	*linep;
46   	static char	fname[256];
47   	static char	tokbuf[256];
48   	static pmID	tokpmid;
49   	static int	numpmid;
50   	
51   	static __pmnsNode *seen; /* list of pass-1 subtree nodes */
52   	
53   	/* Last modification time for loading main_pmns file. */
54   	#if defined(HAVE_STAT_TIMESTRUC)
55   	static timestruc_t	last_mtim;
56   	#elif defined(HAVE_STAT_TIMESPEC)
57   	static struct timespec	last_mtim;
58   	#elif defined(HAVE_STAT_TIMESPEC_T)
59   	static timespec_t	last_mtim;
60   	#elif defined(HAVE_STAT_TIME_T)
61   	static time_t	last_mtim;
62   	#else
63   	!bozo!
64   	#endif
65   	
66   	/* The curr_pmns points to PMNS to use for API ops.
67   	 * Curr_pmns will point to either the main_pmns or
68   	 * a pmns from a version 2 archive context.
69   	 */
70   	static __pmnsTree *curr_pmns; 
71   	
72   	/* The main_pmns points to the loaded PMNS (not from archive). */
73   	static __pmnsTree *main_pmns; 
74   	
75   	
76   	/* == 1 if PMNS loaded and __pmExportPMNS has been called */
77   	static int export;
78   	
79   	static int havePmLoadCall;
80   	static int useExtPMNS;	/* set by __pmUsePMNS() */
81   	
82   	static int load(const char *filename, int binok, int dupok);
83   	static __pmnsNode *locate(const char *name, __pmnsNode *root);
84   	
85   	
86   	/*
87   	 * Set current pmns to an externally supplied PMNS.
88   	 * Useful for testing the API routines during debugging.
89   	 */
90   	void
91   	__pmUsePMNS(__pmnsTree *t)
92   	{
93   	    useExtPMNS = 1;
94   	    curr_pmns = t;
95   	}
96   	
97   	static const char *
98   	pmPMNSLocationStr(int location)
99   	{
100  	    if (location < 0)
101  		return pmErrStr(location);
102  	
103  	    switch(location) {
104  	    case PMNS_LOCAL:	return "Local";
105  	    case PMNS_REMOTE:	return "Remote";
106  	    case PMNS_ARCHIVE:	return "Archive";
107  	    }
108  	    return "Internal Error";
109  	}
110  	
111  	
112  	static int
113  	LoadDefault(char *reason_msg)
114  	{
115  	    if (main_pmns == NULL) {
116  	#ifdef PCP_DEBUG
117  		if (pmDebug & DBG_TRACE_PMNS) {
118  		    fprintf(stderr,
119  			"pmGetPMNSLocation: Loading local PMNS for %s PMAPI context\n",
120  			reason_msg);
121  		}
122  	#endif
123  		if (load(PM_NS_DEFAULT, 1, 0) < 0)
124  		    return PM_ERR_NOPMNS;
125  		else
126  		    return PMNS_LOCAL;
127  	    }
128  	    return PMNS_LOCAL;
129  	}
130  	
131  	/*
132  	 * Return the pmns_location.  Possibly load the default PMNS.
133  	 */
134  	int 
135  	pmGetPMNSLocation(void)
136  	{
137  	    int pmns_location = PM_ERR_NOPMNS;
138  	    int n;
139  	    int sts;
140  	    int version;
141  	    __pmContext  *ctxp;
142  	
143  	    if (useExtPMNS)
144  		return PMNS_LOCAL;
145  	
146  	    /* 
147  	     * Determine if we are to use PDUs or local PMNS file.
148  	     * Load PMNS if necessary.
149  	     */
150  	    if (!havePmLoadCall) {
151  		if ((n = pmWhichContext()) >= 0) {
152  		    ctxp = __pmHandleToPtr(n);
153  		    switch(ctxp->c_type) {
154  			case PM_CONTEXT_HOST:
155  			    if (ctxp->c_pmcd->pc_fd == -1)
156  				return PM_ERR_IPC;
157  			    if ((sts = version = __pmVersionIPC(ctxp->c_pmcd->pc_fd)) < 0) {
158  				__pmNotifyErr(LOG_ERR, 
159  					"pmGetPMNSLocation: version lookup failed "
160  					"(context=%d, fd=%d): %s", 
161  					n, ctxp->c_pmcd->pc_fd, pmErrStr(sts));
162  				pmns_location = PM_ERR_NOPMNS;
163  			    }
164  			    else if (version == PDU_VERSION1) {
165  				pmns_location = LoadDefault("PMCD (version 1)");
166  			    }
167  			    else if (version == PDU_VERSION2) {
168  				pmns_location = PMNS_REMOTE;
169  			    }
170  			    else {
171  				__pmNotifyErr(LOG_ERR, 
172  					"pmGetPMNSLocation: bad host PDU version "
173  					"(context=%d, fd=%d, ver=%d)",
174  					n, ctxp->c_pmcd->pc_fd, version);
175  				pmns_location = PM_ERR_NOPMNS;
176  			    }
177  			    break;
178  	
179  			case PM_CONTEXT_LOCAL:
180  			    pmns_location = LoadDefault("local");
181  			    break;
182  	
183  			case PM_CONTEXT_ARCHIVE:
184  			    version = ctxp->c_archctl->ac_log->l_label.ill_magic & 0xff;
185  			    if (version == PM_LOG_VERS01) {
186  				pmns_location = LoadDefault("archive (version 1)");
187  			    }
188  			    else if (version == PM_LOG_VERS02) {
189  				pmns_location = PMNS_ARCHIVE;
190  				curr_pmns = ctxp->c_archctl->ac_log->l_pmns; 
191  			    }
192  			    else {
193  				__pmNotifyErr(LOG_ERR, "pmGetPMNSLocation: bad archive "
194  					"version (context=%d, fd=%d, ver=%d)",
195  					n, ctxp->c_pmcd->pc_fd, version); 
196  				pmns_location = PM_ERR_NOPMNS;
197  			    }
198  			    break;
199  	
200  			default: 
201  			    __pmNotifyErr(LOG_ERR, "pmGetPMNSLocation: bogus context "
202  					"type: %d", ctxp->c_type); 
203  			    pmns_location = PM_ERR_NOPMNS;
204  			    break;
205  		    }
206  		}
207  		else {
208  		    pmns_location = PM_ERR_NOPMNS; /* no context for client */
209  		}
210  	    }
211  	    else { /* have explicit external load call */
212  		if (main_pmns == NULL)
213  		    pmns_location = PM_ERR_NOPMNS;
214  		else
215  		    pmns_location = PMNS_LOCAL;
216  	    }
217  	
218  	#ifdef PCP_DEBUG
219  	    if (pmDebug & DBG_TRACE_PMNS) {
220  		static int last_pmns_location = -1;
221  	
222  		if (pmns_location != last_pmns_location) {
223  		    fprintf(stderr, "pmGetPMNSLocation() -> %s\n", 
224  				    pmPMNSLocationStr(pmns_location));
225  		    last_pmns_location = pmns_location;
226  		}
227  	    }
228  	#endif
229  	
230  	    /* fix up curr_pmns for API ops */
231  	    if (pmns_location == PMNS_LOCAL)
232  		curr_pmns = main_pmns;
233  	    else if (pmns_location != PMNS_ARCHIVE)
234  		curr_pmns = NULL;
235  	    return pmns_location;
236  	}
237  	
238  	/*
239  	 * Our own PMNS locator.  Don't distinguish between ARCHIVE or LOCAL.
240  	 */
241  	static
242  	int GetLocation(void)
243  	{
244  	    int	loc = pmGetPMNSLocation();
245  	
246  	    if (loc == PMNS_ARCHIVE)
247  		return PMNS_LOCAL;
248  	    return loc;
249  	}
250  	
251  	/*
252  	 * For debugging, and visible via __pmDumpNameSpace()
253  	 *
254  	 * verbosity is 0 (name), 1 (names and pmids) or 2 (names, pmids and
255  	 * linked-list structures)
256  	 */
257  	static void
258  	dumptree(FILE *f, int level, __pmnsNode *rp, int verbosity)
259  	{
260  	    int		i;
261  	    __pmID_int	*pp;
262  	
263  	    if (rp != NULL) {
264  		if (verbosity > 1)
265  		    fprintf(f, "" PRINTF_P_PFX "%p", rp);
266  		for (i = 0; i < level; i++) {
267  		    fprintf(f, "    ");
268  		}
269  		fprintf(f, " %-16.16s", rp->name);
270  		pp = (__pmID_int *)&rp->pmid;
271  		if (verbosity > 0 && rp->first == NULL)
272  		    fprintf(f, " %d %d.%d.%d 0x%08x", rp->pmid,
273  			    pp->domain, pp->cluster, pp->item,
274  			    rp->pmid);
275  		if (verbosity > 1) {
276  		    fprintf(f, "\t[first: ");
277  		    if (rp->first) fprintf(f, "" PRINTF_P_PFX "%p", rp->first);
278  		    else fprintf(f, "<null>");
279  		    fprintf(f, " next: ");
280  		    if (rp->next) fprintf(f, "" PRINTF_P_PFX "%p", rp->next);
281  		    else fprintf(f, "<null>");
282  		    fprintf(f, " parent: ");
283  		    if (rp->parent) fprintf(f, "" PRINTF_P_PFX "%p", rp->parent);
284  		    else fprintf(f, "<null>");
285  		    fprintf(f, " hash: ");
286  		    if (rp->hash) fprintf(f, "" PRINTF_P_PFX "%p", rp->hash);
287  		    else fprintf(f, "<null>");
288  		}
289  		fputc('\n', f);
290  		dumptree(f, level+1, rp->first, verbosity);
291  		dumptree(f, level, rp->next, verbosity);
292  	    }
293  	}
294  	
295  	static void
296  	err(char *s)
297  	{
298  	    if (lineno > 0)
299  		pmprintf("[%s:%d] ", fname, lineno);
300  	    pmprintf("Error Parsing ASCII PMNS: %s\n", s);
301  	    if (lineno > 0) {
302  		char	*p;
303  		pmprintf("    %s", linebuf);
304  		for (p = linebuf; *p; p++)
305  		    ;
306  		if (p[-1] != '\n')
307  		    pmprintf("\n");
308  		if (linep) {
309  		    p = linebuf;
310  		    for (p = linebuf; p < linep; p++) {
311  			if (!isspace((int)*p))
312  			    *p = ' ';
313  		    }
314  		    *p++ = '^';
315  		    *p++ = '\n';
316  		    *p = '\0';
317  		    pmprintf("    %s", linebuf);
318  		}
319  	    }
320  	    pmflush();
321  	}
322  	
323  	/*
324  	 * lexical analyser for loading the ASCII pmns
325  	 */
326  	static int
327  	lex(int reset)
328  	{
329  	    static int	first = 1;
330  	    static FILE	*fin;
331  	    static char	*lp;
332  	    char	*tp;
333  	    int		colon;
334  	    int		type;
335  	    int		d, c, i;
336  	    __pmID_int	pmid_int;
337  	
338  	    if (reset) {
339  		/* reset! */
340  		linep = NULL;
341  		first = 1;
342  		return 0;
343  	    }
344  	
345  	    if (first) {
346  		char	*alt;
347  		char	cmd[80+MAXPATHLEN];
348  	
349  		first = 0;
350  		if ((alt = getenv("PCP_ALT_CPP")) != NULL) {
351  		    /* $PCP_ALT_CPP used in the build before pmcpp installed */
352  		    snprintf(cmd, sizeof(cmd), "%s %s", alt, fname);
353  		}
354  		else {
355  		    /* the normal case ... */
356  		    int		sep = __pmPathSeparator();
357  		    char	*bin_dir = pmGetConfig("PCP_BINADM_DIR");
358  		    snprintf(cmd, sizeof(cmd), "%s%c%s %s", bin_dir, sep, "pmcpp" EXEC_SUFFIX, fname);
359  		}
360  	
361  		fin = popen(cmd, "r");
362  		if (fin == NULL)
363  		    return -oserror();
364  	
365  		lp = linebuf;
366  		*lp = '\0';
367  	    }
368  	
369  	    while (*lp && isspace((int)*lp)) lp++;
370  	
371  	    while (*lp == '\0') {
372  		for ( ; ; ) {
373  		    char	*p;
374  		    char	*q;
375  		    int		inspace = 0;
376  	
377  		    if (fgets(linebuf, sizeof(linebuf), fin) == NULL) {
378  			if (pclose(fin) != 0) {
379  			    lineno = -1; /* We're outside of line counting range now */
380  			    err("pmcpp returned non-zero exit status");
381  			    return PM_ERR_PMNS;
382  			} else {
383  			    return 0;
384  			}
385  		    }
386  		    for (q = p = linebuf; *p; p++) {
387  			if (isspace((int)*p)) {
388  			    if (!inspace) {
389  				if (q > linebuf && q[-1] != ':')
390  				    *q++ = *p;
391  				inspace = 1;
392  			    }
393  			}
394  			else if (*p == ':') {
395  			    if (inspace) {
396  				q--;
397  				inspace = 0;
398  			    }
399  			    *q++ = *p;
400  			}
401  			else {
402  			    *q++ = *p;
403  			    inspace = 0;
404  			}
405  		    }
406  		    if (p[-1] != '\n') {
407  			err("Absurdly long line, cannot recover");
408  			pclose(fin);	/* wait for pmcpp to finish */
409  			exit(1);
410  		    }
411  		    *q = '\0';
412  		    if (linebuf[0] == '#') {
413  			/* pmcpp line number control line */
414  			if (sscanf(linebuf, "# %d \"%s", &lineno, fname) != 2) {
415  			    err ("Illegal line number control number");
416  			    return PM_ERR_PMNS;
417  			}
418  			--lineno;
419  			for (p = fname; *p; p++)
420  			    ;
421  			*--p = '\0';
422  			continue;
423  		    }
424  		    else
425  			lineno++;
426  		    lp = linebuf;
427  		    while (*lp && isspace((int)*lp)) lp++;
428  		    break;
429  		}
430  	    }
431  	
432  	    linep = lp;
433  	    tp = tokbuf;
434  	    while (!isspace((int)*lp))
435  		*tp++ = *lp++;
436  	    *tp = '\0';
437  	
438  	    if (tokbuf[0] == '{' && tokbuf[1] == '\0') return LBRACE;
439  	    else if (tokbuf[0] == '}' && tokbuf[1] == '\0') return RBRACE;
440  	    else if (isalpha((int)tokbuf[0])) {
441  		type = NAME;
442  		for (tp = &tokbuf[1]; *tp; tp++) {
443  		    if (*tp == '.')
444  			type = PATH;
445  		    else if (!isalpha((int)*tp) && !isdigit((int)*tp) && *tp != '_')
446  			break;
447  		}
448  		if (*tp == '\0') return type;
449  	    }
450  	    colon = 0;
451  	    for (tp = tokbuf; *tp; tp++) {
452  		if (*tp == ':') {
453  		    if (++colon > 3) return BOGUS;
454  		}
455  		else if (!isdigit((int)*tp) && *tp != '*') return BOGUS;
456  	    }
457  	
458  	    /*
459  	     * Internal PMID format
460  	     * domain 9 bits
461  	     * cluster 12 bits
462  	     * item 10 bits
463  	     */
464  	    if (sscanf(tokbuf, "%d:%d:%d", &d, &c, &i) == 3) {
465  		if (d > 510) {
466  		    err("Illegal domain field in PMID");
467  		    return BOGUS;
468  		}
469  		else if (c > 4095) {
470  		    err("Illegal cluster field in PMID");
471  		    return BOGUS;
472  		}
473  		else if (i > 1023) {
474  		    err("Illegal item field in PMID");
475  		    return BOGUS;
476  		}
477  		pmid_int.flag = 0;
478  		pmid_int.domain = d;
479  		pmid_int.cluster = c;
480  		pmid_int.item = i;
481  	    }
482  	    else {
483  		for (tp = tokbuf; *tp; tp++) {
484  		    if (*tp == ':') {
485  			if (strcmp("*:*", ++tp) != 0) {
486  			    err("Illegal PMID");
487  			    return BOGUS;
488  			}
489  			break;
490  		    }
491  		}
492  		if (sscanf(tokbuf, "%d:", &d) != 1) {
493  		    err("Illegal PMID");
494  		    return BOGUS;
495  		}
496  		if (d > 510) {
497  		    err("Illegal domain field in dynamic PMID");
498  		    return BOGUS;
499  		}
500  		else {
501  		    /*
502  		     * this node is the base of a dynamic subtree in the PMNS
503  		     * ... identified by setting the domain field to the reserved
504  		     * value DYNAMIC_PMID and storing the real domain of the PMDA
505  		     * that can enumerate the subtree in the cluster field, while
506  		     * the item field is not used (and set to zero)
507  		     */
508  		    pmid_int.flag = 0;
509  		    pmid_int.domain = DYNAMIC_PMID;
510  		    pmid_int.cluster = d;
511  		    pmid_int.item = 0;
512  		}
513  	    }
514  	    tokpmid = *(pmID *)&pmid_int;
515  	
516  	    return PMID;
517  	}
518  	
519  	/*
520  	 * Remove the named node from the seen list and return it.
521  	 * The seen-list is a list of subtrees from pass 1.
522  	 */
523  	
524  	static __pmnsNode *
525  	findseen(char *name)
526  	{
527  	    __pmnsNode	*np;
528  	    __pmnsNode	*lnp; /* last np */
529  	
530  	    for (np = seen, lnp = NULL; np != NULL; lnp = np, np = np->next) {
531  		if (strcmp(np->name, name) == 0) {
532  		    if (np == seen)
533  			seen = np->next;
534  		    else
535  			lnp->next = np->next;
536  		    np->next = NULL;
537  		    return np;
538  		}
539  	    }
540  	    return NULL;
541  	}
542  	
543  	/*
544  	 * Attach the subtrees from pass-1 to form a whole 
545  	 * connected tree.
546  	 */
547  	static int
548  	attach(char *base, __pmnsNode *rp)
549  	{
550  	    int		i;
551  	    __pmnsNode	*np;
552  	    __pmnsNode	*xp;
553  	    char	*path;
554  	
555  	    if (rp != NULL) {
556  		for (np = rp->first; np != NULL; np = np->next) {
557  		    if (np->pmid == PM_ID_NULL) {
558  			/* non-terminal node ... */
559  			if (*base == '\0') {
560  			    if ((path = (char *)malloc(strlen(np->name)+1)) == NULL)
561  				return -oserror();
562  			    strcpy(path, np->name);
563  			}
564  			else {
565  			    if ((path = (char *)malloc(strlen(base)+strlen(np->name)+2)) == NULL)
566  				return -oserror();
567  			    strcpy(path, base);
568  			    strcat(path, ".");
569  			    strcat(path, np->name);
570  			}
571  			if ((xp = findseen(path)) == NULL) {
572  			    snprintf(linebuf, sizeof(linebuf), "Cannot find definition for non-terminal node \"%s\" in name space",
573  			        path);
574  			    err(linebuf);
575  			    return PM_ERR_PMNS;
576  			}
577  			np->first = xp->first;
578  			free(xp);
579  			numpmid--;
580  			i = attach(path, np);
581  			free(path);
582  			if (i != 0)
583  			    return i;
584  		    }
585  		}
586  	    }
587  	    return 0;
588  	}
589  	
590  	/*
591  	 * Create a fullpath name by walking from the current
592  	 * tree node up to the root.
593  	 */
594  	static int
595  	backname(__pmnsNode *np, char **name)
596  	{
597  	    __pmnsNode	*xp;
598  	    char	*p;
599  	    int		nch;
600  	
601  	    nch = 0;
602  	    xp = np;
603  	    while (xp->parent != NULL) {
604  		nch += (int)strlen(xp->name)+1;
605  		xp = xp->parent;
606  	    }
607  	
608  	    if ((p = (char *)malloc(nch)) == NULL)
609  		return -oserror();
610  	
611  	    p[--nch] = '\0';
612  	    xp = np;
613  	    while (xp->parent != NULL) {
614  		int	xl;
615  	
616  		xl = (int)strlen(xp->name);
617  		nch -= xl;
618  		strncpy(&p[nch], xp->name, xl);
619  		xp = xp->parent;
620  		if (xp->parent == NULL)
621  		    break;
622  		else
623  		    p[--nch] = '.';
624  	    }
625  	    *name = p;
626  	
627  	    return 0;
628  	}
629  	
630  	/*
631  	 * Fixup the parent pointers of the tree.
632  	 * Fill in the hash table with nodes from the tree.
633  	 * Hashing is done on pmid.
634  	 */
635  	static int
636  	backlink(__pmnsTree *tree, __pmnsNode *root, int dupok)
637  	{
638  	    __pmnsNode	*np;
639  	    int		status;
640  	
641  	    for (np = root->first; np != NULL; np = np->next) {
642  		np->parent = root;
643  		if (np->pmid != PM_ID_NULL) {
644  		    int		i;
645  		    __pmnsNode	*xp;
646  		    i = np->pmid % tree->htabsize;
647  		    for (xp = tree->htab[i]; xp != NULL; xp = xp->hash) {
648  			if (xp->pmid == np->pmid && !dupok &&
649  			    pmid_domain(np->pmid) != DYNAMIC_PMID) {
650  			    char *nn, *xn;
651  			    backname(np, &nn);
652  			    backname(xp, &xn);
653  			    snprintf(linebuf, sizeof(linebuf), "Duplicate metric id (%s) in name space for metrics \"%s\" and \"%s\"\n",
654  			        pmIDStr(np->pmid), nn, xn);
655  			    err(linebuf);
656  			    free(nn);
657  			    free(xn);
658  			    return PM_ERR_PMNS;
659  			}
660  		    }
661  		    np->hash = tree->htab[i];
662  		    tree->htab[i] = np;
663  		}
664  		if ((status = backlink(tree, np, dupok)))
665  		    return status;
666  	    }
667  	    return 0;
668  	}
669  	
670  	/*
671  	 * Build up the whole tree by attaching the subtrees
672  	 * from the seen list.
673  	 * Create the hash table keyed on pmid.
674  	 *
675  	 */
676  	static int
677  	pass2(int dupok)
678  	{
679  	    __pmnsNode	*np;
680  	    int		status;
681  	
682  	    lineno = -1;
683  	
684  	    main_pmns = (__pmnsTree*)malloc(sizeof(*main_pmns));
685  	    if (main_pmns == NULL) {
686  		return -oserror();
687  	    }
688  	
689  	    /* Get the root subtree out of the seen list */
690  	    if ((main_pmns->root = findseen("root")) == NULL) {
691  		err("No name space entry for \"root\"");
692  		return PM_ERR_PMNS;
693  	    }
694  	
695  	    if (findseen("root") != NULL) {
696  		err("Multiple name space entries for \"root\"");
697  		return PM_ERR_PMNS;
698  	    }
699  	
700  	    /* Build up main tree from subtrees in seen-list */
701  	    if ((status = attach("", main_pmns->root)))
702  		return status;
703  	
704  	    /* Make sure all subtrees have been used in the main tree */
705  	    for (np = seen; np != NULL; np = np->next) {
706  		snprintf(linebuf, sizeof(linebuf), "Disconnected subtree (\"%s\") in name space", np->name);
707  		err(linebuf);
708  		status = PM_ERR_PMNS;
709  	    }
710  	    if (status)
711  		return status;
712  	
713  	    main_pmns->symbol = NULL;
714  	    main_pmns->contiguous = 0;
715  	    main_pmns->mark_state = UNKNOWN_MARK_STATE;
716  	
717  	    return __pmFixPMNSHashTab(main_pmns, numpmid, dupok);
718  	
719  	}
720  	
721  	
722  	/*
723  	 * clear/set the "mark" bit used by pmTrimNameSpace, for all pmids
724  	 */
725  	static void
726  	mark_all(__pmnsTree *pmns, int bit)
727  	{
728  	    int		i;
729  	    __pmnsNode	*np;
730  	    __pmnsNode	*pp;
731  	
732  	    if (pmns->mark_state == bit)
733  		return;
734  	
735  	    pmns->mark_state = bit;
736  	    for (i = 0; i < pmns->htabsize; i++) {
737  		for (np = pmns->htab[i]; np != NULL; np = np->hash) {
738  		    for (pp = np ; pp != NULL; pp = pp->parent) {
739  			if (bit)
740  			    pp->pmid |= MARK_BIT;
741  			else
742  			    pp->pmid &= ~MARK_BIT;
743  		    }
744  		}
745  	    }
746  	}
747  	
748  	/*
749  	 * clear/set the "mark" bit used by pmTrimNameSpace, for one pmid, and
750  	 * for all parent nodes on the path to the root of the PMNS
751  	 */
752  	static void
753  	mark_one(__pmnsTree *pmns, pmID pmid, int bit)
754  	{
755  	    __pmnsNode	*np;
756  	
757  	    if (pmns->mark_state == bit)
758  		return;
759  	
760  	    pmns->mark_state = UNKNOWN_MARK_STATE;
761  	    for (np = pmns->htab[pmid % pmns->htabsize]; np != NULL; np = np->hash) {
762  		if ((np->pmid & PMID_MASK) == (pmid & PMID_MASK)) {
763  		    for ( ; np != NULL; np = np->parent) {
764  			if (bit)
765  			    np->pmid |= MARK_BIT;
766  			else
767  			    np->pmid &= ~MARK_BIT;
768  		    }
769  		    return;
770  		}
771  	    }
772  	}
773  	
774  	
775  	/*
776  	 * Create a new empty PMNS for Adding nodes to.
777  	 * Use with __pmAddPMNSNode() and __pmFixPMNSHashTab()
778  	 */
779  	int
780  	__pmNewPMNS(__pmnsTree **pmns)
781  	{
782  	    __pmnsTree *t = NULL;
783  	    __pmnsNode *np = NULL;
784  	
785  	    t = (__pmnsTree*)malloc(sizeof(*main_pmns));
786  	    if (t == NULL) {
787  		return -oserror();
788  	    }
789  	
790  	    /* Insert the "root" node first */
791  	    if ((np = (__pmnsNode *)malloc(sizeof(*np))) == NULL)
792  		return -oserror();
793  	    np->pmid = PM_ID_NULL;
794  	    np->parent = np->first = np->hash = np->next = NULL;
795  	    np->name = strdup("root");
796  	    if (np->name == NULL) {
797  		free(np);
798  		return -oserror();
799  	    }
800  	
801  	    t->root = np;
802  	    t->htab = NULL;
803  	    t->htabsize = 0;
804  	    t->symbol = NULL;
805  	    t->contiguous = 0;
806  	    t->mark_state = UNKNOWN_MARK_STATE;
807  	
808  	    *pmns = t;
809  	    return 0;
810  	}
811  	
812  	/*
813  	 * Go through the tree and build a hash table.
814  	 * Fix up parent links while we're there.
815  	 * Unmark all nodes.
816  	 */
817  	int
818  	__pmFixPMNSHashTab(__pmnsTree *tree, int numpmid, int dupok)
819  	{
820  	    int sts;
821  	    int htabsize = numpmid/5;
822  	
823  	    /*
824  	     * make the average hash list no longer than 5, and the number
825  	     * of hash table entries not a multiple of 2, 3 or 5
826  	     */
827  	    if (htabsize % 2 == 0) htabsize++;
828  	    if (htabsize % 3 == 0) htabsize += 2;
829  	    if (htabsize % 5 == 0) htabsize += 2;
830  	    tree->htabsize = htabsize;
831  	    tree->htab = (__pmnsNode **)calloc(htabsize, sizeof(__pmnsNode *));
832  	    if (tree->htab == NULL)
833  		return -oserror();
834  	
835  	    if ((sts = backlink(tree, tree->root, dupok)) < 0)
836  		return sts;
837  	    mark_all(tree, 0);
838  	    return 0;
839  	}
840  	
841  	
842  	/*
843  	 * Add a new node for fullpath, name, with pmid.
844  	 * Does NOT update the hash table;
845  	 * need to call __pmFixPMNSHashTab() for that.
846  	 * Recursive routine.
847  	 */
848  	
849  	static int
850  	AddPMNSNode(__pmnsNode *root, int pmid, const char *name)
851  	{
852  	    __pmnsNode *np = NULL;
853  	    const char *tail;
854  	    int nch;
855  	
856  	    /* Traverse until '.' or '\0' */
857  	    for (tail = name; *tail && *tail != '.'; tail++)
858  		;
859  	
860  	    nch = (int)(tail - name);
861  	
862  	    /* Compare name with all the child nodes */
863  	    for (np = root->first; np != NULL; np = np->next) {
864  		if (strncmp(name, np->name, (int)nch) == 0 && np->name[(int)nch] == '\0')
865  		    break;
866  	    }
867  	
868  	    if (np == NULL) { /* no match with child */
869  		__pmnsNode *parent_np = root;
870  		const char *name_p = name;
871  		int is_first = 1;
872  	 
873  		/* create nodes until reach leaf */
874  	
875  		for ( ; ; ) { 
876  		    if ((np = (__pmnsNode *)malloc(sizeof(*np))) == NULL)
877  			return -oserror();
878  	
879  		    /* fixup name */
880  		    if ((np->name = (char *)malloc(nch+1)) == NULL)
881  			return -oserror();
882  		    strncpy(np->name, name_p, nch);
883  		    np->name[nch] = '\0';
884  	
885  		    /* fixup some links */
886  		    np->first = np->hash = np->next = NULL;
887  		    np->parent = parent_np;
888  		    if (is_first) {
889  			is_first = 0;
890  			if (root->first != NULL) {
891  			    /* chuck new node at front of list */
892  			    np->next = root->first;
893  			}
894  		    }
895  		    parent_np->first = np;
896  	
897  		    /* at this stage, assume np is a non-leaf */
898  		    np->pmid = PM_ID_NULL;
899  	
900  		    parent_np = np;
901  		    if (*tail == '\0')
902  			break;
903  		    name_p += nch+1; /* skip over node + dot */ 
904  		    for (tail = name_p; *tail && *tail != '.'; tail++)
905  			;
906  		    nch = (int)(tail - name_p);
907  		}
908  	
909  		np->pmid = pmid; /* set pmid of leaf node */
910  		return 0;
911  	    }
912  	    else if (*tail == '\0') { /* matched with whole path */
913  		if (np->pmid != pmid)
914  		    return PM_ERR_PMID;
915  		else 
916  		    return 0;
917  	    }
918  	    else {
919  		return AddPMNSNode(np, pmid, tail+1); /* try matching with rest of pathname */
920  	    }
921  	
922  	}
923  	
924  	
925  	/*
926  	 * Add a new node for fullpath, name, with pmid.
927  	 * NOTE: Need to call __pmFixPMNSHashTab() to update hash table
928  	 *       when have finished adding nodes.
929  	 */
930  	int
931  	__pmAddPMNSNode(__pmnsTree *tree, int pmid, const char *name)
932  	{
933  	    if (tree->contiguous) {
934  		pmprintf("Cannot add node to contiguously allocated tree!\n"); 
935  		pmflush();
936  		exit(1);
937  	    }
938  	
939  	    return AddPMNSNode(tree->root, pmid, name);
940  	}
941  	
942  	
943  	/*
944  	 * 32-bit and 64-bit dependencies ... there are TWO external format,
945  	 * both created by pmnscomp ... choose the correct one based upon
946  	 * how big pointer is ...
947  	 *
948  	 * Magic cookies in the binary format file
949  	 *	PmNs	- old 32-bit (Version 0)
950  	 *	PmN1	- new 32-bit and 64-bit (Version 1)
951  	 *	PmN2	- new 32-bit and 64-bit (Version 1 + checksum)
952  	 *
953  	 * File format:
954  	 *
955  	 *   Version 0
956  	 *     htab
957  	 *     tree-nodes
958  	 *     symbols
959  	 *
960  	 *
961  	 *
962  	 *   Version 1/2
963  	 *     symbols
964  	 *     list of binary-format PMNS (see below)
965  	 *
966  	 *   Binary-format PMNS
967  	 *     htab size, htab entry size
968  	 *     tree-node-tab size, tree-node-tab entry size
969  	 *     htab
970  	 *     tree-nodes
971  	 *     
972  	 */
973  	static int
974  	loadbinary(void)
975  	{
976  	    FILE	*fbin;
977  	    char	magic[4];
978  	    int		nodecnt;
979  	    int		symbsize;
980  	    int		htabsize;
981  	    int		i;
982  	    int		try;
983  	    int		version;
984  	    __int32_t	sum;
985  	    __int32_t	chksum;
986  	    long	endsum;
987  	    __psint_t	ord;
988  	    __pmnsNode	*root;
989  	    __pmnsNode	**htab;
990  	    char	*symbol;
991  	
992  	    for (try = 0; try < 2; try++) {
993  		if (try == 0) {
994  		    strcpy(linebuf, fname);
995  		    strcat(linebuf, ".bin");
996  		}
997  		else
998  		    strcpy(linebuf, fname);
999  	
1000 	#ifdef PCP_DEBUG
1001 		if (pmDebug & DBG_TRACE_PMNS)
1002 		    fprintf(stderr, "loadbinary(file=%s)\n", linebuf);
1003 	#endif
1004 		if ((fbin = fopen(linebuf, "r")) == NULL)
1005 		    continue;
1006 	
1007 		if (fread(magic, sizeof(magic), 1, fbin) != 1) {
1008 		    fclose(fbin);
1009 		    continue;
1010 		}
1011 		version = -1;
1012 		if (strncmp(magic, "PmNs", 4) == 0) {
1013 	#if !defined(HAVE_32BIT_PTR)
1014 		    __pmNotifyErr(LOG_WARNING, "pmLoadNameSpace: old 32-bit format binary file \"%s\"", linebuf);
1015 		    fclose(fbin);
1016 		    continue;
1017 	#else
1018 		    version = 0;
1019 	#endif
1020 		}
1021 		else if (strncmp(magic, "PmN1", 4) == 0)
1022 		    version= 1;
1023 		else if (strncmp(magic, "PmN2", 4) == 0) {
1024 		    version= 2;
1025 		    if (fread(&sum, sizeof(sum), 1, fbin) != 1) {
1026 			fclose(fbin);
1027 			continue;
1028 		    }
1029 		    sum = ntohl(sum);
1030 		    endsum = ftell(fbin);
1031 		    chksum = __pmCheckSum(fbin);
1032 	#ifdef PCP_DEBUG
1033 		    if (pmDebug & DBG_TRACE_PMNS)
1034 			fprintf(stderr, "Version 2 Binary PMNS Checksums: got=%x expected=%x\n", chksum, sum);
1035 	#endif
1036 		    if (chksum != sum) {
1037 			__pmNotifyErr(LOG_WARNING, "pmLoadNameSpace: checksum failure for binary file \"%s\"", linebuf);
1038 			fclose(fbin);
1039 			continue;
1040 		    }
1041 		    fseek(fbin, endsum, SEEK_SET);
1042 		}
1043 		if (version == -1) {
1044 		    fclose(fbin);
1045 		    continue;
1046 		}
1047 	
1048 		if (version == 0) {
1049 		    /*
1050 		     * Expunge support for Version 0 binary PMNS format.
1051 		     * It can never work on anything but 32-bit int and 32-bit ptrs.
1052 		     */
1053 		    goto bad;
1054 		}
1055 		else if (version == 1 || version == 2) {
1056 		    int		sz_htab_ent;
1057 		    int		sz_nodetab_ent;
1058 	
1059 		    if (fread(&symbsize, sizeof(symbsize), 1, fbin) != 1) goto bad;
1060 		    symbsize = ntohl(symbsize);
Event alloc_fn: Calling allocation function "malloc".
Event var_assign: Assigning: "symbol" = storage returned from "malloc(symbsize)".
Also see events: [noescape][leaked_storage]
1061 		    symbol = (char *)malloc(symbsize);
At conditional (1): "symbol == NULL": Taking false branch.
1062 		    if (symbol == NULL) {
1063 			__pmNoMem("loadbinary-symbol", symbsize, PM_FATAL_ERR);
1064 			/*NOTREACHED*/
1065 		    }
Event noescape: Variable "symbol" is not freed or pointed-to in function "fread".
Also see events: [alloc_fn][var_assign][leaked_storage]
At conditional (2): "fread(symbol, sizeof (symbol[0]) /*1*/, symbsize, fbin) != symbsize": Taking true branch.
1066 		    if (fread(symbol, sizeof(symbol[0]), 
1067 		        symbsize, fbin) != symbsize) goto bad;
1068 	
1069 	
1070 		    /* once for each style ... or until EOF */
1071 		    for ( ; ; ) {
1072 			long	skip;
1073 	
1074 			if (fread(&htabsize, sizeof(htabsize), 1, fbin) != 1) goto bad;
1075 			htabsize = ntohl(htabsize);
1076 			if (fread(&sz_htab_ent, sizeof(sz_htab_ent), 1, fbin) != 1) goto bad;
1077 			sz_htab_ent = ntohl(sz_htab_ent);
1078 			if (fread(&nodecnt, sizeof(nodecnt), 1, fbin) != 1) goto bad;
1079 			nodecnt = ntohl(nodecnt);
1080 			if (fread(&sz_nodetab_ent, sizeof(sz_nodetab_ent), 1, fbin) != 1) goto bad;
1081 			sz_nodetab_ent = ntohl(sz_nodetab_ent);
1082 			if (sz_htab_ent == sizeof(htab[0]) && sz_nodetab_ent == sizeof(*root))
1083 			   break; /* found correct one */
1084 	
1085 			/* skip over hash-table and node-table */
1086 			skip = htabsize * sz_htab_ent + nodecnt * sz_nodetab_ent;
1087 			fseek(fbin, skip, SEEK_CUR);
1088 		    }
1089 	
1090 		    /* the structure elements are all the right size */
1091 		    main_pmns = (__pmnsTree*)malloc(sizeof(*main_pmns));
1092 		    htab = (__pmnsNode **)malloc(htabsize * sizeof(htab[0]));
1093 		    root = (__pmnsNode *)malloc(nodecnt * sizeof(*root));
1094 	
1095 		    if (main_pmns == NULL || htab == NULL || root == NULL) {
1096 			__pmNoMem("loadbinary-1",
1097 				 sizeof(*main_pmns) +
1098 				 htabsize * sizeof(htab[0]) + 
1099 				 nodecnt * sizeof(*root),
1100 				 PM_FATAL_ERR);
1101 			/*NOTREACHED*/
1102 		    }
1103 	
1104 		    if (fread(htab, sizeof(htab[0]), htabsize, fbin) != htabsize) goto bad;
1105 		    if (fread(root, sizeof(*root), nodecnt, fbin) != nodecnt) goto bad;
1106 	
1107 	#if defined(HAVE_32BIT_PTR)
1108 		    /* swab htab : pointers are 32 bits */
1109 		    for (i=0; i < htabsize; i++) {
1110 			htab[i] = (__pmnsNode *)ntohl((__uint32_t)htab[i]);
1111 		    }
1112 	
1113 		    /* swab all nodes : pointers are 32 bits */
1114 		    for (i=0; i < nodecnt; i++) {
1115 			__pmnsNode *p = &root[i];
1116 			p->pmid = __ntohpmID(p->pmid);
1117 			p->parent = (__pmnsNode *)ntohl((__uint32_t)p->parent);
1118 			p->next = (__pmnsNode *)ntohl((__uint32_t)p->next);
1119 			p->first = (__pmnsNode *)ntohl((__uint32_t)p->first);
1120 			p->hash = (__pmnsNode *)ntohl((__uint32_t)p->hash);
1121 			p->name = (char *)ntohl((__uint32_t)p->name);
1122 		    }
1123 	#elif defined(HAVE_64BIT_PTR)
1124 		    /* swab htab : pointers are 64 bits */
1125 		    for (i=0; i < htabsize; i++) {
1126 			__ntohll((char *)&htab[i]);
1127 		    }
1128 	
1129 		    /* swab all nodes : pointers are 64 bits */
1130 		    for (i=0; i < nodecnt; i++) {
1131 			__pmnsNode *p = &root[i];
1132 			p->pmid = __ntohpmID(p->pmid);
1133 			__ntohll((char *)&p->parent);
1134 			__ntohll((char *)&p->next);
1135 			__ntohll((char *)&p->first);
1136 			__ntohll((char *)&p->hash);
1137 			__ntohll((char *)&p->name);
1138 		    }
1139 	#else
1140 	!bozo!
1141 	#endif
1142 	
1143 	#ifdef PCP_DEBUG
1144 		    if (pmDebug & DBG_TRACE_PMNS)
1145 			fprintf(stderr, "Loaded Version 1 or 2 Binary PMNS, nodetab ent = %d bytes\n", sz_nodetab_ent);
1146 	#endif
1147 		}
1148 	
1149 		fclose(fbin);
1150 	
1151 		/* relocate */
1152 		for (i = 0; i < htabsize; i++) {
1153 		    ord = (ptrdiff_t)htab[i];
1154 		    if (ord == (__psint_t)-1)
1155 			htab[i] = NULL;
1156 		    else
1157 			htab[i] = &root[ord];
1158 		}
1159 	
1160 		for (i = 0; i < nodecnt; i++) {
1161 		    ord = (__psint_t)root[i].parent;
1162 		    if (ord == (__psint_t)-1)
1163 			root[i].parent = NULL;
1164 		    else
1165 			root[i].parent = &root[ord];
1166 		    ord = (__psint_t)root[i].next;
1167 		    if (ord == (__psint_t)-1)
1168 			root[i].next = NULL;
1169 		    else
1170 			root[i].next = &root[ord];
1171 		    ord = (__psint_t)root[i].first;
1172 		    if (ord == (__psint_t)-1)
1173 			root[i].first = NULL;
1174 		    else
1175 			root[i].first = &root[ord];
1176 		    ord = (__psint_t)root[i].hash;
1177 		    if (ord == (__psint_t)-1)
1178 			root[i].hash = NULL;
1179 		    else
1180 			root[i].hash = &root[ord];
1181 		    ord = (__psint_t)root[i].name;
1182 		    root[i].name = &symbol[ord];
1183 		}
1184 	
1185 		/* set the pmns tree fields */
1186 		main_pmns->root = root;
1187 		main_pmns->htab = htab;
1188 		main_pmns->htabsize = htabsize;
1189 		main_pmns->symbol = symbol;
1190 		main_pmns->contiguous = 1;
1191 		main_pmns->mark_state = UNKNOWN_MARK_STATE;
1192 		return 1;
1193 		
1194 	bad:
1195 		__pmNotifyErr(LOG_WARNING, "pmLoadNameSpace: bad binary file, \"%s\"", linebuf);
1196 		fclose(fbin);
Event leaked_storage: Variable "symbol" going out of scope leaks the storage it points to.
Also see events: [alloc_fn][var_assign][noescape]
1197 		return 0;
1198 	    }
1199 	
1200 	    /* failed to open and/or find magic cookie */
1201 	    return 0;
1202 	}
1203 	
1204 	
1205 	/*
1206 	 * fsa for parser
1207 	 *
1208 	 *	old	token	new
1209 	 *	0	NAME	1
1210 	 *	0	PATH	1
1211 	 *	1	LBRACE	2
1212 	 *      2	NAME	3
1213 	 *	2	RBRACE	0
1214 	 *	3	NAME	3
1215 	 *	3	PMID	2
1216 	 *	3	RBRACE	0
1217 	 */
1218 	static int
1219 	loadascii(int dupok)
1220 	{
1221 	    int		state = 0;
1222 	    int		type;
1223 	    __pmnsNode	*np = NULL;	/* pander to gcc */
1224 	
1225 	#ifdef PCP_DEBUG
1226 	    if (pmDebug & DBG_TRACE_PMNS)
1227 		fprintf(stderr, "loadascii(file=%s)\n", fname);
1228 	#endif
1229 	
1230 	
1231 	    /* do some resets */
1232 	    lex(1);      /* reset analyzer */
1233 	    seen = NULL; /* make seen-list empty */
1234 	    numpmid = 0;
1235 	
1236 	
1237 	    if (access(fname, R_OK) == -1) {
1238 		snprintf(linebuf, sizeof(linebuf), "Cannot open \"%s\"", fname);
1239 		err(linebuf);
1240 		return -oserror();
1241 	    }
1242 	    lineno = 1;
1243 	
1244 	    while ((type = lex(0)) > 0) {
1245 		switch (state) {
1246 	
1247 		case 0:
1248 		    if (type != NAME && type != PATH) {
1249 			err("Expected NAME or PATH");
1250 			return PM_ERR_PMNS;
1251 		    }
1252 		    state = 1;
1253 		    break;
1254 	
1255 		case 1:
1256 		    if (type != LBRACE) {
1257 			err("{ expected");
1258 			return PM_ERR_PMNS;
1259 		    }
1260 		    state = 2;
1261 		    break;
1262 	
1263 		case 2:
1264 		    if (type == NAME) {
1265 			state = 3;
1266 		    }
1267 		    else if (type == RBRACE) {
1268 			state = 0;
1269 		    }
1270 		    else {
1271 			err("Expected NAME or }");
1272 			return PM_ERR_PMNS;
1273 		    }
1274 		    break;
1275 	
1276 		case 3:
1277 		    if (type == NAME) {
1278 			state = 3;
1279 		    }
1280 		    else if (type == PMID) {
1281 			np->pmid = tokpmid;
1282 			state = 2;
1283 	#ifdef PCP_DEBUG
1284 			if (pmDebug & DBG_TRACE_PMNS) {
1285 			    fprintf(stderr, "pmLoadNameSpace: %s -> %s\n",
1286 						np->name, pmIDStr(np->pmid));
1287 			}
1288 	#endif
1289 		    }
1290 		    else if (type == RBRACE) {
1291 			state = 0;
1292 		    }
1293 		    else {
1294 			err("Expected NAME, PMID or }");
1295 			return PM_ERR_PMNS;
1296 		    }
1297 		    break;
1298 	
1299 		default:
1300 		    err("Internal botch");
1301 		    abort();
1302 	
1303 		}
1304 	
1305 		if (state == 1 || state == 3) {
1306 		    if ((np = (__pmnsNode *)malloc(sizeof(*np))) == NULL)
1307 			return -oserror();
1308 		    numpmid++;
1309 		    if ((np->name = (char *)malloc(strlen(tokbuf)+1)) == NULL)
1310 			return -oserror();
1311 		    strcpy(np->name, tokbuf);
1312 		    np->first = np->hash = np->next = np->parent = NULL;
1313 		    np->pmid = PM_ID_NULL;
1314 		    if (state == 1) {
1315 			np->next = seen;
1316 			seen = np;
1317 		    }
1318 		    else {
1319 			if (seen->hash)
1320 			    seen->hash->next = np;
1321 			else
1322 			    seen->first = np;
1323 			seen->hash = np;
1324 		    }
1325 		}
1326 		else if (state == 0) {
1327 		    if (seen) {
1328 			__pmnsNode	*xp;
1329 	
1330 			for (np = seen->first; np != NULL; np = np->next) {
1331 			    for (xp = np->next; xp != NULL; xp = xp->next) {
1332 				if (strcmp(xp->name, np->name) == 0) {
1333 				    snprintf(linebuf, sizeof(linebuf), "Duplicate name \"%s\" in subtree for \"%s\"\n",
1334 				        np->name, seen->name);
1335 				    err(linebuf);
1336 				    return PM_ERR_PMNS;
1337 				}
1338 			    }
1339 			}
1340 		    }
1341 		}
1342 	    }
1343 	
1344 	    if (type == 0)
1345 		type = pass2(dupok);
1346 	
1347 	
1348 	    if (type == 0) {
1349 	#ifdef PCP_DEBUG
1350 		if (pmDebug & DBG_TRACE_PMNS)
1351 		    fprintf(stderr, "Loaded ASCII PMNS\n");
1352 	#endif
1353 	    }
1354 	
1355 	    return type;
1356 	}
1357 	
1358 	static const char * 
1359 	getfname(const char *filename)
1360 	{
1361 	    /*
1362 	     * 0xffffffff is there to maintain backwards compatibility with PCP 1.0
1363 	     */
1364 	    if (filename == PM_NS_DEFAULT || (__psint_t)filename == 0xffffffff) {
1365 		char	*def_pmns;
1366 	
1367 		def_pmns = getenv("PMNS_DEFAULT");
1368 		if (def_pmns != NULL) {
1369 		    /* get default PMNS name from environment */
1370 		    return def_pmns;
1371 		}
1372 		else {
1373 		    static char repname[MAXPATHLEN];
1374 		    int sep = __pmPathSeparator();
1375 		    snprintf(repname, sizeof(repname), "%s%c" "pmns" "%c" "root",
1376 			     pmGetConfig("PCP_VAR_DIR"), sep, sep);
1377 		    return repname;
1378 		}
1379 	    }
1380 	    return filename;
1381 	}
1382 	
1383 	int
1384 	__pmHasPMNSFileChanged(const char *filename)
1385 	{
1386 	    static const char *f;
1387 	
1388 	    f = getfname(filename);
1389 	    if (f == NULL)
1390 		return 1; /* error encountered -> must have changed :) */
1391 	 
1392 	    /* if still using same filename ... */
1393 	    if (strcmp(f, fname) == 0) {
1394 		struct stat statbuf;
1395 	
1396 		if (stat(f, &statbuf) == 0) {
1397 		    /* If the modification times have changed */
1398 	#if defined(HAVE_ST_MTIME_WITH_E) && defined(HAVE_STAT_TIME_T)
1399 	#ifdef PCP_DEBUG
1400 		    if (pmDebug & DBG_TRACE_PMNS) {
1401 			fprintf(stderr,
1402 				"__pmHasPMNSFileChanged(%s) -> %s last=%d now=%d\n",
1403 				filename == PM_NS_DEFAULT ||
1404 				(__psint_t)filename == 0xffffffff ?
1405 					"PM_NS_DEFAULT" : filename,
1406 				f, (int)last_mtim, (int)statbuf.st_mtime);
1407 		    }
1408 	#endif
1409 		    return ((statbuf.st_mtime == last_mtim) ? 0 : 1);
1410 	#elif defined(HAVE_ST_MTIME_WITH_SPEC)
1411 	#ifdef PCP_DEBUG
1412 		    if (pmDebug & DBG_TRACE_PMNS) {
1413 			fprintf(stderr,
1414 				"__pmHasPMNSFileChanged(%s) -> %s last=%d.%09ld now=%d.%09ld\n",
1415 				filename == PM_NS_DEFAULT ||
1416 				(__psint_t)filename == 0xffffffff ?
1417 					"PM_NS_DEFAULT" : filename,
1418 				f, (int)last_mtim.tv_sec, last_mtim.tv_nsec,
1419 				(int)statbuf.st_mtimespec.tv_sec,
1420 				statbuf.st_mtimespec.tv_nsec);
1421 		    }
1422 	#endif
1423 		    return ((statbuf.st_mtimespec.tv_sec == last_mtim.tv_sec &&
1424 			statbuf.st_mtimespec.tv_nsec == last_mtim.tv_nsec) ? 0 : 1);
1425 	#elif defined(HAVE_STAT_TIMESTRUC) || defined(HAVE_STAT_TIMESPEC) || defined(HAVE_STAT_TIMESPEC_T)
1426 	#ifdef PCP_DEBUG
1427 		    if (pmDebug & DBG_TRACE_PMNS) {
1428 			fprintf(stderr,
1429 				"__pmHasPMNSFileChanged(%s) -> %s last=%d.%09ld now=%d.%09ld\n",
1430 				filename == PM_NS_DEFAULT ||
1431 				(__psint_t)filename == 0xffffffff ?
1432 					"PM_NS_DEFAULT" : filename,
1433 				f, (int)last_mtim.tv_sec, last_mtim.tv_nsec,
1434 				(int)statbuf.st_mtim.tv_sec, statbuf.st_mtim.tv_nsec);
1435 		    }
1436 	#endif
1437 		    return ((statbuf.st_mtim.tv_sec == last_mtim.tv_sec &&
1438 			    (statbuf.st_mtim.tv_nsec == last_mtim.tv_nsec)) ? 0 : 1);
1439 	#else
1440 	!bozo!
1441 	#endif
1442 		}
1443 		else {
1444 		    return 1;	/* error encountered -> must have changed */
1445 		}
1446 	    }
1447 	    return 1;	/* different filenames at least */
1448 	}
1449 	
1450 	static int
1451 	load(const char *filename, int binok, int dupok)
1452 	{
1453 	    int 	i = 0;
1454 	
1455 	    if (main_pmns != NULL) {
1456 		if (export) {
1457 		    export = 0;
1458 	
1459 		    /*
1460 		     * drop the loaded PMNS ... huge memory leak, but it is
1461 		     * assumed the caller has saved the previous PMNS after calling
1462 		     * __pmExportPMNS()
1463 		     */
1464 		    main_pmns = NULL;
1465 		}
1466 		else {
1467 		    return PM_ERR_DUPPMNS;
1468 		}
1469 	    }
1470 	
1471 	    strcpy(fname, getfname(filename));
1472 	 
1473 	#ifdef PCP_DEBUG
1474 	    if (pmDebug & DBG_TRACE_PMNS)
1475 		fprintf(stderr, "load(name=%s, binok=%d, dupok=%d) lic case=%d fname=%s\n",
1476 			filename, binok, dupok, i, fname);
1477 	#endif
1478 	
1479 	    /* Note modification time of pmns file */
1480 	    {
1481 		struct stat statbuf;
1482 	
1483 		if (stat(fname, &statbuf) == 0) {
1484 	#if defined(HAVE_ST_MTIME_WITH_E)
1485 		    last_mtim = statbuf.st_mtime; /* possible struct assignment */
1486 	#elif defined(HAVE_ST_MTIME_WITH_SPEC)
1487 		    last_mtim = statbuf.st_mtimespec; /* possible struct assignment */
1488 	#else
1489 		    last_mtim = statbuf.st_mtim; /* possible struct assignment */
1490 	#endif
1491 		}
1492 	    }
1493 	
1494 	    /* try the easy way, c/o pmnscomp */
1495 	    if (binok && loadbinary()) {
1496 		mark_all(main_pmns, 0);
1497 		return 0;
1498 	    }
1499 	
1500 	    /*
1501 	     * the hard way, compiling as we go ...
1502 	     */
1503 	    return loadascii(dupok);
1504 	}
1505 	
1506 	/*
1507 	 * just for pmnscomp to use
1508 	 */
1509 	__pmnsTree*
1510 	__pmExportPMNS(void)
1511 	{
1512 	    export = 1;
1513 	    return main_pmns;
1514 	}
1515 	
1516 	/*
1517 	 * Find and return the named node in the tree, root.
1518 	 */
1519 	static __pmnsNode *
1520 	locate(const char *name, __pmnsNode *root)
1521 	{
1522 	    const char	*tail;
1523 	    ptrdiff_t	nch;
1524 	    __pmnsNode	*np;
1525 	
1526 	    /* Traverse until '.' or '\0' */
1527 	    for (tail = name; *tail && *tail != '.'; tail++)
1528 		;
1529 	
1530 	    nch = tail - name;
1531 	
1532 	    /* Compare name with all the child nodes */
1533 	    for (np = root->first; np != NULL; np = np->next) {
1534 		if (strncmp(name, np->name, (int)nch) == 0 && np->name[(int)nch] == '\0' &&
1535 		    (np->pmid & MARK_BIT) == 0)
1536 		    break;
1537 	    }
1538 	
1539 	    if (np == NULL) /* no match with child */
1540 		return NULL;
1541 	    else if (*tail == '\0') /* matched with whole path */
1542 		return np;
1543 	    else
1544 		return locate(tail+1, np); /* try matching with rest of pathname */
1545 	}
1546 	
1547 	/*
1548 	 * PMAPI routines from here down
1549 	 */
1550 	
1551 	int
1552 	pmLoadNameSpace(const char *filename)
1553 	{
1554 	    havePmLoadCall = 1;
1555 	    return load(filename, 1, 0);
1556 	}
1557 	
1558 	int
1559 	pmLoadASCIINameSpace(const char *filename, int dupok)
1560 	{
1561 	    havePmLoadCall = 1;
1562 	    return load(filename, 0, dupok);
1563 	}
1564 	
1565 	/*
1566 	 * Assume that each node has been malloc'ed separately.
1567 	 * This is the case for an ASCII loaded PMNS.
1568 	 * Traverse entire tree and free each node.
1569 	 */
1570 	static void
1571 	FreeTraversePMNS(__pmnsNode *parent)
1572 	{
1573 	    __pmnsNode *np, *next;
1574 	
1575 	    if (!parent)
1576 		return;
1577 	
1578 	    /* Free child sub-trees */
1579 	    for (np = parent->first; np != NULL; np = next) {
1580 		next = np->next;
1581 		FreeTraversePMNS(np);
1582 	    }
1583 	
1584 	    free(parent->name);
1585 	    free(parent);
1586 	}
1587 	
1588 	void
1589 	__pmFreePMNS(__pmnsTree *pmns)
1590 	{
1591 	    if (pmns != NULL) {
1592 		if (pmns->contiguous) {
1593 		    free(pmns->root);
1594 		    free(pmns->htab);
1595 		    free(pmns->symbol);
1596 		}
1597 		else { 
1598 		    free(pmns->htab);
1599 		    FreeTraversePMNS(pmns->root); 
1600 		}
1601 	
1602 		free(pmns);
1603 	    }
1604 	}
1605 	
1606 	void
1607 	pmUnloadNameSpace(void)
1608 	{
1609 	    havePmLoadCall = 0;
1610 	    __pmFreePMNS(main_pmns);
1611 	    main_pmns = NULL;
1612 	}
1613 	
1614 	static int
1615 	request_names(__pmContext *ctxp, int numpmid, char *namelist[])
1616 	{
1617 	    int n;
1618 	
1619 	#ifdef ASYNC_API
1620 	    if (ctxp->c_pmcd->pc_curpdu != 0)
1621 		return PM_ERR_CTXBUSY;
1622 	#endif /*ASYNC_API*/
1623 	
1624 	    n = __pmSendNameList(ctxp->c_pmcd->pc_fd, __pmPtrToHandle(ctxp),
1625 			numpmid, namelist, NULL);
1626 	    if (n < 0)
1627 		n = __pmMapErrno(n);
1628 	
1629 	    return n;
1630 	}
1631 	
1632 	#ifdef ASYNC_API
1633 	int
1634 	pmRequestNames(int ctxid, int numpmid, char *namelist[])
1635 	{
1636 	    int n;
1637 	    __pmContext *ctxp;
1638 	
1639 	    if ((n =__pmGetHostContextByID(ctxid, &ctxp)) >= 0) {
1640 		if ((n = request_names(ctxp, numpmid, namelist)) >= 0) {
1641 		    ctxp->c_pmcd->pc_curpdu = PDU_PMNS_NAMES;
1642 		    ctxp->c_pmcd->pc_tout_sec = TIMEOUT_DEFAULT;
1643 		}
1644 	    }
1645 	
1646 	    return n;
1647 	}
1648 	#endif /*ASYNC_API*/
1649 	
1650 	static int
1651 	receive_names(__pmContext *ctxp, int numpmid, pmID pmidlist[])
1652 	{
1653 	    int n;
1654 	    __pmPDU      *pb;
1655 	
1656 	    n = __pmGetPDU(ctxp->c_pmcd->pc_fd, ANY_SIZE,
1657 			   ctxp->c_pmcd->pc_tout_sec, &pb);
1658 	    if (n == PDU_PMNS_IDS) {
1659 		/* Note:
1660 		 * pmLookupName may return an error even though
1661 		 * it has a valid list of ids.
1662 		 * This is why we need op_status.
1663 		 */
1664 		int op_status; 
1665 		n = __pmDecodeIDList(pb, numpmid, pmidlist, &op_status);
1666 		if (n >= 0)
1667 		    n = op_status;
1668 	    }
1669 	    else if (n == PDU_ERROR) {
1670 		__pmDecodeError(pb, &n);
1671 	    }
1672 	    else if (n != PM_ERR_TIMEOUT) {
1673 		n = PM_ERR_IPC;
1674 	    }
1675 	
1676 	    return n;
1677 	}
1678 	
1679 	#ifdef ASYNC_API
1680 	int
1681 	pmReceiveNames(int ctxid, int numpmid, pmID pmidlist[])
1682 	{
1683 	    int n;
1684 	    __pmContext *ctxp;
1685 	
1686 	    if ((n =__pmGetBusyHostContextByID(ctxid, &ctxp, PDU_PMNS_NAMES)) >= 0) {
1687 		n = receive_names(ctxp, numpmid, pmidlist);
1688 		ctxp->c_pmcd->pc_curpdu = 0;
1689 		ctxp->c_pmcd->pc_tout_sec = 0;
1690 	    }
1691 	
1692 	    return n;
1693 	}
1694 	#endif /*ASYNC_API*/
1695 	
1696 	int
1697 	pmLookupName(int numpmid, char *namelist[], pmID pmidlist[])
1698 	{
1699 	    int pmns_location;
1700 	    int	sts = 0;
1701 	    __pmContext	*ctxp;
1702 	    int		lsts;
1703 	    int		ctx;
1704 	    int		i;
1705 	    int		nfail = 0;
1706 	
1707 	    if (numpmid < 1) {
1708 	#ifdef PCP_DEBUG
1709 		if (pmDebug & DBG_TRACE_PMNS) {
1710 		    fprintf(stderr, "pmLookupName(%d, ...) bad numpmid!\n", numpmid);
1711 		}
1712 	#endif
1713 		return PM_ERR_TOOSMALL;
1714 	    }
1715 	
1716 	    ctx = lsts = pmWhichContext();
1717 	    if (lsts >= 0)
1718 		ctxp = __pmHandleToPtr(lsts);
1719 	    else
1720 		ctxp = NULL;
1721 	
1722 	    pmns_location = GetLocation();
1723 	    
1724 	    if (pmns_location < 0) {
1725 		sts = pmns_location;
1726 		/* only hope is derived metrics ... set up for this */
1727 		for (i = 0; i < numpmid; i++) {
1728 		    pmidlist[i] = PM_ID_NULL;
1729 		    nfail++;
1730 		}
1731 	    }
1732 	    else if (pmns_location == PMNS_LOCAL) {
1733 		char		*xname;
1734 		char		*xp;
1735 		__pmnsNode	*np;
1736 	
1737 		for (i = 0; i < numpmid; i++) {
1738 		    /*
1739 		     * if we locate the name and it is a leaf in the PMNS
1740 		     * this is good
1741 		     */
1742 		    if ((np = locate(namelist[i], curr_pmns->root)) != NULL) {
1743 			if (np->first == NULL)
1744 			    pmidlist[i] = np->pmid;
1745 			else {
1746 			    sts = PM_ERR_NONLEAF;
1747 			    pmidlist[i] = PM_ID_NULL;
1748 			    nfail++;
1749 			}
1750 			continue;
1751 		    }
1752 		    pmidlist[i] = PM_ID_NULL;
1753 		    nfail++;
1754 		    /*
1755 		     * did not match name in PMNS ... try for prefix matching
1756 		     * the name to the root of a dynamic subtree of the PMNS,
1757 		     * or possibly we're using a local context and then we may
1758 		     * be able to ship request to PMDA
1759 		     */
1760 		    xname = strdup(namelist[i]);
1761 		    if (xname == NULL) {
1762 			__pmNoMem("pmLookupName", strlen(namelist[i])+1, PM_RECOV_ERR);
1763 			sts = -oserror();
1764 			continue;
1765 		    }
1766 		    while ((xp = rindex(xname, '.')) != NULL) {
1767 			*xp = '\0';
1768 			lsts = 0;
1769 			np = locate(xname, curr_pmns->root);
1770 			if (np != NULL && np->first == NULL &&
1771 			    pmid_domain(np->pmid) == DYNAMIC_PMID &&
1772 			    pmid_item(np->pmid) == 0) {
1773 			    /* root of dynamic subtree */
1774 			    if (ctxp != NULL && ctxp->c_type == PM_CONTEXT_LOCAL) {
1775 				/* have PM_CONTEXT_LOCAL ... ship request to PMDA */
1776 				int	domain = ((__pmID_int *)&np->pmid)->cluster;
1777 				__pmDSO	*dp;
1778 				if ((dp = __pmLookupDSO(domain)) == NULL) {
1779 				    if (sts >= 0) sts = PM_ERR_NOAGENT;
1780 				    break;
1781 				}
1782 				if (dp->dispatch.comm.pmda_interface >= PMDA_INTERFACE_5)
1783 				    dp->dispatch.version.four.ext->e_context = ctx;
1784 				if (dp->dispatch.comm.pmda_interface >= PMDA_INTERFACE_4) {
1785 				    lsts = dp->dispatch.version.four.pmid(namelist[i], &pmidlist[i], dp->dispatch.version.four.ext);
1786 				    if (lsts >= 0)
1787 					nfail--;
1788 	
1789 				    break;
1790 				}
1791 			    }
1792 			    else {
1793 				/* No PM_LOCAL_CONTEXT, use PMID from PMNS */
1794 				pmidlist[i] = np->pmid;
1795 				nfail--;
1796 				break;
1797 			    }
1798 			}
1799 		    }
1800 		    free(xname);
1801 		}
1802 	
1803 	    	sts = (sts == 0 ? numpmid - nfail : sts);
1804 	
1805 	#ifdef PCP_DEBUG
1806 		if (pmDebug & DBG_TRACE_PMNS) {
1807 		    int	i;
1808 		    fprintf(stderr, "pmLookupName(%d, ...) using local PMNS returns %d and ...\n",
1809 			numpmid, sts);
1810 		    for (i = 0; i < numpmid; i++) {
1811 			fprintf(stderr, "  name[%d]: \"%s\"", i, namelist[i]);
1812 			if (sts >= 0)
1813 			    fprintf(stderr, " PMID: 0x%x %s",
1814 				pmidlist[i], pmIDStr(pmidlist[i]));
1815 			fputc('\n', stderr);
1816 		    }
1817 		}
1818 	#endif
1819 	    }
1820 	    else {
1821 		/*
1822 		 * PMNS_REMOTE so there must be a current host context
1823 		 */
1824 		assert(ctxp != NULL && ctxp->c_type == PM_CONTEXT_HOST);
1825 	#ifdef PCP_DEBUG
1826 		if (pmDebug & DBG_TRACE_PMNS) {
1827 		    fprintf(stderr, "pmLookupName: request_names ->");
1828 		    for (i = 0; i < numpmid; i++)
1829 			fprintf(stderr, " [%d] %s", i, namelist[i]);
1830 		    fputc('\n', stderr);
1831 		}
1832 	#endif
1833 		if ((sts = request_names (ctxp, numpmid, namelist)) >= 0) {
1834 		    sts = receive_names(ctxp, numpmid, pmidlist);
1835 		    if (sts >= 0)
1836 			nfail = numpmid - sts;
1837 	#ifdef PCP_DEBUG
1838 		    if (pmDebug & DBG_TRACE_PMNS) {
1839 			fprintf(stderr, "pmLookupName: receive_names <-");
1840 			if (sts >= 0) {
1841 			    for (i = 0; i < numpmid; i++)
1842 				fprintf(stderr, " [%d] %s", i, pmIDStr(pmidlist[i]));
1843 			    fputc('\n', stderr);
1844 			}
1845 		    else
1846 			fprintf(stderr, " %s\n", pmErrStr(sts));
1847 		    }
1848 	#endif
1849 		}
1850 	    }
1851 	
1852 	    if (sts < 0 || nfail > 0) {
1853 		/*
1854 		 * Try derived metrics for any remaining unknown pmids.
1855 		 * The return status is a little tricky ... prefer the status
1856 		 * from above unless all of the remaining unknown PMIDs are
1857 		 * resolved by __dmgetpmid() in which case success (numpmid)
1858 		 * is the right return status
1859 		 */
1860 		nfail = 0;
1861 		for (i = 0; i < numpmid; i++) {
1862 		    if (pmidlist[i] == PM_ID_NULL) {
1863 			lsts = __dmgetpmid(namelist[i], &pmidlist[i]);
1864 			if (lsts < 0) {
1865 			    nfail++;
1866 			}
1867 	#ifdef PCP_DEBUG
1868 			if (pmDebug & DBG_TRACE_DERIVE) {
1869 			    fprintf(stderr, "__dmgetpmid: metric \"%s\" -> ", namelist[i]);
1870 			    if (lsts < 0)
1871 				fprintf(stderr, "%s\n", pmErrStr(lsts));
1872 			    else
1873 				fprintf(stderr, "PMID %s\n", pmIDStr(pmidlist[i]));
1874 			}
1875 	#endif
1876 		    }
1877 		}
1878 		if (nfail == 0)
1879 		    sts = numpmid;
1880 	    }
1881 	
1882 	    /*
1883 	     * special case for a single metric, PM_ERR_NAME is more helpful than
1884 	     * returning 0 and having one PM_ID_NULL pmid
1885 	     */
1886 	    if (sts == 0 && numpmid == 1)
1887 		sts = PM_ERR_NAME;
1888 	
1889 	#ifdef PCP_DEBUG
1890 	    if (pmDebug & DBG_TRACE_PMNS) {
1891 		fprintf(stderr, "pmLookupName(%d, ...) -> ", numpmid);
1892 		if (sts < 0)
1893 		    fprintf(stderr, "%s\n", pmErrStr(sts));
1894 		else
1895 		    fprintf(stderr, "%d\n", sts);
1896 	    }
1897 	#endif
1898 	
1899 	    return sts;
1900 	}
1901 	
1902 	static int
1903 	request_names_of_children(__pmContext *ctxp, const char *name, int wantstatus)
1904 	{
1905 	    int n;
1906 	
1907 	#ifdef ASYNC_API
1908 	    if (ctxp->c_pmcd->pc_curpdu != 0)
1909 		return PM_ERR_CTXBUSY;
1910 	#endif /*ASYNC_API*/
1911 	
1912 	    n = __pmSendChildReq(ctxp->c_pmcd->pc_fd, __pmPtrToHandle(ctxp),
1913 			name, wantstatus);
1914 	    if (n < 0)
1915 		n =  __pmMapErrno(n);
1916 	    return n;
1917 	}
1918 	
1919 	#ifdef ASYNC_API
1920 	int
1921 	pmRequestNamesOfChildren(int ctxid, const char *name, int wantstatus)
1922 	{
1923 	    int n;
1924 	    __pmContext *ctxp;
1925 	
1926 	    if ((n = __pmGetHostContextByID(ctxid, &ctxp)) >= 0) {
1927 		if ((n = request_names_of_children(ctxp, name, wantstatus)) >= 0) {
1928 		    ctxp->c_pmcd->pc_curpdu = PDU_PMNS_CHILD;
1929 		    ctxp->c_pmcd->pc_tout_sec = TIMEOUT_DEFAULT;
1930 		}
1931 	    }
1932 	
1933 	    return n;
1934 	}
1935 	#endif /*ASYNC_API*/
1936 	
1937 	static int
1938 	receive_names_of_children(__pmContext *ctxp, char ***offspring,
1939 				  int **statuslist)
1940 	{
1941 	    int n;
1942 	    __pmPDU      *pb;
1943 	
1944 	    n = __pmGetPDU(ctxp->c_pmcd->pc_fd, ANY_SIZE, 
1945 			   ctxp->c_pmcd->pc_tout_sec, &pb);
1946 	    if (n == PDU_PMNS_NAMES) {
1947 		int numnames;
1948 	
1949 		n = __pmDecodeNameList(pb, &numnames, offspring, statuslist);
1950 		if (n >= 0)
1951 		    n = numnames;
1952 	    }
1953 	    else if (n == PDU_ERROR)
1954 		__pmDecodeError(pb, &n);
1955 	    else if (n != PM_ERR_TIMEOUT)
1956 		n = PM_ERR_IPC;
1957 	    return n;
1958 	}
1959 	
1960 	#ifdef ASYNC_API
1961 	int
1962 	pmReceiveNamesOfChildren(int ctxid, char ***offsprings, int **status)
1963 	{
1964 	    int n;
1965 	    __pmContext *ctxp;
1966 	
1967 	    if ((n = __pmGetBusyHostContextByID (ctxid, &ctxp, PDU_PMNS_CHILD)) >= 0) {
1968 		n = receive_names_of_children (ctxp, offsprings, status);
1969 	
1970 		ctxp->c_pmcd->pc_curpdu = 0;
1971 		ctxp->c_pmcd->pc_tout_sec = 0;
1972 	    }
1973 	
1974 	    return n;
1975 	}
1976 	#endif /*ASYNC_API*/
1977 	
1978 	static int
1979 	GetChildrenStatusRemote(__pmContext *ctxp, const char *name,
1980 				char ***offspring, int **statuslist)
1981 	{
1982 	    int n;
1983 	
1984 	    if ((n = request_names_of_children(ctxp, name,
1985 					       (statuslist==NULL) ? 0 : 1)) >= 0) {
1986 		n = receive_names_of_children(ctxp, offspring, statuslist);
1987 	    }
1988 	    return n;
1989 	}
1990 	
1991 	static void
1992 	stitch_list(int *num, char ***offspring, int **statuslist, int x_num, char **x_offspring, int *x_statuslist)
1993 	{
1994 	    /*
1995 	     * so this gets tricky ... need to stitch the additional metrics
1996 	     * (derived metrics or dynamic metrics) at the end of the existing
1997 	     * metrics (if any) after removing any duplicates (!) ... and honour
1998 	     * the bizarre pmGetChildren contract in terms of malloc'ing the
1999 	     * result arrays
2000 	     */
2001 	    int		n_num;
2002 	    char	**n_offspring;
2003 	    int		*n_statuslist = NULL;
2004 	    int		i;
2005 	    int		j;
2006 	    char	*q;
2007 	    size_t	need;
2008 	
2009 	    if (*num > 0)
2010 		n_num = *num + x_num;
2011 	    else
2012 		n_num = x_num;
2013 	
2014 	    for (i = 0; i < x_num; i++) {
2015 		for (j = 0; j < *num; j++) {
2016 		    if (strcmp(x_offspring[i], (*offspring)[j]) == 0) {
2017 			/* duplicate ... bugger */
2018 			n_num--;
2019 			free(x_offspring[i]);
2020 			x_offspring[i] = NULL;
2021 			break;
2022 		    }
2023 		}
2024 	    }
2025 	
2026 	    need = n_num*sizeof(char *);
2027 	    for (j = 0; j < *num; j++) {
2028 		need += strlen((*offspring)[j]) + 1;
2029 	    }
2030 	    for (i = 0; i < x_num; i++) {
2031 		if (x_offspring[i] != NULL) {
2032 		    need += strlen(x_offspring[i]) + 1;
2033 		}
2034 	    }
2035 	    if ((n_offspring = (char **)malloc(need)) == NULL) {
2036 		__pmNoMem("pmGetChildrenStatus: n_offspring", need, PM_FATAL_ERR);
2037 		/*NOTREACHED*/
2038 	    }
2039 	    if (statuslist != NULL) {
2040 		if ((n_statuslist = (int *)malloc(n_num*sizeof(n_statuslist[0]))) == NULL) {
2041 		    __pmNoMem("pmGetChildrenStatus: n_statuslist", n_num*sizeof(n_statuslist[0]), PM_FATAL_ERR);
2042 		    /*NOTREACHED*/
2043 		}
2044 	    }
2045 	    q = (char *)&n_offspring[n_num];
2046 	    for (j = 0; j < *num; j++) {
2047 		n_offspring[j] = q;
2048 		strcpy(q, (*offspring)[j]);
2049 		q += strlen(n_offspring[j]) + 1;
2050 		if (statuslist != NULL)
2051 		    n_statuslist[j] = (*statuslist)[j];
2052 	    }
2053 	    for (i = 0; i < x_num; i++) {
2054 		if (x_offspring[i] != NULL) {
2055 		    n_offspring[j] = q;
2056 		    strcpy(q, x_offspring[i]);
2057 		    q += strlen(n_offspring[j]) + 1;
2058 		    if (statuslist != NULL)
2059 			n_statuslist[j] = x_statuslist[i];
2060 		    j++;
2061 		}
2062 	    }
2063 	    if (*num > 0) {
2064 		free(*offspring);
2065 		if (statuslist != NULL)
2066 		    free(*statuslist);
2067 	    }
2068 	    *num = n_num;
2069 	    if (statuslist != NULL)
2070 		*statuslist = n_statuslist;
2071 	    *offspring = n_offspring;
2072 	}
2073 	
2074 	/*
2075 	 * It is allowable to pass in a statuslist arg of NULL. It is therefore
2076 	 * important to check that this is not NULL before accessing it.
2077 	 */
2078 	int
2079 	pmGetChildrenStatus(const char *name, char ***offspring, int **statuslist)
2080 	{
2081 	    int		*status = NULL;
2082 	    int		pmns_location = GetLocation();
2083 	    int		num;
2084 	    int		dm_num;
2085 	    char	**dm_offspring;
2086 	    int		*dm_statuslist;
2087 	    int		sts;
2088 	    int		ctx;
2089 	    __pmContext	*ctxp;
2090 	
2091 	    if (pmns_location < 0)
2092 		return pmns_location;
2093 	
2094 	    if (name == NULL) 
2095 		return PM_ERR_NAME;
2096 	
2097 	    ctx = sts = pmWhichContext();
2098 	    if (sts >= 0)
2099 		ctxp = __pmHandleToPtr(sts);
2100 	    else
2101 		ctxp = NULL;
2102 	
2103 	    if (pmns_location == PMNS_LOCAL) {
2104 		__pmnsNode	*np;
2105 		__pmnsNode	*tnp;
2106 		int		i;
2107 		int		need;
2108 		char		**result;
2109 		char		*p;
2110 	
2111 	#ifdef PCP_DEBUG
2112 		if (pmDebug & DBG_TRACE_PMNS) {
2113 		    fprintf(stderr, "pmGetChildren(name=\"%s\") [local]\n", name);
2114 		}
2115 	#endif
2116 	
2117 		/* avoids ambiguity, for errors and leaf nodes */
2118 		*offspring = NULL;
2119 		num = 0;
2120 		if (statuslist)
2121 		  *statuslist = NULL;
2122 	
2123 		if (*name == '\0')
2124 		    np = curr_pmns->root; /* use "" to name the root of the PMNS */
2125 		else
2126 		    np = locate(name, curr_pmns->root);
2127 		if (np == NULL) {
2128 		    if (ctxp != NULL && ctxp->c_type == PM_CONTEXT_LOCAL) {
2129 			/*
2130 			 * No match in PMNS and using PM_CONTEXT_LOCAL so for
2131 			 * dynamic metrics, need to consider prefix matches back to
2132 			 * the root on the PMNS to find a possible root of a dynamic
2133 			 * subtree, and hence the domain of the responsible PMDA
2134 			 */
2135 			char	*xname = strdup(name);
2136 			char	*xp;
2137 			if (xname == NULL) {
2138 			    __pmNoMem("pmGetChildrenStatus", strlen(name)+1, PM_RECOV_ERR);
2139 			    num = -oserror();
2140 			    goto report;
2141 			}
2142 			while ((xp = rindex(xname, '.')) != NULL) {
2143 			    *xp = '\0';
2144 			    np = locate(xname, curr_pmns->root);
2145 			    if (np != NULL && np->first == NULL &&
2146 				pmid_domain(np->pmid) == DYNAMIC_PMID &&
2147 				pmid_item(np->pmid) == 0) {
2148 				int		domain = ((__pmID_int *)&np->pmid)->cluster;
2149 				__pmDSO		*dp;
2150 				if ((dp = __pmLookupDSO(domain)) == NULL) {
2151 				    num = PM_ERR_NOAGENT;
2152 				    free(xname);
2153 				    goto check;
2154 				}
2155 				if (dp->dispatch.comm.pmda_interface >= PMDA_INTERFACE_5)
2156 				    dp->dispatch.version.four.ext->e_context = ctx;
2157 				if (dp->dispatch.comm.pmda_interface >= PMDA_INTERFACE_4) {
2158 				    char	**x_offspring = NULL;
2159 				    int		*x_statuslist = NULL;
2160 				    int		x_num;
2161 				    x_num = dp->dispatch.version.four.children(
2162 						name, 0, &x_offspring, &x_statuslist,
2163 						dp->dispatch.version.four.ext);
2164 				    if (x_num < 0)
2165 					num = x_num;
2166 				    else
2167 					stitch_list(&num, offspring, statuslist,
2168 						    x_num, x_offspring, x_statuslist);
2169 				    free(xname);
2170 				    goto check;
2171 				}
2172 				else {
2173 				    /* Not PMDA_INTERFACE_4 or later */
2174 				    num = PM_ERR_NAME;
2175 				    free(xname);
2176 				    goto check;
2177 				}
2178 			    }
2179 			}
2180 			free(xname);
2181 		    }
2182 		   num = PM_ERR_NAME;
2183 		   goto check;
2184 		}
2185 	
2186 		if (np != NULL && np->first == NULL) {
2187 		    /*
2188 		     * this is a leaf node ... if it is the root of a dynamic
2189 		     * subtree of the PMNS and we have an existing context
2190 		     * of type PM_CONTEXT_LOCAL than we should chase the
2191 		     * relevant PMDA to provide the details
2192 		     */
2193 		    if (pmid_domain(np->pmid) == DYNAMIC_PMID &&
2194 			pmid_item(np->pmid) == 0) {
2195 			if (ctxp != NULL && ctxp->c_type == PM_CONTEXT_LOCAL) {
2196 			    int		domain = ((__pmID_int *)&np->pmid)->cluster;
2197 			    __pmDSO	*dp;
2198 			    if ((dp = __pmLookupDSO(domain)) == NULL) {
2199 				num = PM_ERR_NOAGENT;
2200 				goto check;
2201 			    }
2202 			    if (dp->dispatch.comm.pmda_interface >= PMDA_INTERFACE_5)
2203 				dp->dispatch.version.four.ext->e_context = ctx;
2204 			    if (dp->dispatch.comm.pmda_interface >= PMDA_INTERFACE_4) {
2205 				char	**x_offspring = NULL;
2206 				int	*x_statuslist = NULL;
2207 				int	x_num;
2208 				x_num = dp->dispatch.version.four.children(name, 0,
2209 						&x_offspring, &x_statuslist,
2210 						dp->dispatch.version.four.ext);
2211 				if (x_num < 0)
2212 				    num = x_num;
2213 				else
2214 				    stitch_list(&num, offspring, statuslist,
2215 						x_num, x_offspring, x_statuslist);
2216 				goto check;
2217 			    }
2218 			    else {
2219 				/* Not PMDA_INTERFACE_4 or later */
2220 				num = PM_ERR_NAME;
2221 				goto check;
2222 			    }
2223 			}
2224 		    }
2225 		    num = 0;
2226 		    goto check;
2227 		}
2228 	
2229 		need = 0;
2230 		num = 0;
2231 	
2232 		if (np != NULL) {
2233 		    for (i = 0, tnp = np->first; tnp != NULL; tnp = tnp->next, i++) {
2234 		        if ((tnp->pmid & MARK_BIT) == 0) {
2235 			    num++;
2236 			    need += sizeof(**offspring) + strlen(tnp->name) + 1;
2237 		        }
2238 		    }
2239 		}
2240 	
2241 		if ((result = (char **)malloc(need)) == NULL) {
2242 		    num = -oserror();
2243 		    goto report;
2244 		}
2245 	
2246 		if (statuslist != NULL) {
2247 		    if ((status = (int *)malloc(num*sizeof(int))) == NULL) {
2248 			num = -oserror();
2249 			goto report;
2250 		    }
2251 		}
2252 	
2253 		p = (char *)&result[num];
2254 	
2255 		if (np != NULL) {
2256 		    for (i = 0, tnp = np->first; tnp != NULL; tnp = tnp->next) {
2257 		        if ((tnp->pmid & MARK_BIT) == 0) {
2258 			    result[i] = p;
2259 			    /*
2260 			     * a name at the root of a dynamic metrics subtree
2261 			     * needs some special handling ... they will have a
2262 			     * "special" PMID, but need the status set to indicate
2263 			     * they are not a leaf node of the PMNS
2264 			     */
2265 			    if (statuslist != NULL) {
2266 				if (pmid_domain(tnp->pmid) == DYNAMIC_PMID &&
2267 				    pmid_item(tnp->pmid) == 0) {
2268 				    status[i] = PMNS_NONLEAF_STATUS;
2269 				}
2270 				else
2271 				  /* node has children? */
2272 				  status[i] = (tnp->first == NULL ? PMNS_LEAF_STATUS : PMNS_NONLEAF_STATUS);
2273 			    }
2274 			    strcpy(result[i], tnp->name);
2275 			    p += strlen(tnp->name) + 1;
2276 			    i++;
2277 		        }
2278 		    }
2279 		}
2280 		else
2281 		    i = 0;
2282 	
2283 		*offspring = result;
2284 		if (statuslist != NULL)
2285 		  *statuslist = status;
2286 	    }
2287 	    else {
2288 		/*
2289 		 * PMNS_REMOTE so there must be a current host context
2290 		 */
2291 		assert(ctxp != NULL && ctxp->c_type == PM_CONTEXT_HOST);
2292 		num = GetChildrenStatusRemote(ctxp, name, offspring, statuslist);
2293 	    }
2294 	
2295 	check:
2296 	    /*
2297 	     * see if there are derived metrics that qualify
2298 	     */
2299 	    dm_num = __dmchildren(name, &dm_offspring, &dm_statuslist);
2300 	#ifdef PCP_DEBUG
2301 	    if (pmDebug & DBG_TRACE_DERIVE) {
2302 		if (num < 0)
2303 		    fprintf(stderr, "pmGetChildren(name=\"%s\") no regular children (%s)", name, pmErrStr(num));
2304 		else
2305 		    fprintf(stderr, "pmGetChildren(name=\"%s\") %d regular children", name, num);
2306 		if (dm_num < 0)
2307 		    fprintf(stderr, ", no derived children (%s)\n", pmErrStr(dm_num));
2308 		else if (dm_num == 0)
2309 		    fprintf(stderr, ", derived leaf\n");
2310 		else
2311 		    fprintf(stderr, ", %d derived children\n", dm_num);
2312 	    }
2313 	#endif
2314 	    if (dm_num > 0) {
2315 		stitch_list(&num, offspring, statuslist, dm_num, dm_offspring, dm_statuslist);
2316 		free(dm_offspring);
2317 		free(dm_statuslist);
2318 	    }
2319 	    else if (dm_num == 0 && num < 0) {
2320 		/* leaf node and derived metric */
2321 		num = 0;
2322 	    }
2323 	
2324 	report:
2325 	#ifdef PCP_DEBUG
2326 	    if (pmDebug & DBG_TRACE_PMNS) {
2327 		fprintf(stderr, "pmGetChildren(name=\"%s\") -> ", name);
2328 		if (num == 0)
2329 		    fprintf(stderr, "leaf\n");
2330 		else if (num > 0) {
2331 		    if (statuslist != NULL)
2332 			__pmDumpNameAndStatusList(stderr, num, *offspring, *statuslist);
2333 		    else
2334 			__pmDumpNameList(stderr, num, *offspring);
2335 		}
2336 		else
2337 		    fprintf(stderr, "%s\n", pmErrStr(num));
2338 	    }
2339 	#endif
2340 	
2341 	    return num;
2342 	}
2343 	
2344 	int
2345 	pmGetChildren(const char *name, char ***offspring)
2346 	{
2347 	    return pmGetChildrenStatus(name, offspring, NULL);
2348 	}
2349 	
2350 	static int
2351 	request_namebypmid(__pmContext *ctxp, pmID pmid)
2352 	{
2353 	    int n;
2354 	
2355 	#ifdef ASYNC_API
2356 	    if (ctxp->c_pmcd->pc_curpdu != 0)
2357 		return PM_ERR_CTXBUSY;
2358 	#endif /*ASYNC_API*/
2359 	
2360 	    n = __pmSendIDList(ctxp->c_pmcd->pc_fd, __pmPtrToHandle(ctxp), 1, &pmid, 0);
2361 	    if (n < 0)
2362 		n = __pmMapErrno(n);
2363 	    return n;
2364 	}
2365 	
2366 	#ifdef ASYNC_API
2367 	int
2368 	pmRequestNameID(int ctxid, pmID pmid)
2369 	{
2370 	    int n;
2371 	    __pmContext *ctxp;
2372 	
2373 	    if ((n = __pmGetHostContextByID(ctxid, &ctxp)) >= 0) {
2374 		if ((n = request_namebypmid(ctxp, pmid)) >= 0) {
2375 		    ctxp->c_pmcd->pc_curpdu = PDU_PMNS_IDS;
2376 		    ctxp->c_pmcd->pc_tout_sec = TIMEOUT_DEFAULT;
2377 		}
2378 	    }
2379 	
2380 	    return n;
2381 	}
2382 	#endif /*ASYNC_API*/
2383 	
2384 	static int
2385 	receive_namesbyid(__pmContext *ctxp, char ***namelist)
2386 	{
2387 	    int         n;
2388 	    __pmPDU      *pb;
2389 	
2390 	    n = __pmGetPDU(ctxp->c_pmcd->pc_fd, ANY_SIZE, 
2391 	                   ctxp->c_pmcd->pc_tout_sec, &pb);
2392 	    
2393 	    if (n == PDU_PMNS_NAMES) {
2394 		int numnames;
2395 	
2396 		n = __pmDecodeNameList(pb, &numnames, namelist, NULL);
2397 		if (n >= 0)
2398 		    n = numnames;
2399 	    }
2400 	    else if (n == PDU_ERROR)
2401 		__pmDecodeError(pb, &n);
2402 	    else if (n != PM_ERR_TIMEOUT)
2403 		n = PM_ERR_IPC;
2404 	
2405 	    return n;
2406 	}
2407 	
2408 	static int 
2409 	receive_a_name(__pmContext *ctxp, char **name)
2410 	{
2411 	    int n;
2412 	    char **namelist;
2413 	
2414 	    if ((n = receive_namesbyid(ctxp, &namelist)) >= 0) {
2415 		char *newname = strdup(namelist[0]);
2416 		free(namelist);
2417 		if (newname == NULL) {
2418 		    n = -oserror();
2419 		} else {
2420 		    *name = newname;
2421 		    n = 0;
2422 		}
2423 	    }
2424 	
2425 	    return n;
2426 	}
2427 	
2428 	#ifdef ASYNC_API
2429 	int
2430 	pmReceiveNameID(int ctxid, char **name)
2431 	{
2432 	    int n;
2433 	    __pmContext *ctxp;
2434 	
2435 	    if ((n = __pmGetBusyHostContextByID(ctxid, &ctxp, PDU_PMNS_IDS)) >= 0) {
2436 		n = receive_a_name(ctxp, name);
2437 	
2438 		ctxp->c_pmcd->pc_curpdu = 0;
2439 		ctxp->c_pmcd->pc_tout_sec = 0;
2440 	    }
2441 	
2442 	    return n;
2443 	}
2444 	
2445 	int
2446 	pmReceiveNamesAll(int ctxid, char ***namelist)
2447 	{
2448 	    int n;
2449 	    __pmContext *ctxp;
2450 	
2451 	    if ((n = __pmGetBusyHostContextByID(ctxid, &ctxp, PDU_PMNS_IDS)) >= 0) {
2452 		n = receive_namesbyid(ctxp, namelist);
2453 	
2454 		ctxp->c_pmcd->pc_curpdu = 0;
2455 		ctxp->c_pmcd->pc_tout_sec = 0;
2456 	    }
2457 	
2458 	    return n;
2459 	}
2460 	#endif /*ASYNC_API*/
2461 	
2462 	int
2463 	pmNameID(pmID pmid, char **name)
2464 	{
2465 	    int pmns_location = GetLocation();
2466 	
2467 	    if (pmns_location < 0)
2468 		return pmns_location;
2469 	
2470 	    else if (pmns_location == PMNS_LOCAL) {
2471 	    	__pmnsNode	*np;
2472 		for (np = curr_pmns->htab[pmid % curr_pmns->htabsize]; np != NULL; np = np->hash) {
2473 		    if (np->pmid == pmid) {
2474 			if (pmid_domain(np->pmid) != DYNAMIC_PMID ||
2475 			    pmid_item(np->pmid) != 0)
2476 			    return backname(np, name);
2477 			return PM_ERR_PMID;
2478 		    }
2479 		}
2480 		/* not found so far, try derived metrics ... */
2481 	    	return __dmgetname(pmid, name);
2482 	    }
2483 	
2484 	    else {
2485 		/* assume PMNS_REMOTE */
2486 		int         n;
2487 		__pmContext  *ctxp;
2488 	
2489 		/* As we have PMNS_REMOTE there must be a current host context */
2490 		n = pmWhichContext();
2491 		assert(n >= 0);
2492 		ctxp = __pmHandleToPtr(n);
2493 	
2494 		if ((n = request_namebypmid(ctxp, pmid)) >= 0) {
2495 		    n = receive_a_name(ctxp, name);
2496 		}
2497 		if (n >= 0) return n;
2498 	    	return __dmgetname(pmid, name);
2499 	    }
2500 	}
2501 	
2502 	int
2503 	pmNameAll(pmID pmid, char ***namelist)
2504 	{
2505 	    int		pmns_location = GetLocation();
2506 	    char	**tmp = NULL;
2507 	    int		n = 0;
2508 	    int		len = 0;
2509 	    char	*sp;
2510 	
2511 	    if (pmns_location < 0)
2512 		return pmns_location;
2513 	
2514 	    else if (pmns_location == PMNS_LOCAL) {
2515 	    	__pmnsNode	*np;
2516 		int		sts;
2517 		int		i;
2518 	
2519 		if (pmid_domain(pmid) == DYNAMIC_PMID && pmid_item(pmid) == 0) {
2520 		    /*
2521 		     * pmid is for the root of a dynamic subtree in the PMNS ...
2522 		     * there is no matching leaf name
2523 		     */
2524 		    return PM_ERR_PMID;
2525 		}
2526 		for (np = curr_pmns->htab[pmid % curr_pmns->htabsize]; np != NULL; np = np->hash) {
2527 		    if (np->pmid == pmid) {
2528 			n++;
2529 			if ((tmp = (char **)realloc(tmp, n * sizeof(tmp[0]))) == NULL)
2530 			    return -oserror();
2531 			if ((sts = backname(np, &tmp[n-1])) < 0) {
2532 			    /* error, ... free any partial allocations */
2533 			    for (i = n-2; i >= 0; i--)
2534 				free(tmp[i]);
2535 			    free(tmp);
2536 			    return sts;
2537 			}
2538 			len += strlen(tmp[n-1])+1;
2539 		    }
2540 		}
2541 	
2542 		if (n == 0)
2543 		    goto try_derive;
2544 	
2545 		len += n * sizeof(tmp[0]);
2546 		if ((tmp = (char **)realloc(tmp, len)) == NULL)
2547 		    return -oserror();
2548 	
2549 		sp = (char *)&tmp[n];
2550 		for (i = 0; i < n; i++) {
2551 		    strcpy(sp, tmp[i]);
2552 		    free(tmp[i]);
2553 		    tmp[i] = sp;
2554 		    sp += strlen(sp)+1;
2555 		}
2556 	
2557 		*namelist = tmp;
2558 		return n;
2559 	    }
2560 	
2561 	    else {
2562 		/* assume PMNS_REMOTE */
2563 		int         n;
2564 		__pmContext  *ctxp;
2565 	
2566 		/* As we have PMNS_REMOTE there must be a current host context */
2567 		n = pmWhichContext();
2568 		assert(n >= 0);
2569 		ctxp = __pmHandleToPtr(n);
2570 	
2571 		if ((n = request_namebypmid (ctxp, pmid)) >= 0) {
2572 		    n = receive_namesbyid (ctxp, namelist);
2573 		}
2574 		if (n == 0)
2575 		    goto try_derive;
2576 		return n;
2577 	    }
2578 	
2579 	try_derive:
2580 	    if ((tmp = (char **)malloc(sizeof(tmp[0]))) == NULL)
2581 		return -oserror();
2582 	    n = __dmgetname(pmid, tmp);
2583 	    if (n < 0) {
2584 		free(tmp);
2585 		return n;
2586 	    }
2587 	    len = sizeof(tmp[0]) + strlen(tmp[0])+1;
2588 	    if ((tmp = (char **)realloc(tmp, len)) == NULL)
2589 		return -oserror();
2590 	    sp = (char *)&tmp[1];
2591 	    strcpy(sp, tmp[0]);
2592 	    free(tmp[0]);
2593 	    tmp[0] = sp;
2594 	    *namelist = tmp;
2595 	    return 1;
2596 	}
2597 	
2598 	
2599 	/*
2600 	 * generic depth-first recursive descent of the PMNS
2601 	 */
2602 	static int
2603 	TraversePMNS_local(const char *name, void(*func)(const char *name))
2604 	{
2605 	    int		sts;
2606 	    char	**enfants;
2607 	
2608 	    if ((sts = pmGetChildren(name, &enfants)) < 0) {
2609 		return sts;
2610 	    }
2611 	    else if (sts > 0) {
2612 		int	j;
2613 		char	*newname;
2614 		int	n;
2615 	
2616 		for (j = 0; j < sts; j++) {
2617 		    newname = (char *)malloc(strlen(name) + 1 + strlen(enfants[j]) + 1);
2618 		    if (newname == NULL) {
2619 			printf("pmTraversePMNS: malloc: %s\n", osstrerror());
2620 			exit(1);
2621 		    }
2622 		    if (*name == '\0')
2623 			strcpy(newname, enfants[j]);
2624 		    else {
2625 			strcpy(newname, name);
2626 			strcat(newname, ".");
2627 			strcat(newname, enfants[j]);
2628 		    }
2629 		    n = TraversePMNS_local(newname, func);
2630 		    free(newname);
2631 		    if (sts == 0)
2632 			sts = n;
2633 		}
2634 		free(enfants);
2635 	    }
2636 	    else if (sts == 0) {
2637 		/* leaf node, name is full name of a metric */
2638 		(*func)(name);
2639 	    }
2640 	
2641 	    return sts;
2642 	}
2643 	
2644 	static int
2645 	request_traverse_pmns(__pmContext *ctxp, const char *name)
2646 	{
2647 	    int n;
2648 	
2649 	#ifdef ASYNC_API
2650 	    if (ctxp->c_pmcd->pc_curpdu != 0)
2651 		return PM_ERR_CTXBUSY;
2652 	#endif /*ASYNC_API*/
2653 	
2654 	    n = __pmSendTraversePMNSReq(ctxp->c_pmcd->pc_fd, __pmPtrToHandle(ctxp),
2655 	    		name);
2656 	    if (n < 0)
2657 		n = __pmMapErrno(n);
2658 	    return n;
2659 	}
2660 	
2661 	#ifdef ASYNC_API
2662 	/*
2663 	 * Note: derived metrics will not work with pmRequestTraversePMNS() and
2664 	 * pmReceiveTraversePMNS() because the by the time the list of names
2665 	 * is received, the original name at the root of the search is no
2666 	 * longer available.
2667 	 *
2668 	 * Probably not an issue as no application or library in the open source
2669 	 * PCP tree uses this pair of routines.
2670 	 */
2671 	
2672 	int
2673 	pmRequestTraversePMNS(int ctx, const char *name)
2674 	{
2675 	    int n;
2676 	    __pmContext *ctxp;
2677 	
2678 	    if ((n = __pmGetHostContextByID(ctx, &ctxp)) >= 0) {
2679 		if ((n = request_traverse_pmns(ctxp, name)) >= 0) {
2680 		    ctxp->c_pmcd->pc_curpdu = PDU_PMNS_TRAVERSE;
2681 		    ctxp->c_pmcd->pc_tout_sec = TIMEOUT_DEFAULT;
2682 		}
2683 	    }
2684 	    return n;
2685 	}
2686 	
2687 	int
2688 	pmReceiveTraversePMNS(int ctxid, void(*func)(const char *name))
2689 	{
2690 	    int n;
2691 	    __pmContext *ctxp;
2692 	    __pmPDU *pb;
2693 	
2694 	    if ((n = __pmGetBusyHostContextByID(ctxid, &ctxp, PDU_PMNS_TRAVERSE)) < 0)
2695 		return n;
2696 	
2697 	    n = __pmGetPDU(ctxp->c_pmcd->pc_fd, ANY_SIZE, 
2698 			   ctxp->c_pmcd->pc_tout_sec, &pb);
2699 	    if (n == PDU_PMNS_NAMES) {
2700 		int numnames;
2701 		int i;
2702 		char **namelist;
2703 	
2704 		n = __pmDecodeNameList(pb, &numnames, &namelist, NULL);
2705 		if (n >= 0) {
2706 		    for (i = 0; i < numnames; i++) {
2707 			func(namelist[i]);
2708 		    }
2709 		
2710 		    free(namelist);
2711 		}
2712 	    }
2713 	    else if (n == PDU_ERROR) {
2714 		__pmDecodeError(pb, &n);
2715 	    }
2716 	    else if (n != PM_ERR_TIMEOUT) {
2717 		n = PM_ERR_IPC;
2718 	    }
2719 	
2720 	    ctxp->c_pmcd->pc_curpdu = 0;
2721 	    ctxp->c_pmcd->pc_tout_sec = 0;
2722 	
2723 	    return n;
2724 	}
2725 	#endif /*ASYNC_API*/
2726 	
2727 	int
2728 	pmTraversePMNS(const char *name, void(*func)(const char *name))
2729 	{
2730 	    int pmns_location = GetLocation();
2731 	
2732 	    if (pmns_location < 0)
2733 		return pmns_location;
2734 	
2735 	    if (name == NULL) 
2736 		return PM_ERR_NAME;
2737 	
2738 	    if (pmns_location == PMNS_LOCAL)
2739 		return TraversePMNS_local(name, func);
2740 	    else { 
2741 		int         sts;
2742 		__pmPDU      *pb;
2743 		__pmContext  *ctxp;
2744 	
2745 		/* As we have PMNS_REMOTE there must be a current host context */
2746 		sts = pmWhichContext();
2747 		assert(sts >= 0);
2748 		ctxp = __pmHandleToPtr(sts);
2749 		if ((sts = request_traverse_pmns (ctxp, name)) < 0) {
2750 		    return sts;
2751 		} else {
2752 		    int		numnames;
2753 		    int		i;
2754 		    int		xtra;
2755 		    char	**namelist;
2756 	
2757 		    sts = __pmGetPDU(ctxp->c_pmcd->pc_fd, ANY_SIZE, 
2758 					TIMEOUT_DEFAULT, &pb);
2759 		    if (sts == PDU_PMNS_NAMES) {
2760 			sts = __pmDecodeNameList(pb, &numnames, 
2761 			                      &namelist, NULL);
2762 			if (sts > 0) {
2763 			    for (i=0; i<numnames; i++) {
2764 				/*
2765 				 * Do not process anonymous metrics here, we'll
2766 				 * pick them up with the derived metrics later on
2767 				 */
2768 				if (strncmp(namelist[i], "anon.", 5) != 0)
2769 				    func(namelist[i]);
2770 			    }
2771 			    numnames = sts;
2772 			    free(namelist);
2773 			}
2774 			else
2775 			    return sts;
2776 		    }
2777 		    else if (sts == PDU_ERROR) {
2778 			__pmDecodeError(pb, &sts);
2779 			if (sts != PM_ERR_NAME)
2780 			    return sts;
2781 			numnames = 0;
2782 		    }
2783 		    else if (sts != PM_ERR_TIMEOUT)
2784 			return PM_ERR_IPC;
2785 	
2786 		    /*
2787 		     * add any derived metrics that have "name" as
2788 		     * their prefix
2789 		     */
2790 		    xtra = __dmtraverse(name, &namelist);
2791 		    if (xtra > 0) {
2792 			sts = 0;
2793 			for (i=0; i<xtra; i++) {
2794 			    func(namelist[i]);
2795 			}
2796 			numnames += xtra;
2797 			free(namelist);
2798 		    }
2799 	
2800 		    return sts > 0 ? numnames : sts;
2801 		}
2802 	    }
2803 	}
2804 	
2805 	int
2806 	pmTrimNameSpace(void)
2807 	{
2808 	    int		i;
2809 	    __pmContext	*ctxp;
2810 	    __pmHashCtl	*hcp;
2811 	    __pmHashNode *hp;
2812 	    int 	version;
2813 	    int		pmns_location = GetLocation();
2814 	
2815 	    if (pmns_location < 0)
2816 		return pmns_location;
2817 	    else if (pmns_location == PMNS_REMOTE)
2818 		return 0;
2819 	
2820 	    /* for PMNS_LOCAL ... */
2821 	
2822 	    if ((ctxp = __pmHandleToPtr(pmWhichContext())) == NULL)
2823 		return PM_ERR_NOCONTEXT;
2824 	
2825 	    if (ctxp->c_type != PM_CONTEXT_ARCHIVE) {
2826 		/* unset all of the marks */
2827 		mark_all(curr_pmns, 0);
2828 		return 0;
2829 	    }
2830 	
2831 	    version = ctxp->c_archctl->ac_log->l_label.ill_magic & 0xff;
2832 	
2833 	    /* Don't do any trimming for the new archives -
2834 	     * they have their own built-in PMNS.
2835 	     * Exception: if an explicit load PMNS call was made.
2836 	     */
2837 	    if (version == PM_LOG_VERS01 || havePmLoadCall) {
2838 		/*
2839 		 * (1) set all of the marks, and
2840 		 * (2) clear the marks for those metrics defined in the archive
2841 		 */
2842 		mark_all(curr_pmns, 1);
2843 		hcp = &ctxp->c_archctl->ac_log->l_hashpmid;
2844 	
2845 		for (i = 0; i < hcp->hsize; i++) {
2846 		    for (hp = hcp->hash[i]; hp != NULL; hp = hp->next) {
2847 			mark_one(curr_pmns, (pmID)hp->key, 0);
2848 		    }
2849 		}
2850 	    }
2851 	
2852 	    return 0;
2853 	}
2854 	
2855 	void
2856 	__pmDumpNameSpace(FILE *f, int verbosity)
2857 	{
2858 	    int pmns_location = GetLocation();
2859 	
2860 	    if (pmns_location < 0)
2861 		fprintf(f, "__pmDumpNameSpace: Unable to determine PMNS location\n");
2862 	    else if (pmns_location == PMNS_REMOTE)
2863 		fprintf(f, "__pmDumpNameSpace: Name Space is remote !\n");
2864 	
2865 	    dumptree(f, 0, curr_pmns->root, verbosity);
2866 	}