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);
1061 symbol = (char *)malloc(symbsize);
1062 if (symbol == NULL) {
1063 __pmNoMem("loadbinary-symbol", symbsize, PM_FATAL_ERR);
1064 /*NOTREACHED*/
1065 }
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);
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) {
|
Event alloc_fn: |
Calling allocation function "malloc". |
|
Event var_assign: |
Assigning: "np" = storage returned from "malloc(sizeof (*np) /*48*/)". |
| Also see events: |
[leaked_storage] |
|
At conditional (1): "(np = (__pmnsNode *)malloc(sizeof (*np) /*48*/)) == NULL": Taking false branch.
|
1306 if ((np = (__pmnsNode *)malloc(sizeof(*np))) == NULL)
1307 return -oserror();
1308 numpmid++;
|
At conditional (2): "(np->name = (char *)malloc(strlen(tokbuf) + 1UL)) == NULL": Taking true branch.
|
1309 if ((np->name = (char *)malloc(strlen(tokbuf)+1)) == NULL)
|
Event leaked_storage: |
Variable "np" going out of scope leaks the storage it points to. |
| Also see events: |
[alloc_fn][var_assign] |
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 }