1 /*
2 * General Utility Routines
3 *
4 * Copyright (c) 1995-2002,2004 Silicon Graphics, Inc. All Rights Reserved.
5 * Copyright (c) 2009 Aconex. All Rights Reserved.
6 *
7 * This library is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU Lesser General Public License as published
9 * by the Free Software Foundation; either version 2.1 of the License, or
10 * (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
14 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
15 * License for more details.
16 */
17
18 #include <stdarg.h>
19 #include <sys/stat.h>
20 #include <inttypes.h>
21 #include <limits.h>
22 #include <ctype.h>
23 #include <math.h>
24
25 #include "pmapi.h"
26 #include "impl.h"
27 #include "pmdbg.h"
28
29 #if defined(HAVE_SYS_TIMES_H)
30 #include <sys/times.h>
31 #endif
32 #if defined(HAVE_SYS_MMAN_H)
33 #include <sys/mman.h>
34 #endif
35 #if defined(HAVE_IEEEFP_H)
36 #include <ieeefp.h>
37 #endif
38
39 static FILE **filelog;
40 static int nfilelog;
41 static int dosyslog;
42 static int pmState = PM_STATE_APPL;
43 static int done_exit;
44
45 INTERN char *pmProgname = "pcp"; /* the real McCoy */
46
47 static int vpmprintf(const char *, va_list);
48
49 /*
50 * if onoff == 1, logging is to syslog and stderr, else logging is
51 * just to stderr (this is the default)
52 */
53 void
54 __pmSyslog(int onoff)
55 {
56 dosyslog = onoff;
57 if (dosyslog)
58 openlog("pcp", LOG_PID, LOG_DAEMON);
59 else
60 closelog();
61 }
62
63 /*
64 * This is a wrapper around syslog(3C) that writes similar messages to stderr,
65 * but if __pmSyslog(1) is called, the messages will really go to syslog
66 */
67 void
68 __pmNotifyErr(int priority, const char *message, ...)
69 {
70 va_list arg;
71 char *p;
72 char *level;
73 time_t now;
74
75 va_start(arg, message);
76
77 time(&now);
78
79 if (dosyslog) {
80 char syslogmsg[2048];
81
82 snprintf(syslogmsg, sizeof(syslogmsg), message, arg);
83 syslog(priority, "%s", syslogmsg);
84 }
85
86 /*
87 * do the stderr equivalent
88 */
89
90 switch (priority) {
91 case LOG_EMERG :
92 level = "Emergency";
93 break;
94 case LOG_ALERT :
95 level = "Alert";
96 break;
97 case LOG_CRIT :
98 level = "Critical";
99 break;
100 case LOG_ERR :
101 level = "Error";
102 break;
103 case LOG_WARNING :
104 level = "Warning";
105 break;
106 case LOG_NOTICE :
107 level = "Notice";
108 break;
109 case LOG_INFO :
110 level = "Info";
111 break;
112 case LOG_DEBUG :
113 level = "Debug";
114 break;
115 default:
116 level = "???";
117 break;
118 }
119
120 pmprintf("[%.19s] %s(%" FMT_PID ") %s: ", ctime(&now), pmProgname, getpid(), level);
121 vpmprintf(message, arg);
122 va_end(arg);
123 /* trailing \n if needed */
124 for (p = (char *)message; *p; p++)
125 ;
126 if (p == message || p[-1] != '\n')
127 pmprintf("\n");
128 pmflush();
129 }
130
131 static void
132 logheader(const char *progname, FILE *log, const char *act)
133 {
134 time_t now;
135 char host[MAXHOSTNAMELEN];
136
137 setlinebuf(log); /* line buffering for log files */
138 gethostname(host, MAXHOSTNAMELEN);
139 host[MAXHOSTNAMELEN-1] = '\0';
140 time(&now);
141 fprintf(log, "Log for %s on %s %s %s\n", progname, host, act, ctime(&now));
142 }
143
144 static void
145 logfooter(FILE *log, const char *act)
146 {
147 time_t now;
148
149 time(&now);
150 fprintf(log, "\nLog %s %s", act, ctime(&now));
151 }
152
153 static void
154 logonexit(void)
155 {
156 int i;
157
158 /*
159 * there is a race condition here ... but the worse that can happen
160 * is (a) no "Log finished" message, or (b) _two_ "Log finished"
161 * messages ... neither case is serious enough to warrant a mutex guard
162 */
163 if (++done_exit != 1)
164 return;
165
166 for (i = 0; i < nfilelog; i++)
167 logfooter(filelog[i], "finished");
168 }
169
170 /* common code shared by __pmRotateLog and __pmOpenLog */
171 static FILE *
172 logreopen(const char *progname, const char *logname, FILE *oldstream,
173 int *status)
174 {
175 int oldfd;
176 int dupoldfd;
177 FILE *dupoldstream = oldstream;
178
179 /*
180 * Do our own version of freopen() because the standard one closes the
181 * original stream BEFORE trying to open the new one. Once it's gone,
182 * there's no way to get the closed stream back if the open fails.
183 */
184
185 fflush(oldstream);
186 oldfd = fileno(oldstream);
|
Event negative_return_fn: |
Function "dup(oldfd)" returns a negative number. |
|
Event var_assign: |
Assigning: signed variable "dupoldfd" = "dup". |
| Also see events: |
[negative_returns] |
187 dupoldfd = dup(oldfd);
188
189 /*
190 * try to remove the file first ... don't bother if this fails,
191 * but if it succeeds, we at least get a chance to define the
192 * owner and mode, rather than inheriting this from an existing
193 * writeable file ... really only a problem when called as with
194 * uid == 0, e.g. from pmcd(1).
195 */
196 unlink(logname);
197
198 oldstream = freopen(logname, "w", oldstream);
|
At conditional (1): "oldstream == NULL": Taking false branch.
|
199 if (oldstream == NULL) {
200 int save_error = oserror(); /* need for error message */
201
202 close(oldfd);
203 if (dup(dupoldfd) != oldfd)
204 /* fd juggling failed! */
205 oldstream = NULL;
206 else
207 /* oldfd now re-instated as at entry */
208 oldstream = fdopen(oldfd, "w");
209 if (oldstream == NULL) {
210 /* serious trouble ... choose least obnoxious alternative */
211 if (dupoldstream == stderr)
212 oldstream = fdopen(fileno(stdout), "w");
213 else
214 oldstream = fdopen(fileno(stderr), "w");
215 }
216 *status = 0;
217 pmprintf("%s: cannot open log \"%s\" for writing : %s\n",
218 progname, logname, strerror(save_error));
219 pmflush();
220 }
221 else {
222 *status = 1;
223 }
224 close(dupoldfd);
225 return oldstream;
226 }
227
228 FILE *
229 __pmOpenLog(const char *progname, const char *logname, FILE *oldstream,
230 int *status)
231 {
232 oldstream = logreopen(progname, logname, oldstream, status);
233 logheader(progname, oldstream, "started");
234
235 nfilelog++;
236 if (nfilelog == 1)
237 atexit(logonexit);
238
239 filelog = (FILE **)realloc(filelog, nfilelog * sizeof(FILE *));
240 if (filelog == NULL) {
241 __pmNoMem("__pmOpenLog", nfilelog * sizeof(FILE *), PM_FATAL_ERR);
242 }
243 filelog[nfilelog-1] = oldstream;
244 return oldstream;
245 }
246
247 FILE *
248 __pmRotateLog(const char *progname, const char *logname, FILE *oldstream,
249 int *status)
250 {
251 int i;
252
253 for (i = 0; i < nfilelog; i++) {
254 if (oldstream == filelog[i]) {
255 logfooter(oldstream, "rotated"); /* old */
256 oldstream = logreopen(progname, logname, oldstream, status);
257 logheader(progname, oldstream, "rotated"); /* new */
258 filelog[i] = oldstream;
259 break;
260 }
261 }
262 return oldstream;
263 }
264
265 const char *
266 pmIDStr(pmID pmid)
267 {
268 static char pbuf[20];
269 __pmID_int* p = (__pmID_int*)&pmid;
270 if (pmid == PM_ID_NULL)
271 return "PM_ID_NULL";
272 if (p->domain == DYNAMIC_PMID && p->item == 0)
273 /*
274 * this PMID represents the base of a dynamic subtree in the PMNS
275 * ... identified by setting the domain field to the reserved
276 * value DYNAMIC_PMID and storing the real domain of the PMDA
277 * that can enumerate the subtree in the cluster field, while
278 * the item field is not used
279 */
280 snprintf(pbuf, sizeof(pbuf), "%d.*.*", p->cluster);
281 else
282 snprintf(pbuf, sizeof(pbuf), "%d.%d.%d", p->domain, p->cluster, p->item);
283 return pbuf;
284 }
285
286 const char *
287 pmInDomStr(pmInDom indom)
288 {
289 static char pbuf[20];
290 __pmInDom_int* p = (__pmInDom_int*)&indom;
291 if (indom == PM_INDOM_NULL)
292 return "PM_INDOM_NULL";
293 snprintf(pbuf, sizeof(pbuf), "%d.%d", p->domain, p->serial);
294 return pbuf;
295 }
296
297 const char *
298 pmNumberStr(double value)
299 {
300 static char buf[8];
301
302 if (value >= 0.0) {
303 if (value >= 999995000000000.0)
304 strncpy(buf, " inf? ", sizeof(buf));
305 else if (value >= 999995000000.0)
306 snprintf(buf, sizeof(buf), "%6.2fT", value / 1000000000000.0);
307 else if (value >= 999995000.0)
308 snprintf(buf, sizeof(buf), "%6.2fG", value / 1000000000.0);
309 else if (value >= 999995.0)
310 snprintf(buf, sizeof(buf), "%6.2fM", value / 1000000.0);
311 else if (value >= 999.995)
312 snprintf(buf, sizeof(buf), "%6.2fK", value / 1000.0);
313 else if (value >= 0.005)
314 snprintf(buf, sizeof(buf), "%6.2f ", value);
315 else
316 snprintf(buf, sizeof(buf), "%6.2f ", 0.0);
317 }
318 else {
319 if (value <= -99995000000000.0)
320 strncpy(buf, "-inf? ", sizeof(buf));
321 else if (value <= -99995000000.0)
322 snprintf(buf, sizeof(buf), "%6.2fT", value / 1000000000000.0);
323 else if (value <= -99995000.0)
324 snprintf(buf, sizeof(buf), "%6.2fG", value / 1000000000.0);
325 else if (value <= -99995.0)
326 snprintf(buf, sizeof(buf), "%6.2fM", value / 1000000.0);
327 else if (value <= -99.995)
328 snprintf(buf, sizeof(buf), "%6.2fK", value / 1000.0);
329 else if (value <= -0.005)
330 snprintf(buf, sizeof(buf), "%6.2f ", value);
331 else
332 snprintf(buf, sizeof(buf), "%6.2f ", 0.0);
333 }
334 return buf;
335 }
336
337 const char *
338 pmEventFlagsStr(int flags)
339 {
340 /*
341 * buffer needs to be long enough to hold each flag name
342 * (excluding missed) plus the separation commas, so
343 * point,start,end,id,parent (even though it is unlikely that
344 * both start and end would be set for the one event record)
345 */
346 static char buffer[64];
347 int started = 0;
348
349 if (flags & PM_EVENT_FLAG_MISSED)
350 return strcpy(buffer, "missed");
351
352 buffer[0] = '\0';
353 if (flags & PM_EVENT_FLAG_POINT) {
354 if (started++) strcat(buffer, ",");
355 strcat(buffer, "point");
356 }
357 if (flags & PM_EVENT_FLAG_START) {
358 if (started++) strcat(buffer, ",");
359 strcat(buffer, "start");
360 }
361 if (flags & PM_EVENT_FLAG_END) {
362 if (started++) strcat(buffer, ",");
363 strcat(buffer, "end");
364 }
365 if (flags & PM_EVENT_FLAG_ID) {
366 if (started++) strcat(buffer, ",");
367 strcat(buffer, "id");
368 }
369 if (flags & PM_EVENT_FLAG_PARENT) {
370 if (started++) strcat(buffer, ",");
371 strcat(buffer, "parent");
372 }
373 return buffer;
374 }
375
376 void
377 __pmDumpResult(FILE *f, const pmResult *resp)
378 {
379 int i;
380 int j;
381 int n;
382 int saveDebug;
383 char *p;
384 pmDesc desc;
385 int have_desc;
386
387 /* tracing PDUs really messes this up when pmNameInDom is called below */
388 saveDebug = pmDebug;
389 pmDebug = 0;
390
391 fprintf(f,"pmResult dump from " PRINTF_P_PFX "%p timestamp: %d.%06d ",
392 resp, (int)resp->timestamp.tv_sec, (int)resp->timestamp.tv_usec);
393 __pmPrintStamp(f, &resp->timestamp);
394 fprintf(f, " numpmid: %d\n", resp->numpmid);
395 for (i = 0; i < resp->numpmid; i++) {
396 pmValueSet *vsp = resp->vset[i];
397 n = pmNameID(vsp->pmid, &p);
398 if (n < 0)
399 fprintf(f," %s (%s):", pmIDStr(vsp->pmid), "<noname>");
400 else {
401 fprintf(f," %s (%s):", pmIDStr(vsp->pmid), p);
402 free(p);
403 }
404 if (vsp->numval == 0) {
405 fprintf(f, " No values returned!\n");
406 continue;
407 }
408 else if (vsp->numval < 0) {
409 fprintf(f, " %s\n", pmErrStr(vsp->numval));
410 continue;
411 }
412 if (__pmGetInternalState() == PM_STATE_PMCS || pmLookupDesc(vsp->pmid, &desc) < 0) {
413 /* don't know, so punt on the most common cases */
414 desc.indom = PM_INDOM_NULL;
415 have_desc = 0;
416 }
417 else
418 have_desc = 1;
419 fprintf(f, " numval: %d", vsp->numval);
420 fprintf(f, " valfmt: %d vlist[]:\n", vsp->valfmt);
421 for (j = 0; j < vsp->numval; j++) {
422 pmValue *vp = &vsp->vlist[j];
423 if (vsp->numval > 1 || vp->inst != PM_INDOM_NULL) {
424 fprintf(f," inst [%d", vp->inst);
425 if (have_desc &&
426 pmNameInDom(desc.indom, vp->inst, &p) >= 0) {
427 fprintf(f, " or \"%s\"]", p);
428 free(p);
429 }
430 else {
431 fprintf(f, " or ???]");
432 }
433 fputc(' ', f);
434 }
435 else
436 fprintf(f, " ");
437 fprintf(f, "value ");
438 if (have_desc)
439 pmPrintValue(f, vsp->valfmt, desc.type, vp, 1);
440 else {
441 if (vsp->valfmt == PM_VAL_INSITU)
442 pmPrintValue(f, vsp->valfmt, PM_TYPE_UNKNOWN, vp, 1);
443 else
444 pmPrintValue(f, vsp->valfmt, (int)vp->value.pval->vtype, vp, 1);
445 }
446 fputc('\n', f);
447 }
448 }
449 pmDebug = saveDebug;
450 }
451
452 static void
453 print_event_summary(FILE *f, const pmValue *val)
454 {
455 pmEventArray *eap = (pmEventArray *)val->value.pval;
456 char *base;
457 struct timeval stamp;
458 __pmTimeval *tvp;
459 int nrecords;
460 int nmissed = 0;
461 int r; /* records */
462 int p; /* parameters in a record ... */
463 pmEventRecord *erp;
464 pmEventParameter *epp;
465
466 nrecords = eap->ea_nrecords;
467 base = (char *)&eap->ea_record[0];
468 tvp = (__pmTimeval *)base;
469 stamp.tv_sec = tvp->tv_sec;
470 stamp.tv_usec = tvp->tv_usec;
471 /* walk packed event record array */
472 for (r = 0; r < eap->ea_nrecords-1; r++) {
473 erp = (pmEventRecord *)base;
474 base += sizeof(erp->er_timestamp) + sizeof(erp->er_flags) + sizeof(erp->er_nparams);
475 if (erp->er_flags & PM_EVENT_FLAG_MISSED) {
476 nmissed += erp->er_nparams;
477 continue;
478 }
479 for (p = 0; p < erp->er_nparams; p++) {
480 epp = (pmEventParameter *)base;
481 base += sizeof(epp->ep_pmid) + PM_PDU_SIZE_BYTES(epp->ep_len);
482 }
483 }
484 fprintf(f, "[%d event record", nrecords);
485 if (nrecords != 1)
486 fputc('s', f);
487 if (nmissed > 0)
488 fprintf(f, " (%d missed)", nmissed);
489 if (nrecords > 0) {
490 fprintf(f, " timestamp");
491 if (nrecords > 1)
492 fputc('s', f);
493 fputc(' ', f);
494 __pmPrintStamp(f, &stamp);
495 if (eap->ea_nrecords > 1) {
496 fprintf(f, "...");
497 tvp = (__pmTimeval *)base;
498 stamp.tv_sec = tvp->tv_sec;
499 stamp.tv_usec = tvp->tv_usec;
500 __pmPrintStamp(f, &stamp);
501 }
502 }
503 fputc(']', f);
504 }
505
506 /* Print single pmValue. */
507 void
508 pmPrintValue(FILE *f, /* output stream */
509 int valfmt, /* from pmValueSet */
510 int type, /* from pmDesc */
511 const pmValue *val, /* value to print */
512 int minwidth) /* output is at least this wide */
513 {
514 pmAtomValue a;
515 int i;
516 int n;
517 char *p;
518 int sts;
519
520 if (type != PM_TYPE_UNKNOWN && type != PM_TYPE_EVENT) {
521 sts = pmExtractValue(valfmt, val, type, &a, type);
522 if (sts < 0)
523 type = PM_TYPE_UNKNOWN;
524 }
525
526 switch (type) {
527 case PM_TYPE_32:
528 fprintf(f, "%*i", minwidth, a.l);
529 break;
530
531 case PM_TYPE_U32:
532 fprintf(f, "%*u", minwidth, a.ul);
533 break;
534
535 case PM_TYPE_64:
536 fprintf(f, "%*"PRIi64, minwidth, a.ll);
537 break;
538
539 case PM_TYPE_U64:
540 fprintf(f, "%*"PRIu64, minwidth, a.ull);
541 break;
542
543 case PM_TYPE_FLOAT:
544 fprintf(f, "%*.8g", minwidth, (double)a.f);
545 break;
546
547 case PM_TYPE_DOUBLE:
548 fprintf(f, "%*.16g", minwidth, a.d);
549 break;
550
551 case PM_TYPE_STRING:
552 n = (int)strlen(a.cp) + 2;
553 while (n < minwidth) {
554 fputc(' ', f);
555 n++;
556 }
557 fprintf(f, "\"%s\"", a.cp);
558 free(a.cp);
559 break;
560
561 case PM_TYPE_AGGREGATE:
562 case PM_TYPE_UNKNOWN:
563 if (valfmt == PM_VAL_INSITU) {
564 float *fp = (float *)&val->value.lval;
565 __uint32_t *ip = (__uint32_t *)&val->value.lval;
566 fprintf(f, "%*u", minwidth, *ip);
567 #ifdef HAVE_ISNANF
568 if (!isnanf(*fp))
569 #endif
570 fprintf(f, " %*.8g", minwidth, (double)*fp);
571 if (minwidth > 2)
572 minwidth -= 2;
573 fprintf(f, " 0x%*x", minwidth, val->value.lval);
574 }
575 else {
576 int string;
577 int done = 0;
578 if (val->value.pval->vlen == PM_VAL_HDR_SIZE + sizeof(__uint64_t)) {
579 __uint64_t i;
580 memcpy((void *)&i, (void *)&val->value.pval->vbuf, sizeof(__uint64_t));
581 fprintf(f, "%*"PRIu64, minwidth, i);
582 done = 1;
583 }
584 if (val->value.pval->vlen == PM_VAL_HDR_SIZE + sizeof(double)) {
585 double d;
586 memcpy((void *)&d, (void *)&val->value.pval->vbuf, sizeof(double));
587 if (!isnand(d)) {
588 if (done) fputc(' ', f);
589 fprintf(f, "%*.16g", minwidth, d);
590 done = 1;
591 }
592 }
593 if (val->value.pval->vlen == PM_VAL_HDR_SIZE + sizeof(float)) {
594 float *fp = (float *)&val->value.pval->vbuf;
595 #ifdef HAVE_ISNANF
596 if (!isnanf(*fp)) {
597 #endif
598 if (done) fputc(' ', f);
599 fprintf(f, "%*.8g", minwidth, (double)*fp);
600 done = 1;
601 #ifdef HAVE_ISNANF
602 }
603 #endif
604 }
605 if (val->value.pval->vlen < PM_VAL_HDR_SIZE)
606 fprintf(f, "pmPrintValue: negative length (%d) for aggregate value?",
607 (int)val->value.pval->vlen - PM_VAL_HDR_SIZE);
608 else {
609 string = 1;
610 for (n = 0; n < val->value.pval->vlen - PM_VAL_HDR_SIZE; n++) {
611 if (!isprint((int)val->value.pval->vbuf[n])) {
612 string = 0;
613 break;
614 }
615 }
616 if (string) {
617 if (done) fputc(' ', f);
618 n = (int)val->value.pval->vlen - PM_VAL_HDR_SIZE + 2;
619 while (n < minwidth) {
620 fputc(' ', f);
621 n++;
622 }
623 n = (int)val->value.pval->vlen - PM_VAL_HDR_SIZE;
624 fprintf(f, "\"%*.*s\"", n, n, val->value.pval->vbuf);
625 done = 1;
626 }
627 n = 2 * (val->value.pval->vlen - PM_VAL_HDR_SIZE) + 2;
628 while (n < minwidth) {
629 fputc(' ', f);
630 n++;
631 }
632 if (done) fputc(' ', f);
633 fputc('[', f);
634 p = &val->value.pval->vbuf[0];
635 for (i = 0; i < val->value.pval->vlen - PM_VAL_HDR_SIZE; i++) {
636 fprintf(f, "%02x", *p & 0xff);
637 p++;
638 }
639 fputc(']', f);
640 }
641 }
642 if (type != PM_TYPE_UNKNOWN)
643 free(a.vbp);
644 break;
645
646 case PM_TYPE_EVENT: /* not much we can do about minwidth */
647 print_event_summary(f, val);
648 break;
649
650 case PM_TYPE_NOSUPPORT:
651 fprintf(f, "pmPrintValue: bogus value, metric Not Supported\n");
652 break;
653
654 default:
655 fprintf(f, "pmPrintValue: unknown value type=%d\n", type);
656 }
657 }
658
659 void
660 __pmNoMem(const char *where, size_t size, int fatal)
661 {
662 __pmNotifyErr(fatal ? LOG_ERR : LOG_WARNING,
663 "%s: malloc(%d) failed: %s",
664 where, (int)size, osstrerror());
665 if (fatal)
666 exit(1);
667 }
668
669 /*
670 * this one is used just below the PMAPI to convert platform errors
671 * into more appropriate PMAPI error codes
672 */
673 int
674 __pmMapErrno(int sts)
675 {
676 if (sts == -EBADF || sts == -EPIPE)
677 sts = PM_ERR_IPC;
678 #ifdef IS_MINGW
679 else if (sts == -EINVAL)
680 sts = PM_ERR_IPC;
681 #endif
682 return sts;
683 }
684
685 /*
686 * difference for two on the internal timestamps
687 */
688 double
689 __pmTimevalSub(const __pmTimeval *ap, const __pmTimeval *bp)
690 {
691 return ap->tv_sec - bp->tv_sec + (double)(ap->tv_usec - bp->tv_usec)/1000000.0;
692 }
693
694 /*
695 * timestamp, e.g. from a log in HH:MM:SS.XXX format
696 */
697 void
698 __pmPrintStamp(FILE *f, const struct timeval *tp)
699 {
700 static struct tm tmp;
701 time_t now;
702
703 now = (time_t)tp->tv_sec;
704 pmLocaltime(&now, &tmp);
705 fprintf(f, "%02d:%02d:%02d.%03d", tmp.tm_hour, tmp.tm_min, tmp.tm_sec, (int)(tp->tv_usec/1000));
706 }
707
708 /*
709 * descriptor
710 */
711 void
712 __pmPrintDesc(FILE *f, const pmDesc *desc)
713 {
714 char *type;
715 char *sem;
716 static char *unknownVal = "???";
717 const char *units;
718
719 if (desc->type == PM_TYPE_NOSUPPORT) {
720 fprintf(f, " Data Type: Not Supported\n");
721 return;
722 }
723
724 switch (desc->type) {
725 case PM_TYPE_32:
726 type = "32-bit int";
727 break;
728 case PM_TYPE_U32:
729 type = "32-bit unsigned int";
730 break;
731 case PM_TYPE_64:
732 type = "64-bit int";
733 break;
734 case PM_TYPE_U64:
735 type = "64-bit unsigned int";
736 break;
737 case PM_TYPE_FLOAT:
738 type = "float";
739 break;
740 case PM_TYPE_DOUBLE:
741 type = "double";
742 break;
743 case PM_TYPE_STRING:
744 type = "string";
745 break;
746 case PM_TYPE_AGGREGATE:
747 type = "aggregate";
748 break;
749 case PM_TYPE_AGGREGATE_STATIC:
750 type = "static aggregate";
751 break;
752 case PM_TYPE_EVENT:
753 type = "event record array";
754 break;
755 default:
756 type = unknownVal;
757 break;
758 }
759 fprintf(f, " Data Type: %s", type);
760 if (type == unknownVal)
761 fprintf(f, " (%d)", desc->type);
762
763 fprintf(f," InDom: %s 0x%x\n", pmInDomStr(desc->indom), desc->indom);
764
765 switch (desc->sem) {
766 case PM_SEM_COUNTER:
767 sem = "counter";
768 break;
769 case PM_SEM_INSTANT:
770 sem = "instant";
771 break;
772 case PM_SEM_DISCRETE:
773 sem = "discrete";
774 break;
775 default:
776 sem = unknownVal;
777 break;
778 }
779
780 fprintf(f, " Semantics: %s", sem);
781 if (sem == unknownVal)
782 fprintf(f, " (%d)", desc->sem);
783
784 fprintf(f, " Units: ");
785 units = pmUnitsStr(&desc->units);
786 if (*units == '\0')
787 fprintf(f, "none\n");
788 else
789 fprintf(f, "%s\n", units);
790 }
791
792 /*
793 * print times between events
794 */
795 void
796 __pmEventTrace(const char *event)
797 {
798 #ifdef PCP_DEBUG
799 static double last = 0;
800 static double sum = 0;
801 static int first = 1;
802 struct timeval tv;
803 double now;
804
805 __pmtimevalNow(&tv);
806 now = (double)tv.tv_sec + (double)tv.tv_usec / 1000000.0;
807 if (!first)
808 sum += now - last;
809 fprintf(stderr, "%s: +%4.2f = %4.2f -> %s\n",
810 pmProgname, first ? 0 : (now-last), sum, event);
811 last = now;
812 first = 0;
813 #endif
814 }
815
816 int
817 __pmParseDebug(const char *spec)
818 {
819 #ifdef PCP_DEBUG
820 int val = 0;
821 int tmp;
822 const char *p;
823 char *pend;
824 int i;
825
826 for (p = spec; *p; ) {
827 tmp = (int)strtol(p, &pend, 10);
828 if (tmp == -1)
829 /* special case ... -1 really means set all the bits! */
830 tmp = INT_MAX;
831 if (*pend == '\0') {
832 val |= tmp;
833 break;
834 }
835 else if (*pend == ',') {
836 val |= tmp;
837 p = pend + 1;
838 }
839 else {
840 pend = strchr(p, ',');
841 if (pend != NULL)
842 *pend = '\0';
843
844 if (strcasecmp(p, "ALL") == 0) {
845 val |= INT_MAX;
846 if (pend != NULL) {
847 *pend = ',';
848 p = pend + 1;
849 }
850 else
851 p = ""; /* force termination of outer loop */
852 break;
853 }
854
855 for (i = 0; i < num_debug; i++) {
856 if (strcasecmp(p, debug_map[i].name) == 0) {
857 val |= debug_map[i].bit;
858 if (pend != NULL) {
859 *pend = ',';
860 p = pend + 1;
861 }
862 else
863 p = ""; /* force termination of outer loop */
864 break;
865 }
866 }
867
868 if (i == num_debug) {
869 if (pend != NULL)
870 *pend = ',';
871 return PM_ERR_CONV;
872 }
873 }
874 }
875
876 return val;
877 #else
878 return PM_ERR_NYI;
879 #endif
880 }
881
882 int
883 __pmGetInternalState(void)
884 {
885 return pmState;
886 }
887
888 void
889 __pmSetInternalState(int state)
890 {
891 pmState = state;
892 }
893
894
895 /*
896 * GUI output option
897 */
898
899 #define MSGBUFLEN 256
900 static FILE *fptr = NULL;
901 static char outbuf[MSGBUFLEN];
902 static int msgsize = 0;
903 static char *fname; /* temporary file name for buffering errors */
904 static char *ferr; /* error output filename from PCP_STDERR */
905
906 #define PM_QUERYERR -1
907 #define PM_USEDIALOG 0
908 #define PM_USESTDERR 1
909 #define PM_USEFILE 2
910
911 static int
912 pmfstate(int state)
913 {
914 static int errtype = -1;
915
916 if (state > PM_QUERYERR)
917 errtype = state;
918
919 if (errtype == PM_QUERYERR) {
920 errtype = PM_USESTDERR;
921 if ((ferr = getenv("PCP_STDERR")) != NULL) {
922 if (strcasecmp(ferr, "DISPLAY") == 0) {
923 char * xconfirm = pmGetConfig("PCP_XCONFIRM_PROG");
924 if (access(__pmNativePath(xconfirm), X_OK) < 0) {
925 fprintf(stderr, "%s: using stderr - cannot access %s: %s\n",
926 pmProgname, xconfirm, osstrerror());
927 }
928 else
929 errtype = PM_USEDIALOG;
930 }
931 else if (strcmp(ferr, "") != 0)
932 errtype = PM_USEFILE;
933 }
934 }
935 return errtype;
936 }
937
938 static int
939 vpmprintf(const char *msg, va_list arg)
940 {
941 int lsize = 0;
942
943 if (fptr == NULL && msgsize == 0) { /* create scratch file */
944 int fd = -1;
945
946 fname = tempnam(pmGetConfig("PCP_TMP_DIR"), "pcp-");
947 if (fname == NULL ||
948 (fd = open(fname, O_RDWR|O_APPEND|O_CREAT|O_EXCL, 0600)) < 0 ||
949 (fptr = fdopen(fd, "a")) == NULL) {
950 fprintf(stderr, "%s: vpmprintf: failed to create \"%s\": %s\n",
951 pmProgname, fname, osstrerror());
952 fprintf(stderr, "vpmprintf msg:\n");
953 if (fd != -1)
954 close(fd);
955 msgsize = -1;
956 }
957 }
958
959 if (msgsize < 0) {
960 vfprintf(stderr, msg, arg);
961 fflush(stderr);
962 lsize = 0;
963 }
964 else
965 msgsize += (lsize = vfprintf(fptr, msg, arg));
966
967 return lsize;
968 }
969
970 int
971 pmprintf(const char *msg, ...)
972 {
973 va_list arg;
974 int lsize;
975
976 va_start(arg, msg);
977 lsize = vpmprintf(msg, arg);
978 va_end(arg);
979 return lsize;
980 }
981
982 int
983 pmflush(void)
984 {
985 int sts = 0;
986 int len;
987 int state;
988 FILE *eptr = NULL;
989
990 if (fptr != NULL && msgsize > 0) {
991 fflush(fptr);
992 state = pmfstate(PM_QUERYERR);
993 if (state == PM_USEFILE) {
994 if ((eptr = fopen(ferr, "a")) == NULL) {
995 fprintf(stderr, "pmflush: cannot append to file '%s' (from "
996 "$PCP_STDERR): %s\n", ferr, osstrerror());
997 state = PM_USESTDERR;
998 }
999 }
1000 switch (state) {
1001 case PM_USESTDERR:
1002 rewind(fptr);
1003 while ((len = (int)read(fileno(fptr), outbuf, MSGBUFLEN)) > 0) {
1004 sts = write(fileno(stderr), outbuf, len);
1005 if (sts != len) {
1006 fprintf(stderr, "pmflush: write() failed: %s\n",
1007 osstrerror());
1008 }
1009 sts = 0;
1010 }
1011 break;
1012 case PM_USEDIALOG:
1013 /* If we're here, it means xconfirm has passed access test */
1014 snprintf(outbuf, sizeof(outbuf), "%s -file %s -c -B OK -icon info"
1015 " %s -header 'PCP Information' >/dev/null",
1016 __pmNativePath(pmGetConfig("PCP_XCONFIRM_PROG")), fname,
1017 (msgsize > 80 ? "-useslider" : ""));
1018 if (system(outbuf) < 0) {
1019 fprintf(stderr, "%s: system failed: %s\n", pmProgname,
1020 osstrerror());
1021 sts = -oserror();
1022 }
1023 break;
1024 case PM_USEFILE:
1025 rewind(fptr);
1026 while ((len = (int)read(fileno(fptr), outbuf, MSGBUFLEN)) > 0) {
1027 sts = write(fileno(eptr), outbuf, len);
1028 if (sts != len) {
1029 fprintf(stderr, "pmflush: write() failed: %s\n",
1030 osstrerror());
1031 }
1032 sts = 0;
1033 }
1034 fclose(eptr);
1035 break;
1036 }
1037 fclose(fptr);
1038 fptr = NULL;
1039 unlink(fname);
1040 free(fname);
1041 if (sts >= 0)
1042 sts = msgsize;
1043 }
1044
1045 msgsize = 0;
1046
1047 return sts;
1048 }
1049
1050 /*
1051 * Set the pmcd client identity as exported by pmcd.client.whoami
1052 *
1053 * Identity is of the form
1054 * hostname (ipaddr) <id>
1055 *
1056 * Assumes you already have a current host context.
1057 */
1058 int
1059 __pmSetClientId(const char *id)
1060 {
1061 char *name = "pmcd.client.whoami";
1062 pmID pmid;
1063 int sts;
1064 pmResult store = { .numpmid = 1 };
1065 pmValueSet pmvs;
1066 pmValueBlock *pmvb;
1067 char host[MAXHOSTNAMELEN];
1068 char ipaddr[16] = ""; /* IPv4 xxx.xxx.xxx.xxx */
1069 struct hostent *hep = NULL;
1070 int vblen;
1071
1072 if ((sts = pmLookupName(1, &name, &pmid)) < 0)
1073 return sts;
1074
1075 (void)gethostname(host, MAXHOSTNAMELEN);
1076 hep = gethostbyname(host);
1077 if (hep != NULL) {
1078 strcpy(host, hep->h_name);
1079 if (hep->h_addrtype == AF_INET) {
1080 strcpy(ipaddr, inet_ntoa(*((struct in_addr *)hep->h_addr_list[0])));
1081 }
1082 vblen = strlen(host) + strlen(ipaddr) + strlen(id) + 5;
1083 }
1084 else
1085 vblen = strlen(host) + strlen(id) + 2;
1086
1087 /* build pmResult for pmStore() */
1088 pmvb = (pmValueBlock *)malloc(PM_VAL_HDR_SIZE+vblen);
1089 if (pmvb == NULL) {
1090 __pmNoMem("__pmSetClientId", PM_VAL_HDR_SIZE+vblen, PM_RECOV_ERR);
1091 return -ENOMEM;
1092 }
1093 pmvb->vtype = PM_TYPE_STRING;
1094 pmvb->vlen = PM_VAL_HDR_SIZE+vblen;
1095 strcpy(pmvb->vbuf, host);
1096 strcat(pmvb->vbuf, " ");
1097 if (ipaddr[0] != '\0') {
1098 strcat(pmvb->vbuf, "(");
1099 strcat(pmvb->vbuf, ipaddr);
1100 strcat(pmvb->vbuf, ") ");
1101 }
1102 strcat(pmvb->vbuf, id);
1103
1104 pmvs.pmid = pmid;
1105 pmvs.numval = 1;
1106 pmvs.valfmt = PM_VAL_SPTR;
1107 pmvs.vlist[0].value.pval = pmvb;
1108 pmvs.vlist[0].inst = PM_IN_NULL;
1109
1110 store.vset[0] = &pmvs;
1111 sts = pmStore(&store);
1112 free(pmvb);
1113 return sts;
1114 }
1115
1116 char *
1117 __pmGetClientId(int argc, char **argv)
1118 {
1119 char *clientID;
1120 int a, need = 0;
1121
1122 for (a = 0; a < argc; a++)
1123 need += strlen(argv[a]) + 1;
1124 clientID = (char *)malloc(need);
1125 if (clientID) {
1126 clientID[0] = '\0';
1127 for (a = 0; a < argc; a++) {
1128 strcat(clientID, argv[a]);
1129 if (a < argc - 1)
1130 strcat(clientID, " ");
1131 }
1132 }
1133 return clientID;
1134 }
1135
1136 int
1137 __pmSetClientIdArgv(int argc, char **argv)
1138 {
1139 char *id = __pmGetClientId(argc, argv);
1140 int sts;
1141
1142 if (id) {
1143 sts = __pmSetClientId(id);
1144 free(id);
1145 return sts;
1146 }
1147 return -ENOMEM;
1148 }
1149
1150 /*
1151 * Support for C environments that have lame libc implementations.
1152 * All of these developed from first principles, so no 3rd party
1153 * copyright or licensing issues.
1154 */
1155
1156 #ifndef HAVE_BASENAME
1157 char *
1158 basename(char *name)
1159 {
1160 char *p = strrchr(name, '/');
1161
1162 if (p == NULL)
1163 return(name);
1164 else
1165 return(p+1);
1166 }
1167 #endif
1168
1169 #ifndef HAVE_DIRNAME
1170 char *
1171 dirname(char *name)
1172 {
1173 char *p = strrchr(name, '/');
1174 static char *dot = ".";
1175
1176 if (p == NULL)
1177 return(dot);
1178 else {
1179 *p = '\0';
1180 return(name);
1181 }
1182 }
1183 #endif
1184
1185 #ifndef HAVE_ISNAND
1186 int
1187 isnand(double d)
1188 {
1189 #ifdef HAVE_ISNANF
1190 float f = (float)d;
1191 /* not exact, but the best we can do! */
1192 return(isnanf(f));
1193 #else
1194 /* no support, assume is _not_ NAN, i.e. OK */
1195 return(0);
1196 #endif
1197 }
1198 #endif
1199
1200 #ifndef HAVE_SCANDIR
1201 /*
1202 * Scan the directory dirname, building an array of pointers to
1203 * dirent entries using malloc(3C). select() and compare() are
1204 * used to optionally filter and sort directory entries.
1205 */
1206 int
1207 scandir(const char *dirname, struct dirent ***namelist,
1208 int(*select)(const_dirent *),
1209 int(*compare)(const_dirent **, const_dirent **))
1210 {
1211 DIR *dirp;
1212 int n = 0;
1213 struct dirent **names = NULL;
1214 struct dirent *dp;
1215 struct dirent *tp;
1216
1217 if ((dirp = opendir(dirname)) == NULL)
1218 return -1;
1219
1220 while ((dp = readdir(dirp)) != NULL) {
1221 if (select && (*select)(dp) == 0)
1222 continue;
1223
1224 n++;
1225 if ((names = (struct dirent **)realloc(names, n * sizeof(dp))) == NULL)
1226 return -1;
1227
1228 if ((names[n-1] = tp = (struct dirent *)malloc(
1229 sizeof(*dp)-sizeof(dp->d_name)+strlen(dp->d_name)+1)) == NULL)
1230 return -1;
1231
1232 tp->d_ino = dp->d_ino;
1233 #if defined(HAVE_DIRENT_D_OFF)
1234 tp->d_off = dp->d_off;
1235 #else
1236 tp->d_reclen = dp->d_reclen;
1237 #endif
1238 memcpy(tp->d_name, dp->d_name, strlen(dp->d_name)+1);
1239 }
1240 closedir(dirp);
1241 *namelist = names;
1242
1243 if (n && compare)
1244 qsort(names, n, sizeof(names[0]),
1245 (int(*)(const void *, const void *))compare);
1246 return n;
1247 }
1248
1249 /*
1250 * Alphabetical sort for default use
1251 */
1252 int
1253 alphasort(const_dirent **p, const_dirent **q)
1254 {
1255 return strcmp((*p)->d_name, (*q)->d_name);
1256 }
1257 #endif
1258
1259 #define PROCFS_ENTRY_SIZE 40 /* encompass any size of entry for pid */
1260
1261 #if defined(IS_DARWIN) /* No procfs on Mac OS X */
1262 #include <sys/sysctl.h>
1263 int
1264 __pmProcessExists(pid_t pid)
1265 {
1266 struct kinfo_proc kp;
1267 size_t len = sizeof(kp);
1268 int mib[4];
1269
1270 mib[0] = CTL_KERN;
1271 mib[1] = KERN_PROC;
1272 mib[2] = KERN_PROC_PID;
1273 mib[3] = pid;
1274 if (sysctl(mib, 4, &kp, &len, NULL, 0) == -1)
1275 return 0;
1276 return (len > 0);
1277 }
1278 #elif defined(HAVE_PROCFS)
1279 #define PROCFS "/proc"
1280 #define PROCFS_PATH_SIZE (sizeof(PROCFS)+PROCFS_ENTRY_SIZE)
1281 int
1282 __pmProcessExists(pid_t pid)
1283 {
1284 char proc_buf[PROCFS_PATH_SIZE];
1285 snprintf(proc_buf, sizeof(proc_buf), "%s/%" FMT_PID, PROCFS, pid);
1286 return (access(proc_buf, F_OK) == 0);
1287 }
1288 #elif !defined(IS_MINGW)
1289 !bozo!
1290 #endif
1291
1292 #if defined(HAVE_KILL)
1293 int
1294 __pmProcessTerminate(pid_t pid, int force)
1295 {
1296 return kill(pid, force ? SIGKILL : SIGTERM);
1297 }
1298 #elif !defined(IS_MINGW)
1299 !bozo!
1300 #endif
1301
1302 #if defined(HAVE_SBRK)
1303 int
1304 __pmProcessDataSize(unsigned long *size)
1305 {
1306 static void *base;
1307
1308 if (size && base)
1309 *size = (sbrk(0) - base) / 1024;
1310 else {
1311 base = sbrk(0);
1312 if (size)
1313 *size = 0;
1314 }
1315 return 0;
1316 }
1317 #elif !defined(IS_MINGW)
1318 #warning "Platform does not define a process datasize interface?"
1319 int __pmProcessDataSize(unsigned long *) { return -1; }
1320 #endif
1321
1322 #if !defined(IS_MINGW)
1323 int
1324 __pmProcessRunTimes(double *usr, double *sys)
1325 {
1326 struct tms tms;
1327 double ticks = (double)sysconf(_SC_CLK_TCK);
1328
1329 if (times(&tms) == (clock_t)-1) {
1330 *usr = *sys = 0.0;
1331 return -1;
1332 }
1333 *usr = (double)tms.tms_utime / ticks;
1334 *sys = (double)tms.tms_stime / ticks;
1335 return 0;
1336 }
1337 #endif
1338
1339 #if !defined(IS_MINGW)
1340 pid_t
1341 __pmProcessCreate(char **argv, int *infd, int *outfd)
1342 {
1343 int in[2];
1344 int out[2];
1345 pid_t pid;
1346
1347 if (pipe1(in) < 0)
1348 return -oserror();
1349 if (pipe1(out) < 0)
1350 return -oserror();
1351
1352 pid = fork();
1353 if (pid < 0) {
1354 return -1;
1355 }
1356 else if (pid) {
1357 /* parent */
1358 close(in[0]);
1359 close(out[1]);
1360 *infd = out[0];
1361 *outfd = in[1];
1362 }
1363 else {
1364 /* child */
1365 close(in[1]);
1366 close(out[0]);
1367 if (in[0] != 0) {
1368 close(0);
1369 dup2(in[0], 0);
1370 close(in[0]);
1371 }
1372 if (out[1] != 1) {
1373 close(1);
1374 dup2(out[1], 1);
1375 close(out[1]);
1376 }
1377 execvp(argv[0], argv);
1378 fprintf(stderr, "execvp: %s\n", osstrerror());
1379 exit(1);
1380 }
1381 return pid;
1382 }
1383
1384 int
1385 __pmSetSignalHandler(int sig, __pmSignalHandler func)
1386 {
1387 signal(sig, func);
1388 return 0;
1389 }
1390
1391 int
1392 __pmSetProgname(const char *program)
1393 {
1394 char *p;
1395
1396 /* Trim command name of leading directory components */
1397 if (program)
1398 pmProgname = (char *)program;
1399 for (p = pmProgname; pmProgname && *p; p++) {
1400 if (*p == '/')
1401 pmProgname = p+1;
1402 }
1403 return 0;
1404 }
1405
1406 void *
1407 __pmMemoryMap(int fd, size_t sz, int writable)
1408 {
1409 int mflags = writable ? (PROT_READ | PROT_WRITE) : PROT_READ;
1410 void *addr = mmap(NULL, sz, mflags, MAP_SHARED, fd, 0);
1411 if (addr == MAP_FAILED)
1412 return NULL;
1413 return addr;
1414 }
1415
1416 void
1417 __pmMemoryUnmap(void *addr, size_t sz)
1418 {
1419 munmap(addr, sz);
1420 }
1421 #endif