1 /*
2 * Copyright (c) 1995-2002,2004 Silicon Graphics, Inc. All Rights Reserved.
3 * Copyright (c) 2010 Ken McDonell. All Rights Reserved.
4 *
5 * This library is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU Lesser General Public License as published
7 * by the Free Software Foundation; either version 2.1 of the License, or
8 * (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
13 * License for more details.
14 */
15
16 #include "pmapi.h"
17 #include "impl.h"
18 #include "pmda.h"
19 #include <ctype.h>
20 #include <sys/stat.h>
21
22 static __pmDSO *dsotab;
23 static int numdso = -1;
24
25 static int
26 build_dsotab(void)
27 {
28 /*
29 * parse pmcd's config file extracting details from dso lines
30 *
31 * very little syntactic checking here ... pmcd(1) does that job
32 * nicely and even if we get confused, the worst thing that happens
33 * is we don't include one or more of the DSO PMDAs in dsotab[]
34 *
35 * lines for DSO PMDAs generally look like this ...
36 * Name Domain Type Init Routine Path
37 * mmv 70 dso mmv_init /var/lib/pcp/pmdas/mmv/pmda_mmv.so
38 *
39 */
40 char configFileName[MAXPATHLEN];
41 FILE *configFile;
42 char *config;
43 char *p;
44 char *q;
45 struct stat sbuf;
46 int lineno = 1;
47 int domain;
48 char *init;
49 char *name;
50 char peekc;
51
52 numdso = 0;
53 dsotab = NULL;
54
55 strcpy(configFileName, pmGetConfig("PCP_PMCDCONF_PATH"));
56 #ifdef PCP_DEBUG
57 if (pmDebug & DBG_TRACE_CONTEXT) {
58 fprintf(stderr, "build_dsotab: parsing %s\n", configFileName);
59 }
60 #endif
61 if (stat(configFileName, &sbuf) < 0) {
62 return -oserror();
63 }
64 configFile = fopen(configFileName, "r");
65 if (configFile == NULL) {
66 return -oserror();
67 }
|
Event alloc_fn: |
Calling allocation function "malloc". |
|
Event var_assign: |
Assigning: "config" = storage returned from "malloc(sbuf.st_size + 1L)". |
| Also see events: |
[noescape][leaked_storage] |
|
At conditional (1): "(config = malloc(sbuf.st_size + 1L)) == NULL": Taking false branch.
|
68 if ((config = malloc(sbuf.st_size+1)) == NULL) {
69 __pmNoMem("build_dsotbl:", sbuf.st_size+1, PM_RECOV_ERR);
70 fclose(configFile);
71 return -oserror();
72 }
|
At conditional (2): "fread(config, 1UL, sbuf.st_size, configFile) != sbuf.st_size": Taking true branch.
|
73 if (fread(config, 1, sbuf.st_size, configFile) != sbuf.st_size) {
74 fclose(configFile);
|
Event leaked_storage: |
Variable "config" going out of scope leaks the storage it points to. |
| Also see events: |
[alloc_fn][var_assign][noescape] |
75 return -oserror();
76 }
77 config[sbuf.st_size] = '\0';
78
79 p = config;
80 while (*p != '\0') {
81 /* each time through here we're at the start of a new line */
82 if (*p == '#')
83 goto eatline;
84 if (strncmp(p, "pmcd", 4) == 0) {
85 /*
86 * the pmcd PMDA is an exception ... it makes reference to
87 * symbols in pmcd, and only makes sense when attached to the
88 * pmcd process, so we skip this one
89 */
90 goto eatline;
91 }
92 /* skip the PMDA's name */
93 while (*p != '\0' && *p != '\n' && !isspace(*p))
94 p++;
95 while (*p != '\0' && *p != '\n' && isspace(*p))
96 p++;
97 /* extract domain number */
98 domain = (int)strtol(p, &q, 10);
99 p = q;
100 while (*p != '\0' && *p != '\n' && isspace(*p))
101 p++;
102 /* only interested if the type is "dso" */
103 if (strncmp(p, "dso", 3) != 0)
104 goto eatline;
105 p += 3;
106 while (*p != '\0' && *p != '\n' && isspace(*p))
107 p++;
108 /* up to the init routine name */
109 init = p;
110 while (*p != '\0' && *p != '\n' && !isspace(*p))
111 p++;
112 *p = '\0';
113 p++;
114 while (*p != '\0' && *p != '\n' && isspace(*p))
115 p++;
116 /* up to the dso pathname */
117 name = p;
118 while (*p != '\0' && *p != '\n' && !isspace(*p))
119 p++;
120 peekc = *p;
121 *p = '\0';
122 #ifdef PCP_DEBUG
123 if (pmDebug & DBG_TRACE_CONTEXT) {
124 fprintf(stderr, "[%d] domain=%d, name=%s, init=%s\n", lineno, domain, name, init);
125 }
126 #endif
127 /*
128 * a little be recursive if we got here via __pmLocalPMDA(),
129 * but numdso has been set correctly, so this is OK
130 */
131 __pmLocalPMDA(PM_LOCAL_ADD, domain, name, init);
132 *p = peekc;
133
134 eatline:
135 while (*p != '\0' && *p != '\n')
136 p++;
137 if (*p == '\n') {
138 lineno++;
139 p++;
140 }
141 }
142
143 fclose(configFile);
144 free(config);
145 return 0;
146 }
147
148 #if defined(HAVE_DLFCN_H)
149 #include <dlfcn.h>
150 #endif
151
152 /*
153 * As of PCP version 2.1, we're no longer searching for DSO's;
154 * pmcd's config file should have full paths to each of 'em.
155 */
156 const char *
157 __pmFindPMDA(const char *name)
158 {
159 return (access(name, F_OK) == 0) ? name : NULL;
160 }
161
162 __pmDSO *
163 __pmLookupDSO(int domain)
164 {
165 int i;
166 for (i = 0; i < numdso; i++) {
167 if (dsotab[i].domain == domain && dsotab[i].handle != NULL)
168 return &dsotab[i];
169 }
170 return NULL;
171 }
172
173 #ifdef HAVE_ATEXIT
174 static void
175 EndLocalContext(void)
176 {
177 int i;
178 __pmDSO *dp;
179 int ctx = pmWhichContext();
180
181 for (i = 0; i < numdso; i++) {
182 dp = &dsotab[i];
183 if (dp->domain != -1 &&
184 dp->dispatch.comm.pmda_interface >= PMDA_INTERFACE_5 &&
185 dp->dispatch.version.four.ext->e_endCallBack != NULL) {
186 #ifdef PCP_DEBUG
187 if (pmDebug & DBG_TRACE_CONTEXT) {
188 fprintf(stderr, "NotifyEndLocalContext: DSO PMDA %s (%d) notified of context %d close\n",
189 dp->name, dp->domain, ctx);
190 }
191 #endif
192 (*(dp->dispatch.version.four.ext->e_endCallBack))(ctx);
193 }
194 }
195 }
196 #endif
197
198 int
199 __pmConnectLocal(void)
200 {
201 int i;
202 __pmDSO *dp;
203 char pathbuf[MAXPATHLEN];
204 const char *path;
205 #if defined(HAVE_DLOPEN)
206 unsigned int challenge;
207 void (*initp)(pmdaInterface *);
208 #ifdef HAVE_ATEXIT
209 static int atexit_installed = 0;
210 #endif
211 #endif
212
213 if (numdso == -1) {
214 int sts;
215 sts = build_dsotab();
216 if (sts < 0) return sts;
217 }
218
219 for (i = 0; i < numdso; i++) {
220 dp = &dsotab[i];
221 if (dp->domain == -1 || dp->handle != NULL)
222 continue;
223 /*
224 * __pmLocalPMDA() means the path to the DSO may be something
225 * other than relative to $PCP_PMDAS_DIR ... need to try both
226 * options and also with and without DSO_SUFFIX (so, dll, etc)
227 */
228 snprintf(pathbuf, sizeof(pathbuf), "%s%c%s",
229 pmGetConfig("PCP_PMDAS_DIR"), __pmPathSeparator(), dp->name);
230 if ((path = __pmFindPMDA(pathbuf)) == NULL) {
231 snprintf(pathbuf, sizeof(pathbuf), "%s%c%s.%s",
232 pmGetConfig("PCP_PMDAS_DIR"), __pmPathSeparator(), dp->name, DSO_SUFFIX);
233 if ((path = __pmFindPMDA(pathbuf)) == NULL) {
234 if ((path = __pmFindPMDA(dp->name)) == NULL) {
235 snprintf(pathbuf, sizeof(pathbuf), "%s.%s", dp->name, DSO_SUFFIX);
236 if ((path = __pmFindPMDA(pathbuf)) == NULL) {
237 pmprintf("__pmConnectLocal: Warning: cannot find DSO at \"%s\" or \"%s\"\n",
238 pathbuf, dp->name);
239 pmflush();
240 dp->domain = -1;
241 dp->handle = NULL;
242 continue;
243 }
244 }
245 }
246 }
247 #if defined(HAVE_DLOPEN)
248 dp->handle = dlopen(path, RTLD_NOW);
249 if (dp->handle == NULL) {
250 pmprintf("__pmConnectLocal: Warning: error attaching DSO "
251 "\"%s\"\n%s\n\n", path, dlerror());
252 pmflush();
253 dp->domain = -1;
254 }
255 #else /* ! HAVE_DLOPEN */
256 dp->handle = NULL;
257 pmprintf("__pmConnectLocal: Warning: error attaching DSO \"%s\"\n",
258 path);
259 pmprintf("No dynamic DSO/DLL support on this platform\n\n");
260 pmflush();
261 dp->domain = -1;
262 #endif
263
264 if (dp->handle == NULL)
265 continue;
266
267 #if defined(HAVE_DLOPEN)
268 /*
269 * rest of this only makes sense if the dlopen() worked
270 */
271 if (dp->init == NULL)
272 initp = NULL;
273 else
274 initp = (void (*)(pmdaInterface *))dlsym(dp->handle, dp->init);
275 if (initp == NULL) {
276 pmprintf("__pmConnectLocal: Warning: couldn't find init function "
277 "\"%s\" in DSO \"%s\"\n", dp->init, path);
278 pmflush();
279 dlclose(dp->handle);
280 dp->domain = -1;
281 continue;
282 }
283
284 /*
285 * Pass in the expected domain id.
286 * The PMDA initialization routine can (a) ignore it, (b) check it
287 * is the expected value, or (c) self-adapt.
288 */
289 dp->dispatch.domain = dp->domain;
290
291 /*
292 * the PMDA interface / PMAPI version discovery as a "challenge" ...
293 * for pmda_interface it is all the bits being set,
294 * for pmapi_version it is the complement of the one you are using now
295 */
296 challenge = 0xff;
297 dp->dispatch.comm.pmda_interface = challenge;
298 dp->dispatch.comm.pmapi_version = ~PMAPI_VERSION;
299
300 dp->dispatch.comm.flags = 0;
301 dp->dispatch.status = 0;
302
303 (*initp)(&dp->dispatch);
304
305 if (dp->dispatch.status != 0) {
306 /* initialization failed for some reason */
307 pmprintf("__pmConnectLocal: Warning: initialization "
308 "routine \"%s\" failed in DSO \"%s\": %s\n",
309 dp->init, path, pmErrStr(dp->dispatch.status));
310 pmflush();
311 dlclose(dp->handle);
312 dp->domain = -1;
313 }
314 else {
315 if (dp->dispatch.comm.pmda_interface == challenge) {
316 /*
317 * DSO did not change pmda_interface, assume PMAPI version 1
318 * from PCP 1.x and PMDA_INTERFACE_1
319 */
320 dp->dispatch.comm.pmda_interface = PMDA_INTERFACE_1;
321 dp->dispatch.comm.pmapi_version = PMAPI_VERSION_1;
322 }
323 else {
324 /*
325 * gets a bit tricky ...
326 * interface_version (8-bits) used to be version (4-bits),
327 * so it is possible that only the bottom 4 bits were
328 * changed and in this case the PMAPI version is 1 for
329 * PCP 1.x
330 */
331 if ((dp->dispatch.comm.pmda_interface & 0xf0) == (challenge & 0xf0)) {
332 dp->dispatch.comm.pmda_interface &= 0x0f;
333 dp->dispatch.comm.pmapi_version = PMAPI_VERSION_1;
334 }
335 }
336
337 if (dp->dispatch.comm.pmda_interface < PMDA_INTERFACE_1 ||
338 dp->dispatch.comm.pmda_interface > PMDA_INTERFACE_LATEST) {
339 pmprintf("__pmConnectLocal: Error: Unknown PMDA interface "
340 "version %d in \"%s\" DSO\n",
341 dp->dispatch.comm.pmda_interface, path);
342 pmflush();
343 dlclose(dp->handle);
344 dp->domain = -1;
345 }
346
347 if (dp->dispatch.comm.pmapi_version != PMAPI_VERSION_1 &&
348 dp->dispatch.comm.pmapi_version != PMAPI_VERSION_2) {
349 pmprintf("__pmConnectLocal: Error: Unknown PMAPI version %d "
350 "in \"%s\" DSO\n",
351 dp->dispatch.comm.pmapi_version, path);
352 pmflush();
353 dlclose(dp->handle);
354 dp->domain = -1;
355 }
356 }
357 #ifdef HAVE_ATEXIT
358 if (dp->dispatch.comm.pmda_interface >= PMDA_INTERFACE_5 &&
359 atexit_installed == 0) {
360 /* install end of local context handler */
361 atexit(EndLocalContext);
362 atexit_installed = 1;
363 }
364 #endif
365 #endif /* HAVE_DLOPEN */
366 }
367
368 return 0;
369 }
370
371 int
372 __pmLocalPMDA(int op, int domain, const char *name, const char *init)
373 {
374 int sts = 0;
375 int i;
376
377 #ifdef PCP_DEBUG
378 if (pmDebug & DBG_TRACE_CONTEXT) {
379 fprintf(stderr, "__pmLocalPMDA(op=");
380 if (op == PM_LOCAL_ADD) fprintf(stderr, "ADD");
381 else if (op == PM_LOCAL_DEL) fprintf(stderr, "DEL");
382 else if (op == PM_LOCAL_CLEAR) fprintf(stderr, "CLEAR");
383 else fprintf(stderr, "%d ???", op);
384 fprintf(stderr, ", domain=%d, name=%s, init=%s)\n", domain, name, init);
385 }
386 #endif
387
388 if (numdso == -1) {
389 sts = build_dsotab();
390 if (sts < 0) return sts;
391 }
392
393 switch (op) {
394 case PM_LOCAL_ADD:
395 if ((dsotab = (__pmDSO *)realloc(dsotab, (numdso+1)*sizeof(__pmDSO))) == NULL) {
396 __pmNoMem("__pmLocalPMDA realloc", (numdso+1)*sizeof(__pmDSO), PM_FATAL_ERR);
397 /*NOTREACHED*/
398 }
399 dsotab[numdso].domain = domain;
400 if (name == NULL) {
401 /* odd, will fail later at dlopen */
402 dsotab[numdso].name = NULL;
403 }
404 else {
405 if ((dsotab[numdso].name = strdup(name)) == NULL) {
406 sts = -oserror();
407 __pmNoMem("__pmLocalPMDA name", strlen(name)+1, PM_RECOV_ERR);
408 return sts;
409 }
410 }
411 if (init == NULL) {
412 /* odd, will fail later at initialization call */
413 dsotab[numdso].init = NULL;
414 }
415 else {
416 if ((dsotab[numdso].init = strdup(init)) == NULL) {
417 sts = -oserror();
418 __pmNoMem("__pmLocalPMDA init", strlen(init)+1, PM_RECOV_ERR);
419 return sts;
420 }
421 }
422 dsotab[numdso].handle = NULL;
423 numdso++;
424 break;
425
426 case PM_LOCAL_DEL:
427 sts = PM_ERR_INDOM;
428 for (i = 0; i < numdso; i++) {
429 if ((domain != -1 && dsotab[i].domain == domain) ||
430 (name != NULL && strcmp(dsotab[i].name, name) == 0)) {
431 if (dsotab[i].handle) {
432 dlclose(dsotab[i].handle);
433 dsotab[i].handle = NULL;
434 }
435 dsotab[i].domain = -1;
436 sts = 0;
437 }
438 }
439 break;
440
441 case PM_LOCAL_CLEAR:
442 for (i = 0; i < numdso; i++) {
443 free(dsotab[i].name);
444 free(dsotab[i].init);
445 if (dsotab[i].handle)
446 dlclose(dsotab[i].handle);
447 }
448 free(dsotab);
449 dsotab = NULL;
450 numdso = 0;
451 break;
452
453 default:
454 sts = PM_ERR_CONV;
455 break;
456 }
457
458 #ifdef PCP_DEBUG
459 if (pmDebug & DBG_TRACE_CONTEXT) {
460 if (sts != 0)
461 fprintf(stderr, "__pmLocalPMDA -> %s\n", pmErrStr(sts));
462 fprintf(stderr, "Local Context PMDA Table");
463 if (numdso == 0)
464 fprintf(stderr, " ... empty");
465 fputc('\n', stderr);
466 for (i = 0; i < numdso; i++) {
467 fprintf(stderr, PRINTF_P_PFX "%p [%d] domain=%d name=%s init=%s handle=" PRINTF_P_PFX "%p\n",
468 &dsotab[i], i, dsotab[i].domain, dsotab[i].name, dsotab[i].init, dsotab[i].handle);
469 }
470 }
471 #endif
472
473 return sts;
474 }
475
476 /*
477 * Parse a command line string that encodes arguments to __pmLocalPMDA(),
478 * then call __pmLocalPMDA().
479 *
480 * The syntax for the string is 1 to 4 fields separated by colons:
481 * - op ("add" for add, "del" for delete, "clear" for clear)
482 * - domain (PMDA's PMD)
483 * - path (path to DSO PMDA)
484 * - init (name of DSO's initialization routine)
485 */
486 char *
487 __pmSpecLocalPMDA(const char *spec)
488 {
489 int op;
490 int domain = -1;
491 char *name = NULL;
492 char *init = NULL;
493 int sts;
494 char *arg;
495 char *sbuf;
496 char *ap;
497
498 if ((arg = sbuf = strdup(spec)) == NULL) {
499 sts = -oserror();
500 __pmNoMem("__pmSpecLocalPMDA dup spec", strlen(spec)+1, PM_RECOV_ERR);
501 return "strdup failed";
502 }
503 if (strncmp(arg, "add", 3) == 0) {
504 op = PM_LOCAL_ADD;
505 ap = &arg[3];
506 }
507 else if (strncmp(arg, "del", 3) == 0) {
508 op = PM_LOCAL_DEL;
509 ap = &arg[3];
510 }
511 else if (strncmp(arg, "clear", 5) == 0) {
512 op = PM_LOCAL_CLEAR;
513 ap = &arg[5];
514 if (*ap == '\0')
515 goto doit;
516 else {
517 free(sbuf);
518 return "unexpected text after clear op in spec";
519 }
520 }
521 else {
522 free(sbuf);
523 return "bad op in spec";
524 }
525
526 if (*ap != ',') {
527 free(sbuf);
528 return "expected , after op in spec";
529 }
530 /* ap-> , after add or del */
531 arg = ++ap;
532 if (*ap == '\0') {
533 free(sbuf);
534 return "missing domain in spec";
535 }
536 else if (*ap != ',') {
537 /* ap-> domain */
538 domain = (int)strtol(arg, &ap, 10);
539 if ((*ap != ',' && *ap != '\0') || domain < 0 || domain > 510) {
540 free(sbuf);
541 return "bad domain in spec";
542 }
543 }
544 else {
545 if (op != PM_LOCAL_DEL) {
546 /* found ,, where ,domain, expected */
547 free(sbuf);
548 return "missing domain in spec";
549 }
550 }
551 ap++;
552 /* ap -> char after , following domain */
553 if (*ap == ',') {
554 /* no path, could have init (not useful but possible!) */
555 ap++;
556 if (*ap != '\0')
557 init = ap;
558 }
559 else if (*ap != '\0') {
560 /* have path and possibly init */
561 name = ap;
562 while (*ap != ',' && *ap != '\0')
563 ap++;
564 if (*ap == ',') {
565 *ap++ = '\0';
566 if (*ap != '\0')
567 init = ap;
568 else {
569 if (op != PM_LOCAL_DEL) {
570 /* found end of string where init-routine expected */
571 free(sbuf);
572 return "missing init-routine in spec";
573 }
574 }
575 }
576 else {
577 if (op != PM_LOCAL_DEL) {
578 /* found end of string where init-routine expected */
579 free(sbuf);
580 return "missing init-routine in spec";
581 }
582 }
583 }
584 else {
585 if (op != PM_LOCAL_DEL) {
586 /* found end of string where path expected */
587 free(sbuf);
588 return "missing dso-path in spec";
589 }
590 }
591
592 if (domain == -1 && name == NULL) {
593 free(sbuf);
594 return "missing domain and dso-path in spec";
595 }
596
597 doit:
598 sts = __pmLocalPMDA(op, domain, name, init);
599 if (sts < 0) {
600 static char buffer[256];
601 snprintf(buffer, sizeof(buffer), "__pmLocalPMDA: %s", pmErrStr(sts));
602 free(sbuf);
603 return buffer;
604 }
605
606 free(sbuf);
607 return NULL;
608 }