summaryrefslogtreecommitdiff
path: root/src/libpcp/src/util.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/libpcp/src/util.c')
-rw-r--r--src/libpcp/src/util.c2351
1 files changed, 2351 insertions, 0 deletions
diff --git a/src/libpcp/src/util.c b/src/libpcp/src/util.c
new file mode 100644
index 0000000..f0e212a
--- /dev/null
+++ b/src/libpcp/src/util.c
@@ -0,0 +1,2351 @@
+/*
+ * General Utility Routines
+ *
+ * Copyright (c) 2012-2014 Red Hat.
+ * Copyright (c) 2009 Aconex. All Rights Reserved.
+ * Copyright (c) 1995-2002,2004 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+ * License for more details.
+ *
+ * Thread-safe notes
+ *
+ * pmState - no side-effects, don't bother locking
+ *
+ * pmProgname - most likely set in main(), not worth protecting here
+ * and impossible to capture all the read uses in other places
+ *
+ * base (in __pmProcessDataSize) - no real side-effects, don't bother
+ * locking
+ */
+
+#include <stdarg.h>
+#include <sys/stat.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <ctype.h>
+
+#include "pmapi.h"
+#include "impl.h"
+#include "pmdbg.h"
+#include "internal.h"
+
+#if defined(HAVE_SYS_TIMES_H)
+#include <sys/times.h>
+#endif
+#if defined(HAVE_SYS_MMAN_H)
+#include <sys/mman.h>
+#endif
+#if defined(HAVE_IEEEFP_H)
+#include <ieeefp.h>
+#endif
+#if defined(HAVE_MATH_H)
+#include <math.h>
+#endif
+#if defined(IS_DARWIN)
+#include <sys/sysctl.h>
+#include <mach/clock.h>
+#include <mach/mach.h>
+#endif
+
+static FILE **filelog;
+static int nfilelog;
+static int dosyslog;
+static int pmState = PM_STATE_APPL;
+static int done_exit;
+
+INTERN char *pmProgname = "pcp"; /* the real McCoy */
+
+static int vpmprintf(const char *, va_list);
+
+/*
+ * if onoff == 1, logging is to syslog and stderr, else logging is
+ * just to stderr (this is the default)
+ */
+void
+__pmSyslog(int onoff)
+{
+ PM_INIT_LOCKS();
+ PM_LOCK(__pmLock_libpcp);
+ dosyslog = onoff;
+ if (dosyslog)
+ openlog("pcp", LOG_PID, LOG_DAEMON);
+ else
+ closelog();
+ PM_UNLOCK(__pmLock_libpcp);
+}
+
+/*
+ * This is a wrapper around syslog(3C) that writes similar messages to stderr,
+ * but if __pmSyslog(1) is called, the messages will really go to syslog
+ */
+void
+__pmNotifyErr(int priority, const char *message, ...)
+{
+ va_list arg;
+ char *p;
+ char *level;
+ time_t now;
+
+ va_start(arg, message);
+
+ time(&now);
+
+ PM_INIT_LOCKS();
+ PM_LOCK(__pmLock_libpcp);
+ if (dosyslog) {
+ char syslogmsg[2048];
+
+ vsnprintf(syslogmsg, sizeof(syslogmsg), message, arg);
+ va_end(arg);
+ va_start(arg, message);
+ syslog(priority, "%s", syslogmsg);
+ }
+ PM_UNLOCK(__pmLock_libpcp);
+
+ /*
+ * do the stderr equivalent
+ */
+
+ switch (priority) {
+ case LOG_EMERG :
+ level = "Emergency";
+ break;
+ case LOG_ALERT :
+ level = "Alert";
+ break;
+ case LOG_CRIT :
+ level = "Critical";
+ break;
+ case LOG_ERR :
+ level = "Error";
+ break;
+ case LOG_WARNING :
+ level = "Warning";
+ break;
+ case LOG_NOTICE :
+ level = "Notice";
+ break;
+ case LOG_INFO :
+ level = "Info";
+ break;
+ case LOG_DEBUG :
+ level = "Debug";
+ break;
+ default:
+ level = "???";
+ break;
+ }
+
+ PM_INIT_LOCKS();
+ PM_LOCK(__pmLock_libpcp);
+ pmprintf("[%.19s] %s(%" FMT_PID ") %s: ", ctime(&now), pmProgname, getpid(), level);
+ PM_UNLOCK(__pmLock_libpcp);
+ vpmprintf(message, arg);
+ va_end(arg);
+ /* trailing \n if needed */
+ for (p = (char *)message; *p; p++)
+ ;
+ if (p == message || p[-1] != '\n')
+ pmprintf("\n");
+ pmflush();
+}
+
+static void
+logheader(const char *progname, FILE *log, const char *act)
+{
+ time_t now;
+ char host[MAXHOSTNAMELEN];
+
+ setlinebuf(log); /* line buffering for log files */
+ gethostname(host, MAXHOSTNAMELEN);
+ host[MAXHOSTNAMELEN-1] = '\0';
+ time(&now);
+ PM_LOCK(__pmLock_libpcp);
+ fprintf(log, "Log for %s on %s %s %s\n", progname, host, act, ctime(&now));
+ PM_UNLOCK(__pmLock_libpcp);
+}
+
+static void
+logfooter(FILE *log, const char *act)
+{
+ time_t now;
+
+ time(&now);
+ PM_LOCK(__pmLock_libpcp);
+ fprintf(log, "\nLog %s %s", act, ctime(&now));
+ PM_UNLOCK(__pmLock_libpcp);
+}
+
+static void
+logonexit(void)
+{
+ int i;
+
+ PM_LOCK(__pmLock_libpcp);
+ if (++done_exit != 1) {
+ PM_UNLOCK(__pmLock_libpcp);
+ return;
+ }
+
+ for (i = 0; i < nfilelog; i++)
+ logfooter(filelog[i], "finished");
+
+ PM_UNLOCK(__pmLock_libpcp);
+}
+
+/* common code shared by __pmRotateLog and __pmOpenLog */
+static FILE *
+logreopen(const char *progname, const char *logname, FILE *oldstream,
+ int *status)
+{
+ int oldfd;
+ int dupoldfd;
+ FILE *dupoldstream = oldstream;
+
+ /*
+ * Do our own version of freopen() because the standard one closes the
+ * original stream BEFORE trying to open the new one. Once it's gone,
+ * there's no way to get the closed stream back if the open fails.
+ */
+
+ fflush(oldstream);
+ *status = 0; /* set to one if all this works ... */
+ oldfd = fileno(oldstream);
+ if ((dupoldfd = dup(oldfd)) >= 0) {
+ /*
+ * try to remove the file first ... don't bother if this fails,
+ * but if it succeeds, we at least get a chance to define the
+ * owner and mode, rather than inheriting this from an existing
+ * writeable file ... really only a problem when called as with
+ * uid == 0, e.g. from pmcd(1).
+ */
+ unlink(logname);
+
+ oldstream = freopen(logname, "w", oldstream);
+ if (oldstream == NULL) {
+ int save_error = oserror(); /* need for error message */
+
+ close(oldfd);
+ if (dup(dupoldfd) != oldfd) {
+ /* fd juggling failed! */
+ oldstream = NULL;
+ }
+ else {
+ /* oldfd now re-instated as at entry */
+ oldstream = fdopen(oldfd, "w");
+ }
+ if (oldstream == NULL) {
+ /* serious trouble ... choose least obnoxious alternative */
+ if (dupoldstream == stderr)
+ oldstream = fdopen(fileno(stdout), "w");
+ else
+ oldstream = fdopen(fileno(stderr), "w");
+ }
+ if (oldstream != NULL) {
+ /*
+ * oldstream was NULL, but recovered so now fixup
+ * input oldstream ... this is potentially dangerous,
+ * but we're relying on
+ * (a) fflush(oldstream) on entry flushes buffers
+ * (b) fdopen() leaves new oldstream initialized
+ * (c) caller knows nothing about "new" oldstream
+ * and is never going to fclose() it, so only
+ * fclose() will come at exit() and should be
+ * benign (except possibly for a free() of an
+ * already free()'d buffer)
+ */
+ *dupoldstream = *oldstream; /* struct copy */
+ /* put oldstream back for return value */
+ oldstream = dupoldstream;
+ }
+ pmprintf("%s: cannot open log \"%s\" for writing : %s\n",
+ progname, logname, strerror(save_error));
+ pmflush();
+ }
+ else {
+ /* yippee */
+ *status = 1;
+ }
+ close(dupoldfd);
+ }
+ else {
+ pmprintf("%s: cannot redirect log output to \"%s\": %s\n",
+ progname, logname, strerror(errno));
+ pmflush();
+ }
+ return oldstream;
+}
+
+FILE *
+__pmOpenLog(const char *progname, const char *logname, FILE *oldstream,
+ int *status)
+{
+ oldstream = logreopen(progname, logname, oldstream, status);
+ PM_INIT_LOCKS();
+ logheader(progname, oldstream, "started");
+
+ PM_LOCK(__pmLock_libpcp);
+ nfilelog++;
+ if (nfilelog == 1)
+ atexit(logonexit);
+
+ filelog = (FILE **)realloc(filelog, nfilelog * sizeof(FILE *));
+ if (filelog == NULL) {
+ __pmNoMem("__pmOpenLog", nfilelog * sizeof(FILE *), PM_FATAL_ERR);
+ }
+ filelog[nfilelog-1] = oldstream;
+
+ PM_UNLOCK(__pmLock_libpcp);
+ return oldstream;
+}
+
+FILE *
+__pmRotateLog(const char *progname, const char *logname, FILE *oldstream,
+ int *status)
+{
+ int i;
+
+ PM_INIT_LOCKS();
+ PM_LOCK(__pmLock_libpcp);
+ for (i = 0; i < nfilelog; i++) {
+ if (oldstream == filelog[i]) {
+ logfooter(oldstream, "rotated"); /* old */
+ oldstream = logreopen(progname, logname, oldstream, status);
+ logheader(progname, oldstream, "rotated"); /* new */
+ filelog[i] = oldstream;
+ break;
+ }
+ }
+ PM_UNLOCK(__pmLock_libpcp);
+ return oldstream;
+}
+
+/* pmID -> string, max length is 20 bytes */
+char *
+pmIDStr_r(pmID pmid, char *buf, int buflen)
+{
+ __pmID_int* p = (__pmID_int*)&pmid;
+ if (pmid == PM_ID_NULL)
+ snprintf(buf, buflen, "%s", "PM_ID_NULL");
+ else if (p->domain == DYNAMIC_PMID && p->item == 0)
+ /*
+ * this PMID represents the base of a dynamic subtree in the PMNS
+ * ... identified by setting the domain field to the reserved
+ * value DYNAMIC_PMID and storing the real domain of the PMDA
+ * that can enumerate the subtree in the cluster field, while
+ * the item field is not used
+ */
+ snprintf(buf, buflen, "%d.*.*", p->cluster);
+ else
+ snprintf(buf, buflen, "%d.%d.%d", p->domain, p->cluster, p->item);
+ return buf;
+}
+
+const char *
+pmIDStr(pmID pmid)
+{
+ static char idbuf[20];
+ pmIDStr_r(pmid, idbuf, sizeof(idbuf));
+ return idbuf;
+}
+
+/* pmInDom -> string, max length is 20 bytes */
+char *
+pmInDomStr_r(pmInDom indom, char *buf, int buflen)
+{
+ __pmInDom_int* p = (__pmInDom_int*)&indom;
+ if (indom == PM_INDOM_NULL)
+ snprintf(buf, buflen, "%s", "PM_INDOM_NULL");
+ else
+ snprintf(buf, buflen, "%d.%d", p->domain, p->serial);
+ return buf;
+}
+
+const char *
+pmInDomStr(pmInDom indom)
+{
+ static char indombuf[20];
+ pmInDomStr_r(indom, indombuf, sizeof(indombuf));
+ return indombuf;
+}
+
+/* double -> string, max length is 8 bytes */
+char *
+pmNumberStr_r(double value, char *buf, int buflen)
+{
+ if (value >= 0.0) {
+ if (value >= 999995000000000.0)
+ snprintf(buf, buflen, " inf? ");
+ else if (value >= 999995000000.0)
+ snprintf(buf, buflen, "%6.2fT", value / 1000000000000.0);
+ else if (value >= 999995000.0)
+ snprintf(buf, buflen, "%6.2fG", value / 1000000000.0);
+ else if (value >= 999995.0)
+ snprintf(buf, buflen, "%6.2fM", value / 1000000.0);
+ else if (value >= 999.995)
+ snprintf(buf, buflen, "%6.2fK", value / 1000.0);
+ else if (value >= 0.005)
+ snprintf(buf, buflen, "%6.2f ", value);
+ else
+ snprintf(buf, buflen, "%6.2f ", 0.0);
+ }
+ else {
+ if (value <= -99995000000000.0)
+ snprintf(buf, buflen, "-inf? ");
+ else if (value <= -99995000000.0)
+ snprintf(buf, buflen, "%6.2fT", value / 1000000000000.0);
+ else if (value <= -99995000.0)
+ snprintf(buf, buflen, "%6.2fG", value / 1000000000.0);
+ else if (value <= -99995.0)
+ snprintf(buf, buflen, "%6.2fM", value / 1000000.0);
+ else if (value <= -99.995)
+ snprintf(buf, buflen, "%6.2fK", value / 1000.0);
+ else if (value <= -0.005)
+ snprintf(buf, buflen, "%6.2f ", value);
+ else
+ snprintf(buf, buflen, "%6.2f ", 0.0);
+ }
+ return buf;
+}
+
+const char *
+pmNumberStr(double value)
+{
+ static char nbuf[8];
+ pmNumberStr_r(value, nbuf, sizeof(nbuf));
+ return nbuf;
+}
+
+/* flags -> string, max length is 64 bytes */
+char *
+pmEventFlagsStr_r(int flags, char *buf, int buflen)
+{
+ /*
+ * buffer needs to be long enough to hold each flag name
+ * (excluding missed) plus the separation commas, so
+ * point,start,end,id,parent (even though it is unlikely that
+ * both start and end would be set for the one event record)
+ */
+ int started = 0;
+
+ if (flags & PM_EVENT_FLAG_MISSED)
+ return strcpy(buf, "missed");
+
+ buf[0] = '\0';
+ if (flags & PM_EVENT_FLAG_POINT) {
+ if (started++) strcat(buf, ",");
+ strcat(buf, "point");
+ }
+ if (flags & PM_EVENT_FLAG_START) {
+ if (started++) strcat(buf, ",");
+ strcat(buf, "start");
+ }
+ if (flags & PM_EVENT_FLAG_END) {
+ if (started++) strcat(buf, ",");
+ strcat(buf, "end");
+ }
+ if (flags & PM_EVENT_FLAG_ID) {
+ if (started++) strcat(buf, ",");
+ strcat(buf, "id");
+ }
+ if (flags & PM_EVENT_FLAG_PARENT) {
+ if (started++) strcat(buf, ",");
+ strcat(buf, "parent");
+ }
+ return buf;
+}
+
+const char *
+pmEventFlagsStr(int flags)
+{
+ static char ebuf[64];
+ pmEventFlagsStr_r(flags, ebuf, sizeof(ebuf));
+ return ebuf;
+}
+
+/*
+ * Several PMAPI interfaces allocate a list of strings into a buffer
+ * pointed to by (char **) which can be safely freed simply by
+ * freeing the pointer to the buffer.
+ *
+ * Here we provide some functions for manipulating these lists.
+ */
+
+/* Add the given item to the list, which may be empty. */
+int
+__pmStringListAdd(char *item, int numElements, char ***list)
+{
+ size_t ptrSize;
+ size_t dataSize;
+ size_t newSize;
+ char *initialString;
+ char *finalString;
+ char **newList;
+ int i;
+
+ /* Compute the sizes of the pointers and data for the current list. */
+ if (*list != NULL) {
+ ptrSize = numElements * sizeof(**list);
+ initialString = **list;
+ finalString = (*list)[numElements - 1];
+ dataSize = (finalString + strlen(finalString) + 1) - initialString;
+ }
+ else {
+ ptrSize = 0;
+ dataSize = 0;
+ }
+
+ /*
+ * Now allocate a new buffer for the expanded list.
+ * We need room for a new pointer and for the new item.
+ */
+ newSize = ptrSize + sizeof(**list) + dataSize + strlen(item) + 1;
+ newList = realloc(*list, newSize);
+ if (newList == NULL) {
+ __pmNoMem("__pmStringListAdd", newSize, PM_FATAL_ERR);
+ }
+
+ /*
+ * Shift the existing data to make room for the new pointer and
+ * recompute each existing pointer.
+ */
+ finalString = (char *)(newList + numElements + 1);
+ if (dataSize != 0) {
+ initialString = (char *)(newList + numElements);
+ memmove(finalString, initialString, dataSize);
+ for (i = 0; i < numElements; ++i) {
+ newList[i] = finalString;
+ finalString += strlen(finalString) + 1;
+ }
+ }
+
+ /* Now add the new item. */
+ newList[numElements] = finalString;
+ strcpy(finalString, item);
+
+ *list = newList;
+ return numElements + 1;
+}
+
+/* Search for the given string in the given string list. */
+char *
+__pmStringListFind(const char *item, int numElements, char **list)
+{
+ int e;
+
+ if (list == NULL)
+ return NULL; /* no list to search */
+
+ for (e = 0; e < numElements; ++e) {
+ if (strcmp(item, list[e]) == 0)
+ return list[e];
+ }
+
+ /* Not found. */
+ return NULL;
+}
+
+/*
+ * Save/restore global debugging flag, without locking.
+ * Needed since tracing PDUs really messes __pmDump*() routines
+ * up when pmNameInDom is called internally.
+ */
+static int
+save_debug(void)
+{
+ int saved = pmDebug;
+ pmDebug = 0;
+ return saved;
+}
+
+static void
+restore_debug(int saved)
+{
+ pmDebug = saved;
+}
+
+static void
+dump_valueset(FILE *f, pmValueSet *vsp)
+{
+ pmDesc desc;
+ char errmsg[PM_MAXERRMSGLEN];
+ char strbuf[20];
+ char *pmid, *p;
+ int have_desc = 1;
+ int n, j;
+
+ pmid = pmIDStr_r(vsp->pmid, strbuf, sizeof(strbuf));
+ if ((n = pmNameID(vsp->pmid, &p)) < 0)
+ fprintf(f," %s (%s):", pmid, "<noname>");
+ else {
+ fprintf(f," %s (%s):", pmid, p);
+ free(p);
+ }
+ if (vsp->numval == 0) {
+ fprintf(f, " No values returned!\n");
+ return;
+ }
+ if (vsp->numval < 0) {
+ fprintf(f, " %s\n", pmErrStr_r(vsp->numval, errmsg, sizeof(errmsg)));
+ return;
+ }
+ if (__pmGetInternalState() == PM_STATE_PMCS ||
+ pmLookupDesc(vsp->pmid, &desc) < 0) {
+ /* don't know, so punt on the most common cases */
+ desc.indom = PM_INDOM_NULL;
+ have_desc = 0;
+ }
+
+ fprintf(f, " numval: %d", vsp->numval);
+ fprintf(f, " valfmt: %d vlist[]:\n", vsp->valfmt);
+ for (j = 0; j < vsp->numval; j++) {
+ pmValue *vp = &vsp->vlist[j];
+ if (vsp->numval > 1 || vp->inst != PM_INDOM_NULL) {
+ fprintf(f," inst [%d", vp->inst);
+ if (have_desc &&
+ pmNameInDom(desc.indom, vp->inst, &p) >= 0) {
+ fprintf(f, " or \"%s\"]", p);
+ free(p);
+ }
+ else {
+ fprintf(f, " or ???]");
+ }
+ fputc(' ', f);
+ }
+ else
+ fprintf(f, " ");
+ fprintf(f, "value ");
+
+ if (have_desc)
+ pmPrintValue(f, vsp->valfmt, desc.type, vp, 1);
+ else {
+ if (vsp->valfmt == PM_VAL_INSITU)
+ pmPrintValue(f, vsp->valfmt, PM_TYPE_UNKNOWN, vp, 1);
+ else
+ pmPrintValue(f, vsp->valfmt, (int)vp->value.pval->vtype, vp, 1);
+ }
+ fputc('\n', f);
+ }
+}
+
+void
+__pmDumpResult(FILE *f, const pmResult *resp)
+{
+ int i, saved;
+
+ saved = save_debug();
+ fprintf(f, "pmResult dump from " PRINTF_P_PFX "%p timestamp: %d.%06d ",
+ resp, (int)resp->timestamp.tv_sec, (int)resp->timestamp.tv_usec);
+ __pmPrintStamp(f, &resp->timestamp);
+ fprintf(f, " numpmid: %d\n", resp->numpmid);
+ for (i = 0; i < resp->numpmid; i++)
+ dump_valueset(f, resp->vset[i]);
+ restore_debug(saved);
+}
+
+void
+__pmDumpHighResResult(FILE *f, const pmHighResResult *hresp)
+{
+ int i, saved;
+
+ saved = save_debug();
+ fprintf(f, "pmHighResResult dump from " PRINTF_P_PFX "%p timestamp: %d.%09d ",
+ hresp, (int)hresp->timestamp.tv_sec, (int)hresp->timestamp.tv_nsec);
+ __pmPrintHighResStamp(f, &hresp->timestamp);
+ fprintf(f, " numpmid: %d\n", hresp->numpmid);
+ for (i = 0; i < hresp->numpmid; i++)
+ dump_valueset(f, hresp->vset[i]);
+ restore_debug(saved);
+}
+
+static void
+print_event_summary(FILE *f, const pmValue *val, int highres)
+{
+ struct timespec tsstamp;
+ struct timeval tvstamp;
+ __pmTimespec *tsp;
+ __pmTimeval *tvp;
+ unsigned int flags;
+ size_t size;
+ char *base;
+ int nparams;
+ int nrecords;
+ int nmissed = 0;
+ int r; /* records */
+ int p; /* parameters in a record ... */
+
+ if (highres) {
+ pmHighResEventArray *hreap = (pmHighResEventArray *)val->value.pval;
+ nrecords = hreap->ea_nrecords;
+ base = (char *)&hreap->ea_record[0];
+ tsp = (__pmTimespec *)base;
+ tsstamp.tv_sec = tsp->tv_sec;
+ tsstamp.tv_nsec = tsp->tv_nsec;
+ }
+ else {
+ pmEventArray *eap = (pmEventArray *)val->value.pval;
+ nrecords = eap->ea_nrecords;
+ base = (char *)&eap->ea_record[0];
+ tvp = (__pmTimeval *)base;
+ tvstamp.tv_sec = tvp->tv_sec;
+ tvstamp.tv_usec = tvp->tv_usec;
+ }
+
+ /* walk packed event record array */
+ for (r = 0; r < nrecords-1; r++) {
+ if (highres) {
+ pmHighResEventRecord *hrerp = (pmHighResEventRecord *)base;
+ size = sizeof(hrerp->er_timestamp) + sizeof(hrerp->er_flags) +
+ sizeof(hrerp->er_nparams);
+ flags = hrerp->er_flags;
+ nparams = hrerp->er_nparams;
+ }
+ else {
+ pmEventRecord *erp = (pmEventRecord *)base;
+ size = sizeof(erp->er_timestamp) + sizeof(erp->er_flags) +
+ sizeof(erp->er_nparams);
+ flags = erp->er_flags;
+ nparams = erp->er_nparams;
+ }
+
+ if (flags & PM_EVENT_FLAG_MISSED) {
+ nmissed += nparams;
+ continue;
+ }
+
+ base += size;
+ for (p = 0; p < nparams; p++) {
+ pmEventParameter *epp = (pmEventParameter *)base;
+ base += sizeof(epp->ep_pmid) + PM_PDU_SIZE_BYTES(epp->ep_len);
+ }
+ }
+ fprintf(f, "[%d event record", nrecords);
+ if (nrecords != 1)
+ fputc('s', f);
+ if (nmissed > 0)
+ fprintf(f, " (%d missed)", nmissed);
+ if (nrecords > 0) {
+ fprintf(f, " timestamp");
+ if (nrecords > 1)
+ fputc('s', f);
+ fputc(' ', f);
+
+ if (highres)
+ __pmPrintHighResStamp(f, &tsstamp);
+ else
+ __pmPrintStamp(f, &tvstamp);
+
+ if (nrecords > 1) {
+ fprintf(f, "...");
+ if (highres) {
+ tsp = (__pmTimespec *)base;
+ tsstamp.tv_sec = tsp->tv_sec;
+ tsstamp.tv_nsec = tsp->tv_nsec;
+ __pmPrintHighResStamp(f, &tsstamp);
+ }
+ else {
+ tvp = (__pmTimeval *)base;
+ tvstamp.tv_sec = tvp->tv_sec;
+ tvstamp.tv_usec = tvp->tv_usec;
+ __pmPrintStamp(f, &tvstamp);
+ }
+ }
+ }
+ fputc(']', f);
+}
+
+/* Print single pmValue. */
+void
+pmPrintValue(FILE *f, /* output stream */
+ int valfmt, /* from pmValueSet */
+ int type, /* from pmDesc */
+ const pmValue *val, /* value to print */
+ int minwidth) /* output is at least this wide */
+{
+ pmAtomValue a;
+ int i;
+ int n;
+ char *p;
+ int sts;
+
+ if (type != PM_TYPE_UNKNOWN &&
+ type != PM_TYPE_EVENT &&
+ type != PM_TYPE_HIGHRES_EVENT) {
+ sts = pmExtractValue(valfmt, val, type, &a, type);
+ if (sts < 0)
+ type = PM_TYPE_UNKNOWN;
+ }
+
+ switch (type) {
+ case PM_TYPE_32:
+ fprintf(f, "%*i", minwidth, a.l);
+ break;
+
+ case PM_TYPE_U32:
+ fprintf(f, "%*u", minwidth, a.ul);
+ break;
+
+ case PM_TYPE_64:
+ fprintf(f, "%*"PRIi64, minwidth, a.ll);
+ break;
+
+ case PM_TYPE_U64:
+ fprintf(f, "%*"PRIu64, minwidth, a.ull);
+ break;
+
+ case PM_TYPE_FLOAT:
+ fprintf(f, "%*.8g", minwidth, (double)a.f);
+ break;
+
+ case PM_TYPE_DOUBLE:
+ fprintf(f, "%*.16g", minwidth, a.d);
+ break;
+
+ case PM_TYPE_STRING:
+ n = (int)strlen(a.cp) + 2;
+ while (n < minwidth) {
+ fputc(' ', f);
+ n++;
+ }
+ fprintf(f, "\"%s\"", a.cp);
+ free(a.cp);
+ break;
+
+ case PM_TYPE_AGGREGATE:
+ case PM_TYPE_UNKNOWN:
+ if (valfmt == PM_VAL_INSITU) {
+ float *fp = (float *)&val->value.lval;
+ __uint32_t *ip = (__uint32_t *)&val->value.lval;
+ int fp_bad = 0;
+ fprintf(f, "%*u", minwidth, *ip);
+#ifdef HAVE_FPCLASSIFY
+ fp_bad = fpclassify(*fp) == FP_NAN;
+#else
+#ifdef HAVE_ISNANF
+ fp_bad = isnanf(*fp);
+#endif
+#endif
+ if (!fp_bad)
+ fprintf(f, " %*.8g", minwidth, (double)*fp);
+ if (minwidth > 2)
+ minwidth -= 2;
+ fprintf(f, " 0x%*x", minwidth, val->value.lval);
+ }
+ else {
+ int string;
+ int done = 0;
+ if (val->value.pval->vlen == PM_VAL_HDR_SIZE + sizeof(__uint64_t)) {
+ __uint64_t tmp;
+ memcpy((void *)&tmp, (void *)val->value.pval->vbuf, sizeof(tmp));
+ fprintf(f, "%*"PRIu64, minwidth, tmp);
+ done = 1;
+ }
+ if (val->value.pval->vlen == PM_VAL_HDR_SIZE + sizeof(double)) {
+ double tmp;
+ int fp_bad = 0;
+ memcpy((void *)&tmp, (void *)val->value.pval->vbuf, sizeof(tmp));
+#ifdef HAVE_FPCLASSIFY
+ fp_bad = fpclassify(tmp) == FP_NAN;
+#else
+#ifdef HAVE_ISNAN
+ fp_bad = isnan(tmp);
+#endif
+#endif
+ if (!fp_bad) {
+ if (done) fputc(' ', f);
+ fprintf(f, "%*.16g", minwidth, tmp);
+ done = 1;
+ }
+ }
+ if (val->value.pval->vlen == PM_VAL_HDR_SIZE + sizeof(float)) {
+ float tmp;
+ int fp_bad = 0;
+ memcpy((void *)&tmp, (void *)val->value.pval->vbuf, sizeof(tmp));
+#ifdef HAVE_FPCLASSIFY
+ fp_bad = fpclassify(tmp) == FP_NAN;
+#else
+#ifdef HAVE_ISNANF
+ fp_bad = isnanf(tmp);
+#endif
+#endif
+ if (!fp_bad) {
+ if (done) fputc(' ', f);
+ fprintf(f, "%*.8g", minwidth, (double)tmp);
+ done = 1;
+ }
+ }
+ if (val->value.pval->vlen < PM_VAL_HDR_SIZE)
+ fprintf(f, "pmPrintValue: negative length (%d) for aggregate value?",
+ (int)val->value.pval->vlen - PM_VAL_HDR_SIZE);
+ else {
+ string = 1;
+ for (n = 0; n < val->value.pval->vlen - PM_VAL_HDR_SIZE; n++) {
+ if (!isprint((int)val->value.pval->vbuf[n])) {
+ string = 0;
+ break;
+ }
+ }
+ if (string) {
+ if (done) fputc(' ', f);
+ n = (int)val->value.pval->vlen - PM_VAL_HDR_SIZE + 2;
+ while (n < minwidth) {
+ fputc(' ', f);
+ n++;
+ }
+ n = (int)val->value.pval->vlen - PM_VAL_HDR_SIZE;
+ fprintf(f, "\"%*.*s\"", n, n, val->value.pval->vbuf);
+ done = 1;
+ }
+ n = 2 * (val->value.pval->vlen - PM_VAL_HDR_SIZE) + 2;
+ while (n < minwidth) {
+ fputc(' ', f);
+ n++;
+ }
+ if (done) fputc(' ', f);
+ fputc('[', f);
+ p = &val->value.pval->vbuf[0];
+ for (i = 0; i < val->value.pval->vlen - PM_VAL_HDR_SIZE; i++) {
+ fprintf(f, "%02x", *p & 0xff);
+ p++;
+ }
+ fputc(']', f);
+ }
+ }
+ if (type != PM_TYPE_UNKNOWN)
+ free(a.vbp);
+ break;
+
+ case PM_TYPE_EVENT: /* not much we can do about minwidth */
+ case PM_TYPE_HIGHRES_EVENT:
+ if (valfmt == PM_VAL_INSITU) {
+ /*
+ * Special case for pmlc/pmlogger where PMLC_SET_*() macros
+ * used to set control requests / state in the lval field
+ * and the pval does not really contain a valid event record
+ * Code here comes from PrintState() in actions.c from pmlc.
+ */
+ fputs("[pmlc control ", f);
+ fputs(PMLC_GET_MAND(val->value.lval) ? "mand " : "adv ", f);
+ fputs(PMLC_GET_ON(val->value.lval) ? "on " : "off ", f);
+ if (PMLC_GET_INLOG(val->value.lval))
+ fputs(PMLC_GET_AVAIL(val->value.lval) ? " " : "na ", f);
+ else
+ fputs("nl ", f);
+ fprintf(f, "%d]", PMLC_GET_DELTA(val->value.lval));
+ }
+ else
+ print_event_summary(f, val, type != PM_TYPE_EVENT);
+ break;
+
+ case PM_TYPE_NOSUPPORT:
+ fprintf(f, "pmPrintValue: bogus value, metric Not Supported\n");
+ break;
+
+ default:
+ fprintf(f, "pmPrintValue: unknown value type=%d\n", type);
+ }
+}
+
+void
+__pmNoMem(const char *where, size_t size, int fatal)
+{
+ char errmsg[PM_MAXERRMSGLEN];
+ __pmNotifyErr(fatal ? LOG_ERR : LOG_WARNING,
+ "%s: malloc(%d) failed: %s",
+ where, (int)size, osstrerror_r(errmsg, sizeof(errmsg)));
+ if (fatal)
+ exit(1);
+}
+
+/*
+ * this one is used just below the PMAPI to convert platform errors
+ * into more appropriate PMAPI error codes
+ */
+int
+__pmMapErrno(int sts)
+{
+ if (sts == -EBADF || sts == -EPIPE)
+ sts = PM_ERR_IPC;
+#ifdef IS_MINGW
+ else if (sts == -EINVAL)
+ sts = PM_ERR_IPC;
+#endif
+ return sts;
+}
+
+int
+__pmGetTimespec(struct timespec *ts)
+{
+#if defined(IS_DARWIN)
+ clock_serv_t cclock;
+ mach_timespec_t mts;
+
+ host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &cclock);
+ clock_get_time(cclock, &mts);
+ mach_port_deallocate(mach_task_self(), cclock);
+ ts->tv_sec = mts.tv_sec;
+ ts->tv_nsec = mts.tv_nsec;
+ return 0;
+#elif defined(HAVE_CLOCK_GETTIME)
+ return clock_gettime(CLOCK_REALTIME, ts);
+#else
+#warning "No high resolution timestamp support on this platform"
+ struct timeval tv;
+ int sts;
+
+ if ((sts = gettimeofday(&tv, NULL)) == 0) {
+ ts->tv_sec = tv.tv_sec;
+ ts->tv_nsec = tv.tv_usec * 1000;
+ }
+ return sts;
+#endif
+}
+
+/*
+ * difference for two on the internal timestamps
+ */
+double
+__pmTimevalSub(const __pmTimeval *ap, const __pmTimeval *bp)
+{
+ return ap->tv_sec - bp->tv_sec + (double)(ap->tv_usec - bp->tv_usec)/1000000.0;
+}
+
+/*
+ * print timeval timestamp in HH:MM:SS.XXX format
+ */
+void
+__pmPrintStamp(FILE *f, const struct timeval *tp)
+{
+ struct tm tmp;
+ time_t now;
+
+ now = (time_t)tp->tv_sec;
+ pmLocaltime(&now, &tmp);
+ fprintf(f, "%02d:%02d:%02d.%03d", tmp.tm_hour, tmp.tm_min, tmp.tm_sec, (int)(tp->tv_usec/1000));
+}
+
+/*
+ * print high resolution timestamp in HH:MM:SS.XXXXXXXXX format
+ */
+void
+__pmPrintHighResStamp(FILE *f, const struct timespec *tp)
+{
+ struct tm tmp;
+ time_t now;
+
+ now = (time_t)tp->tv_sec;
+ pmLocaltime(&now, &tmp);
+ fprintf(f, "%02d:%02d:%02d.%09d", tmp.tm_hour, tmp.tm_min, tmp.tm_sec, (int)(tp->tv_nsec));
+}
+
+/*
+ * print __pmTimeval timestamp in HH:MM:SS.XXX format
+ * (__pmTimeval variant used in PDUs, archives and internally)
+ */
+void
+__pmPrintTimeval(FILE *f, const __pmTimeval *tp)
+{
+ struct tm tmp;
+ time_t now;
+
+ now = (time_t)tp->tv_sec;
+ pmLocaltime(&now, &tmp);
+ fprintf(f, "%02d:%02d:%02d.%03d", tmp.tm_hour, tmp.tm_min, tmp.tm_sec, tp->tv_usec/1000);
+}
+
+/*
+ * print __pmTimespec timestamp in HH:MM:SS.XXXXXXXXX format
+ * (__pmTimespec variant used in events, archives and internally)
+ */
+void
+__pmPrintTimespec(FILE *f, const __pmTimespec *tp)
+{
+ struct tm tmp;
+ time_t now;
+
+ now = (time_t)tp->tv_sec;
+ pmLocaltime(&now, &tmp);
+ fprintf(f, "%02d:%02d:%02d.%09ld", tmp.tm_hour, tmp.tm_min, tmp.tm_sec, (long)tp->tv_nsec);
+}
+
+/*
+ * descriptor
+ */
+void
+__pmPrintDesc(FILE *f, const pmDesc *desc)
+{
+ const char *type;
+ const char *sem;
+ static const char *unknownVal = "???";
+ const char *units;
+ char strbuf[60];
+
+ if (desc->type == PM_TYPE_NOSUPPORT) {
+ fprintf(f, " Data Type: Not Supported\n");
+ return;
+ }
+
+ switch (desc->type) {
+ case PM_TYPE_32:
+ type = "32-bit int";
+ break;
+ case PM_TYPE_U32:
+ type = "32-bit unsigned int";
+ break;
+ case PM_TYPE_64:
+ type = "64-bit int";
+ break;
+ case PM_TYPE_U64:
+ type = "64-bit unsigned int";
+ break;
+ case PM_TYPE_FLOAT:
+ type = "float";
+ break;
+ case PM_TYPE_DOUBLE:
+ type = "double";
+ break;
+ case PM_TYPE_STRING:
+ type = "string";
+ break;
+ case PM_TYPE_AGGREGATE:
+ type = "aggregate";
+ break;
+ case PM_TYPE_AGGREGATE_STATIC:
+ type = "static aggregate";
+ break;
+ case PM_TYPE_EVENT:
+ type = "event record array";
+ break;
+ case PM_TYPE_HIGHRES_EVENT:
+ type = "highres event record array";
+ break;
+ default:
+ type = unknownVal;
+ break;
+ }
+ fprintf(f, " Data Type: %s", type);
+ if (type == unknownVal)
+ fprintf(f, " (%d)", desc->type);
+
+ fprintf(f," InDom: %s 0x%x\n", pmInDomStr_r(desc->indom, strbuf, sizeof(strbuf)), desc->indom);
+
+ switch (desc->sem) {
+ case PM_SEM_COUNTER:
+ sem = "counter";
+ break;
+ case PM_SEM_INSTANT:
+ sem = "instant";
+ break;
+ case PM_SEM_DISCRETE:
+ sem = "discrete";
+ break;
+ default:
+ sem = unknownVal;
+ break;
+ }
+
+ fprintf(f, " Semantics: %s", sem);
+ if (sem == unknownVal)
+ fprintf(f, " (%d)", desc->sem);
+
+ fprintf(f, " Units: ");
+ units = pmUnitsStr_r(&desc->units, strbuf, sizeof(strbuf));
+ if (*units == '\0')
+ fprintf(f, "none\n");
+ else
+ fprintf(f, "%s\n", units);
+}
+
+/*
+ * print times between events
+ */
+void
+__pmEventTrace_r(const char *event, int *first, double *sum, double *last)
+{
+#ifdef PCP_DEBUG
+ struct timeval tv;
+ double now;
+
+ __pmtimevalNow(&tv);
+ now = (double)tv.tv_sec + (double)tv.tv_usec / 1000000.0;
+ if (*first) {
+ *first = 0;
+ *sum = 0;
+ *last = now;
+ }
+ *sum += now - *last;
+ fprintf(stderr, "%s: +%4.2f = %4.2f -> %s\n",
+ pmProgname, now-*last, *sum, event);
+ *last = now;
+#endif
+}
+
+void
+__pmEventTrace(const char *event)
+{
+#ifdef PCP_DEBUG
+ static double last;
+ static double sum;
+ static int first = 1;
+
+ __pmEventTrace_r(event, &first, &sum, &last);
+#endif
+}
+
+int
+__pmParseDebug(const char *spec)
+{
+#ifdef PCP_DEBUG
+ int val = 0;
+ int tmp;
+ const char *p;
+ char *pend;
+ int i;
+
+ for (p = spec; *p; ) {
+ tmp = (int)strtol(p, &pend, 10);
+ if (tmp == -1)
+ /* special case ... -1 really means set all the bits! */
+ tmp = INT_MAX;
+ if (*pend == '\0') {
+ val |= tmp;
+ break;
+ }
+ else if (*pend == ',') {
+ val |= tmp;
+ p = pend + 1;
+ }
+ else {
+ pend = strchr(p, ',');
+ if (pend != NULL)
+ *pend = '\0';
+
+ if (strcasecmp(p, "ALL") == 0) {
+ val |= INT_MAX;
+ if (pend != NULL) {
+ *pend = ',';
+ p = pend + 1;
+ }
+ else
+ p = ""; /* force termination of outer loop */
+ break;
+ }
+
+ for (i = 0; i < num_debug; i++) {
+ if (strcasecmp(p, debug_map[i].name) == 0) {
+ val |= debug_map[i].bit;
+ if (pend != NULL) {
+ *pend = ',';
+ p = pend + 1;
+ }
+ else
+ p = ""; /* force termination of outer loop */
+ break;
+ }
+ }
+
+ if (i == num_debug) {
+ if (pend != NULL)
+ *pend = ',';
+ return PM_ERR_CONV;
+ }
+ }
+ }
+
+ return val;
+#else
+ return PM_ERR_NYI;
+#endif
+}
+
+int
+__pmGetInternalState(void)
+{
+ return pmState;
+}
+
+void
+__pmSetInternalState(int state)
+{
+ pmState = state;
+}
+
+
+/*
+ * GUI output option
+ */
+
+#define MSGBUFLEN 256
+static FILE *fptr = NULL;
+static int msgsize = 0;
+static char *fname; /* temporary file name for buffering errors */
+static char *ferr; /* error output filename from PCP_STDERR */
+
+#define PM_QUERYERR -1
+#define PM_USEDIALOG 0
+#define PM_USESTDERR 1
+#define PM_USEFILE 2
+
+static int
+pmfstate(int state)
+{
+ static int errtype = -1;
+
+ if (state > PM_QUERYERR)
+ errtype = state;
+
+ PM_INIT_LOCKS();
+ PM_LOCK(__pmLock_libpcp);
+ if (errtype == PM_QUERYERR) {
+ errtype = PM_USESTDERR;
+ if ((ferr = getenv("PCP_STDERR")) != NULL) {
+ if (strcasecmp(ferr, "DISPLAY") == 0) {
+ char * xconfirm = pmGetConfig("PCP_XCONFIRM_PROG");
+ if (access(__pmNativePath(xconfirm), X_OK) < 0) {
+ char errmsg[PM_MAXERRMSGLEN];
+ fprintf(stderr, "%s: using stderr - cannot access %s: %s\n",
+ pmProgname, xconfirm, osstrerror_r(errmsg, sizeof(errmsg)));
+ }
+ else
+ errtype = PM_USEDIALOG;
+ }
+ else if (strcmp(ferr, "") != 0)
+ errtype = PM_USEFILE;
+ }
+ }
+ PM_UNLOCK(__pmLock_libpcp);
+ return errtype;
+}
+
+static int
+vpmprintf(const char *msg, va_list arg)
+{
+ int lsize = 0;
+
+ PM_INIT_LOCKS();
+ PM_LOCK(__pmLock_libpcp);
+ if (fptr == NULL && msgsize == 0) { /* create scratch file */
+ int fd = -1;
+ char *tmpdir = pmGetConfig("PCP_TMPFILE_DIR");
+
+ if (tmpdir[0] != '\0') {
+ mode_t cur_umask;
+
+ /*
+ * PCP_TMPFILE_DIR found in the configuration/environment,
+ * otherwise fall through to the stderr case
+ */
+
+#if HAVE_MKSTEMP
+ fname = (char *)malloc(MAXPATHLEN+1);
+ if (fname == NULL) goto fail;
+ snprintf(fname, MAXPATHLEN, "%s/pcp-XXXXXX", tmpdir);
+ cur_umask = umask(S_IXUSR | S_IRWXG | S_IRWXO);
+ fd = mkstemp(fname);
+ umask(cur_umask);
+#else
+ fname = tempnam(tmpdir, "pcp-");
+ if (fname == NULL) goto fail;
+ cur_umask = umask(S_IXUSR | S_IRWXG | S_IRWXO);
+ fd = open(fname, O_RDWR|O_APPEND|O_CREAT|O_EXCL, 0600);
+ umask(cur_umask);
+#endif /* HAVE_MKSTEMP */
+
+ if (fd < 0) goto fail;
+ if ((fptr = fdopen(fd, "a")) == NULL) {
+ char errmsg[PM_MAXERRMSGLEN];
+fail:
+ if (fname != NULL) {
+ fprintf(stderr, "%s: vpmprintf: failed to create \"%s\": %s\n",
+ pmProgname, fname, osstrerror_r(errmsg, sizeof(errmsg)));
+ unlink(fname);
+ free(fname);
+ }
+ else {
+ fprintf(stderr, "%s: vpmprintf: failed to create temporary file: %s\n",
+ pmProgname, osstrerror_r(errmsg, sizeof(errmsg)));
+ }
+ fprintf(stderr, "vpmprintf msg:\n");
+ if (fd >= 0)
+ close(fd);
+ msgsize = -1;
+ fptr = NULL;
+ }
+ }
+ else
+ msgsize = -1;
+ }
+
+ if (msgsize < 0) {
+ vfprintf(stderr, msg, arg);
+ fflush(stderr);
+ lsize = 0;
+ }
+ else
+ msgsize += (lsize = vfprintf(fptr, msg, arg));
+
+ PM_UNLOCK(__pmLock_libpcp);
+ return lsize;
+}
+
+int
+pmprintf(const char *msg, ...)
+{
+ va_list arg;
+ int lsize;
+
+ va_start(arg, msg);
+ lsize = vpmprintf(msg, arg);
+ va_end(arg);
+ return lsize;
+}
+
+int
+pmflush(void)
+{
+ int sts = 0;
+ int len;
+ int state;
+ FILE *eptr = NULL;
+ char outbuf[MSGBUFLEN];
+
+ PM_INIT_LOCKS();
+ PM_LOCK(__pmLock_libpcp);
+ if (fptr != NULL && msgsize > 0) {
+ fflush(fptr);
+ state = pmfstate(PM_QUERYERR);
+ if (state == PM_USEFILE) {
+ if ((eptr = fopen(ferr, "a")) == NULL) {
+ char errmsg[PM_MAXERRMSGLEN];
+ fprintf(stderr, "pmflush: cannot append to file '%s' (from "
+ "$PCP_STDERR): %s\n", ferr, osstrerror_r(errmsg, sizeof(errmsg)));
+ state = PM_USESTDERR;
+ }
+ }
+ switch (state) {
+ case PM_USESTDERR:
+ rewind(fptr);
+ while ((len = (int)read(fileno(fptr), outbuf, MSGBUFLEN)) > 0) {
+ sts = write(fileno(stderr), outbuf, len);
+ if (sts != len) {
+ char errmsg[PM_MAXERRMSGLEN];
+ fprintf(stderr, "pmflush: write() failed: %s\n",
+ osstrerror_r(errmsg, sizeof(errmsg)));
+ }
+ sts = 0;
+ }
+ break;
+ case PM_USEDIALOG:
+ /* If we're here, it means xconfirm has passed access test */
+ snprintf(outbuf, sizeof(outbuf), "%s -file %s -c -B OK -icon info"
+ " %s -header 'PCP Information' >/dev/null",
+ __pmNativePath(pmGetConfig("PCP_XCONFIRM_PROG")), fname,
+ (msgsize > 80 ? "-useslider" : ""));
+ if (system(outbuf) < 0) {
+ char errmsg[PM_MAXERRMSGLEN];
+ fprintf(stderr, "%s: system failed: %s\n", pmProgname,
+ osstrerror_r(errmsg, sizeof(errmsg)));
+ sts = -oserror();
+ }
+ break;
+ case PM_USEFILE:
+ rewind(fptr);
+ while ((len = (int)read(fileno(fptr), outbuf, MSGBUFLEN)) > 0) {
+ sts = write(fileno(eptr), outbuf, len);
+ if (sts != len) {
+ char errmsg[PM_MAXERRMSGLEN];
+ fprintf(stderr, "pmflush: write() failed: %s\n",
+ osstrerror_r(errmsg, sizeof(errmsg)));
+ }
+ sts = 0;
+ }
+ fclose(eptr);
+ break;
+ }
+ fclose(fptr);
+ fptr = NULL;
+ unlink(fname);
+ free(fname);
+ if (sts >= 0)
+ sts = msgsize;
+ }
+
+ msgsize = 0;
+
+ PM_UNLOCK(__pmLock_libpcp);
+ return sts;
+}
+
+/*
+ * Set the pmcd client identity as exported by pmcd.client.whoami
+ *
+ * Identity is of the form
+ * hostname (ipaddr) <id>
+ *
+ * Assumes you already have a current host context.
+ */
+int
+__pmSetClientId(const char *id)
+{
+ char *name = "pmcd.client.whoami";
+ pmID pmid;
+ int sts;
+ pmResult store = { .numpmid = 1 };
+ pmValueSet pmvs;
+ pmValueBlock *pmvb;
+ char host[MAXHOSTNAMELEN];
+ char *ipaddr = NULL;
+ __pmHostEnt *servInfo;
+ int vblen;
+
+ if ((sts = pmLookupName(1, &name, &pmid)) < 0)
+ return sts;
+
+ /*
+ * Try to obtain the address and the actual host name.
+ * Compute the vblen as we go.
+ */
+ vblen = 0;
+ (void)gethostname(host, MAXHOSTNAMELEN);
+ if ((servInfo = __pmGetAddrInfo(host)) != NULL) {
+ __pmSockAddr *addr;
+ void *enumIx = NULL;
+ char *servInfoName = NULL;
+ for (addr = __pmHostEntGetSockAddr(servInfo, &enumIx);
+ addr != NULL;
+ addr = __pmHostEntGetSockAddr(servInfo, &enumIx)) {
+ servInfoName = __pmGetNameInfo(addr);
+ if (servInfoName != NULL)
+ break;
+ __pmSockAddrFree(addr);
+ }
+ __pmHostEntFree(servInfo);
+
+ /* Did we get a name? */
+ if (servInfoName == NULL) {
+ char errmsg[PM_MAXERRMSGLEN];
+ fprintf(stderr, "__pmSetClientId: __pmGetNameInfo() failed: %s\n",
+ osstrerror_r(errmsg, sizeof(errmsg)));
+ }
+ else {
+ strncpy(host, servInfoName, sizeof(host));
+ host[sizeof(host) - 1] = '\0';
+ free(servInfoName);
+ }
+ vblen = strlen(host) + strlen(id) + 2;
+
+ /* Did we get an address? */
+ if (addr != NULL) {
+ ipaddr = __pmSockAddrToString(addr);
+ __pmSockAddrFree(addr);
+ if (ipaddr == NULL) {
+ char errmsg[PM_MAXERRMSGLEN];
+ fprintf(stderr, "__pmSetClientId: __pmSockAddrToString() failed: %s\n",
+ osstrerror_r(errmsg, sizeof(errmsg)));
+ }
+ else
+ vblen += strlen(ipaddr) + 3;
+ }
+ }
+ vblen += strlen(host) + strlen(id) + 2;
+
+ /* build pmResult for pmStore() */
+ pmvb = (pmValueBlock *)malloc(PM_VAL_HDR_SIZE+vblen);
+ if (pmvb == NULL) {
+ __pmNoMem("__pmSetClientId", PM_VAL_HDR_SIZE+vblen, PM_RECOV_ERR);
+ return -ENOMEM;
+ }
+ pmvb->vtype = PM_TYPE_STRING;
+ pmvb->vlen = PM_VAL_HDR_SIZE+vblen;
+ strcpy(pmvb->vbuf, host);
+ strcat(pmvb->vbuf, " ");
+ if (ipaddr != NULL) {
+ strcat(pmvb->vbuf, "(");
+ strcat(pmvb->vbuf, ipaddr);
+ strcat(pmvb->vbuf, ") ");
+ free(ipaddr);
+ }
+ strcat(pmvb->vbuf, id);
+
+ pmvs.pmid = pmid;
+ pmvs.numval = 1;
+ pmvs.valfmt = PM_VAL_SPTR;
+ pmvs.vlist[0].value.pval = pmvb;
+ pmvs.vlist[0].inst = PM_IN_NULL;
+
+ store.vset[0] = &pmvs;
+ sts = pmStore(&store);
+ free(pmvb);
+ return sts;
+}
+
+char *
+__pmGetClientId(int argc, char **argv)
+{
+ char *clientID;
+ int a, need = 0;
+
+ for (a = 0; a < argc; a++)
+ need += strlen(argv[a]) + 1;
+ clientID = (char *)malloc(need);
+ if (clientID) {
+ clientID[0] = '\0';
+ for (a = 0; a < argc; a++) {
+ strcat(clientID, argv[a]);
+ if (a < argc - 1)
+ strcat(clientID, " ");
+ }
+ }
+ return clientID;
+}
+
+int
+__pmSetClientIdArgv(int argc, char **argv)
+{
+ char *id = __pmGetClientId(argc, argv);
+ int sts;
+
+ if (id) {
+ sts = __pmSetClientId(id);
+ free(id);
+ return sts;
+ }
+ return -ENOMEM;
+}
+
+/*
+ * Support for C environments that have lame libc implementations.
+ * All of these developed from first principles, so no 3rd party
+ * copyright or licensing issues, else used under a licence that
+ * is compatible with the PCP licence.
+ */
+
+#ifndef HAVE_BASENAME
+char *
+basename(char *name)
+{
+ char *p = strrchr(name, '/');
+
+ if (p == NULL)
+ return(name);
+ else
+ return(p+1);
+}
+#endif /* HAVE_BASENAME */
+
+#ifndef HAVE_DIRNAME
+char *
+dirname(char *name)
+{
+ char *p = strrchr(name, '/');
+
+ if (p == NULL)
+ return(".");
+ else {
+ *p = '\0';
+ return(name);
+ }
+}
+#endif /* HAVE_DIRNAME */
+
+/*
+ * Create a directory, including all of its path components.
+ */
+int
+__pmMakePath(const char *dir, mode_t mode)
+{
+ char path[MAXPATHLEN], *p;
+ int sts;
+
+ sts = access(dir, R_OK|W_OK|X_OK);
+ if (sts == 0)
+ return 0;
+ if (sts < 0 && oserror() != ENOENT)
+ return -1;
+
+ strncpy(path, dir, sizeof(path));
+ path[sizeof(path)-1] = '\0';
+
+ for (p = path+1; *p != '\0'; p++) {
+ if (*p == __pmPathSeparator()) {
+ *p = '\0';
+ mkdir2(path, mode);
+ *p = __pmPathSeparator();
+ }
+ }
+ return mkdir2(path, mode);
+}
+
+#ifndef HAVE_STRNDUP
+char *
+strndup(const char *s, size_t n)
+{
+ char *buf;
+
+ if ((buf = malloc(n + 1)) != NULL) {
+ strncpy(buf, s, n);
+ buf[n] = '\0';
+ }
+ return buf;
+}
+#endif /* HAVE_STRNDUP */
+
+#ifndef HAVE_STRCHRNUL
+char *
+strchrnul(const char *s, int c)
+{
+ char *result;
+
+ if ((result = strchr(s, c)) == NULL)
+ result = strchr(s, '\0');
+ return result;
+}
+#endif /* HAVE_STRCHRNUL */
+
+#ifndef HAVE_SCANDIR
+/*
+ * Scan the directory dirname, building an array of pointers to
+ * dirent entries using malloc(3C). select() and compare() are
+ * used to optionally filter and sort directory entries.
+ */
+int
+scandir(const char *dirname, struct dirent ***namelist,
+ int(*select)(const_dirent *),
+ int(*compare)(const_dirent **, const_dirent **))
+{
+ DIR *dirp;
+ int n = 0;
+ struct dirent **names = NULL;
+ struct dirent *dp;
+ struct dirent *tp;
+
+ PM_INIT_LOCKS();
+ PM_LOCK(__pmLock_libpcp);
+ if ((dirp = opendir(dirname)) == NULL)
+ return -1;
+
+ while ((dp = readdir(dirp)) != NULL) {
+ if (select && (*select)(dp) == 0)
+ continue;
+
+ n++;
+ if ((names = (struct dirent **)realloc(names, n * sizeof(dp))) == NULL) {
+ PM_UNLOCK(__pmLock_libpcp);
+ closedir(dirp);
+ return -1;
+ }
+
+ if ((names[n-1] = tp = (struct dirent *)malloc(
+ sizeof(*dp)-sizeof(dp->d_name)+strlen(dp->d_name)+1)) == NULL) {
+ PM_UNLOCK(__pmLock_libpcp);
+ closedir(dirp);
+ return -1;
+ }
+
+ tp->d_ino = dp->d_ino;
+#if defined(HAVE_DIRENT_D_OFF)
+ tp->d_off = dp->d_off;
+#else
+ tp->d_reclen = dp->d_reclen;
+#endif
+ memcpy(tp->d_name, dp->d_name, strlen(dp->d_name)+1);
+ }
+ closedir(dirp);
+ PM_UNLOCK(__pmLock_libpcp);
+ *namelist = names;
+
+ if (n && compare)
+ qsort(names, n, sizeof(names[0]),
+ (int(*)(const void *, const void *))compare);
+ return n;
+}
+
+/*
+ * Alphabetical sort for default use
+ */
+int
+alphasort(const_dirent **p, const_dirent **q)
+{
+ return strcmp((*p)->d_name, (*q)->d_name);
+}
+#endif /* HAVE_SCANDIR */
+
+#ifndef HAVE_POW
+/*
+ * For PCP we have not found a platform yet that needs this, but just
+ * in case, this implementation comes from
+ * http://www.netlib.org/fdlibm/e_pow.c
+ *
+ * ====================================================
+ * Copyright (C) 2003 by Sun Microsystems, Inc. All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this
+ * software is freely granted, provided that this notice
+ * is preserved.
+ * ====================================================
+ */
+
+#ifdef HAVE_ENDIAN_H
+#include <endian.h>
+#else
+#ifdef HAVE_SYS_ENDIAN_H
+#include <sys/endian.h>
+#else
+bozo!
+#endif
+#endif
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+#define __HI(x) *(1+(int*)&x)
+#define __LO(x) *(int*)&x
+#define __HIp(x) *(1+(int*)x)
+#define __LOp(x) *(int*)x
+#else
+#define __HI(x) *(int*)&x
+#define __LO(x) *(1+(int*)&x)
+#define __HIp(x) *(int*)x
+#define __LOp(x) *(1+(int*)x)
+#endif
+
+/* _pow(x,y) return x**y
+ *
+ * n
+ * Method: Let x = 2 * (1+f)
+ * 1. Compute and return log2(x) in two pieces:
+ * log2(x) = w1 + w2,
+ * where w1 has 53-24 = 29 bit trailing zeros.
+ * 2. Perform y*log2(x) = n+y' by simulating muti-precision
+ * arithmetic, where |y'|<=0.5.
+ * 3. Return x**y = 2**n*exp(y'*log2)
+ *
+ * Special cases:
+ * 1. (anything) ** 0 is 1
+ * 2. (anything) ** 1 is itself
+ * 3. (anything) ** NAN is NAN
+ * 4. NAN ** (anything except 0) is NAN
+ * 5. +-(|x| > 1) ** +INF is +INF
+ * 6. +-(|x| > 1) ** -INF is +0
+ * 7. +-(|x| < 1) ** +INF is +0
+ * 8. +-(|x| < 1) ** -INF is +INF
+ * 9. +-1 ** +-INF is NAN
+ * 10. +0 ** (+anything except 0, NAN) is +0
+ * 11. -0 ** (+anything except 0, NAN, odd integer) is +0
+ * 12. +0 ** (-anything except 0, NAN) is +INF
+ * 13. -0 ** (-anything except 0, NAN, odd integer) is +INF
+ * 14. -0 ** (odd integer) = -( +0 ** (odd integer) )
+ * 15. +INF ** (+anything except 0,NAN) is +INF
+ * 16. +INF ** (-anything except 0,NAN) is +0
+ * 17. -INF ** (anything) = -0 ** (-anything)
+ * 18. (-anything) ** (integer) is (-1)**(integer)*(+anything**integer)
+ * 19. (-anything except 0 and inf) ** (non-integer) is NAN
+ *
+ * Accuracy:
+ * pow(x,y) returns x**y nearly rounded. In particular
+ * pow(integer,integer)
+ * always returns the correct integer provided it is
+ * representable.
+ *
+ * Constants :
+ * The hexadecimal values are the intended ones for the following
+ * constants. The decimal values may be used, provided that the
+ * compiler will convert from decimal to binary accurately enough
+ * to produce the hexadecimal values shown.
+ */
+
+static const double
+bp[] = {1.0, 1.5,},
+dp_h[] = { 0.0, 5.84962487220764160156e-01,}, /* 0x3FE2B803, 0x40000000 */
+dp_l[] = { 0.0, 1.35003920212974897128e-08,}, /* 0x3E4CFDEB, 0x43CFD006 */
+zero = 0.0,
+one = 1.0,
+two = 2.0,
+two53 = 9007199254740992.0, /* 0x43400000, 0x00000000 */
+huge = 1.0e300,
+tiny = 1.0e-300,
+ /* poly coefs for (3/2)*(log(x)-2s-2/3*s**3 */
+L1 = 5.99999999999994648725e-01, /* 0x3FE33333, 0x33333303 */
+L2 = 4.28571428578550184252e-01, /* 0x3FDB6DB6, 0xDB6FABFF */
+L3 = 3.33333329818377432918e-01, /* 0x3FD55555, 0x518F264D */
+L4 = 2.72728123808534006489e-01, /* 0x3FD17460, 0xA91D4101 */
+L5 = 2.30660745775561754067e-01, /* 0x3FCD864A, 0x93C9DB65 */
+L6 = 2.06975017800338417784e-01, /* 0x3FCA7E28, 0x4A454EEF */
+P1 = 1.66666666666666019037e-01, /* 0x3FC55555, 0x5555553E */
+P2 = -2.77777777770155933842e-03, /* 0xBF66C16C, 0x16BEBD93 */
+P3 = 6.61375632143793436117e-05, /* 0x3F11566A, 0xAF25DE2C */
+P4 = -1.65339022054652515390e-06, /* 0xBEBBBD41, 0xC5D26BF1 */
+P5 = 4.13813679705723846039e-08, /* 0x3E663769, 0x72BEA4D0 */
+lg2 = 6.93147180559945286227e-01, /* 0x3FE62E42, 0xFEFA39EF */
+lg2_h = 6.93147182464599609375e-01, /* 0x3FE62E43, 0x00000000 */
+lg2_l = -1.90465429995776804525e-09, /* 0xBE205C61, 0x0CA86C39 */
+ovt = 8.0085662595372944372e-0017, /* -(1024-log2(ovfl+.5ulp)) */
+cp = 9.61796693925975554329e-01, /* 0x3FEEC709, 0xDC3A03FD =2/(3ln2) */
+cp_h = 9.61796700954437255859e-01, /* 0x3FEEC709, 0xE0000000 =(float)cp */
+cp_l = -7.02846165095275826516e-09, /* 0xBE3E2FE0, 0x145B01F5 =tail of cp_h*/
+ivln2 = 1.44269504088896338700e+00, /* 0x3FF71547, 0x652B82FE =1/ln2 */
+ivln2_h = 1.44269502162933349609e+00, /* 0x3FF71547, 0x60000000 =24b 1/ln2*/
+ivln2_l = 1.92596299112661746887e-08; /* 0x3E54AE0B, 0xF85DDF44 =1/ln2 tail*/
+
+double
+pow(double x, double y)
+{
+ double z,ax,z_h,z_l,p_h,p_l;
+ double y1,t1,t2,r,s,t,u,v,w;
+ int i,j,k,yisint,n;
+ int hx,hy,ix,iy;
+ unsigned lx,ly;
+
+ hx = __HI(x); lx = __LO(x);
+ hy = __HI(y); ly = __LO(y);
+ ix = hx&0x7fffffff; iy = hy&0x7fffffff;
+
+ /* y==zero: x**0 = 1 */
+ if((iy|ly)==0) return one;
+
+ /* +-NaN return x+y */
+ if(ix > 0x7ff00000 || ((ix==0x7ff00000)&&(lx!=0)) ||
+ iy > 0x7ff00000 || ((iy==0x7ff00000)&&(ly!=0)))
+ return x+y;
+
+ /* determine if y is an odd int when x < 0
+ * yisint = 0 ... y is not an integer
+ * yisint = 1 ... y is an odd int
+ * yisint = 2 ... y is an even int
+ */
+ yisint = 0;
+ if(hx<0) {
+ if(iy>=0x43400000) yisint = 2; /* even integer y */
+ else if(iy>=0x3ff00000) {
+ k = (iy>>20)-0x3ff; /* exponent */
+ if(k>20) {
+ j = ly>>(52-k);
+ if((j<<(52-k))==ly) yisint = 2-(j&1);
+ } else if(ly==0) {
+ j = iy>>(20-k);
+ if((j<<(20-k))==iy) yisint = 2-(j&1);
+ }
+ }
+ }
+
+ /* special value of y */
+ if(ly==0) {
+ if (iy==0x7ff00000) { /* y is +-inf */
+ if(((ix-0x3ff00000)|lx)==0)
+ return y - y; /* inf**+-1 is NaN */
+ else if (ix >= 0x3ff00000)/* (|x|>1)**+-inf = inf,0 */
+ return (hy>=0)? y: zero;
+ else /* (|x|<1)**-,+inf = inf,0 */
+ return (hy<0)?-y: zero;
+ }
+ if(iy==0x3ff00000) { /* y is +-1 */
+ if(hy<0) return one/x; else return x;
+ }
+ if(hy==0x40000000) return x*x; /* y is 2 */
+ if(hy==0x3fe00000) { /* y is 0.5 */
+ if(hx>=0) /* x >= +0 */
+ return sqrt(x);
+ }
+ }
+
+ ax = fabs(x);
+ /* special value of x */
+ if(lx==0) {
+ if(ix==0x7ff00000||ix==0||ix==0x3ff00000){
+ z = ax; /*x is +-0,+-inf,+-1*/
+ if(hy<0) z = one/z; /* z = (1/|x|) */
+ if(hx<0) {
+ if(((ix-0x3ff00000)|yisint)==0) {
+ z = (z-z)/(z-z); /* (-1)**non-int is NaN */
+ } else if(yisint==1)
+ z = -z; /* (x<0)**odd = -(|x|**odd) */
+ }
+ return z;
+ }
+ }
+
+ n = (hx>>31)+1;
+
+ /* (x<0)**(non-int) is NaN */
+ if((n|yisint)==0) return (x-x)/(x-x);
+
+ s = one; /* s (sign of result -ve**odd) = -1 else = 1 */
+ if((n|(yisint-1))==0) s = -one;/* (-ve)**(odd int) */
+
+ /* |y| is huge */
+ if(iy>0x41e00000) { /* if |y| > 2**31 */
+ if(iy>0x43f00000){ /* if |y| > 2**64, must o/uflow */
+ if(ix<=0x3fefffff) return (hy<0)? huge*huge:tiny*tiny;
+ if(ix>=0x3ff00000) return (hy>0)? huge*huge:tiny*tiny;
+ }
+ /* over/underflow if x is not close to one */
+ if(ix<0x3fefffff) return (hy<0)? s*huge*huge:s*tiny*tiny;
+ if(ix>0x3ff00000) return (hy>0)? s*huge*huge:s*tiny*tiny;
+ /* now |1-x| is tiny <= 2**-20, suffice to compute
+ log(x) by x-x^2/2+x^3/3-x^4/4 */
+ t = ax-one; /* t has 20 trailing zeros */
+ w = (t*t)*(0.5-t*(0.3333333333333333333333-t*0.25));
+ u = ivln2_h*t; /* ivln2_h has 21 sig. bits */
+ v = t*ivln2_l-w*ivln2;
+ t1 = u+v;
+ __LO(t1) = 0;
+ t2 = v-(t1-u);
+ } else {
+ double ss,s2,s_h,s_l,t_h,t_l;
+ n = 0;
+ /* take care subnormal number */
+ if(ix<0x00100000)
+ {ax *= two53; n -= 53; ix = __HI(ax); }
+ n += ((ix)>>20)-0x3ff;
+ j = ix&0x000fffff;
+ /* determine interval */
+ ix = j|0x3ff00000; /* normalize ix */
+ if(j<=0x3988E) k=0; /* |x|<sqrt(3/2) */
+ else if(j<0xBB67A) k=1; /* |x|<sqrt(3) */
+ else {k=0;n+=1;ix -= 0x00100000;}
+ __HI(ax) = ix;
+
+ /* compute ss = s_h+s_l = (x-1)/(x+1) or (x-1.5)/(x+1.5) */
+ u = ax-bp[k]; /* bp[0]=1.0, bp[1]=1.5 */
+ v = one/(ax+bp[k]);
+ ss = u*v;
+ s_h = ss;
+ __LO(s_h) = 0;
+ /* t_h=ax+bp[k] High */
+ t_h = zero;
+ __HI(t_h)=((ix>>1)|0x20000000)+0x00080000+(k<<18);
+ t_l = ax - (t_h-bp[k]);
+ s_l = v*((u-s_h*t_h)-s_h*t_l);
+ /* compute log(ax) */
+ s2 = ss*ss;
+ r = s2*s2*(L1+s2*(L2+s2*(L3+s2*(L4+s2*(L5+s2*L6)))));
+ r += s_l*(s_h+ss);
+ s2 = s_h*s_h;
+ t_h = 3.0+s2+r;
+ __LO(t_h) = 0;
+ t_l = r-((t_h-3.0)-s2);
+ /* u+v = ss*(1+...) */
+ u = s_h*t_h;
+ v = s_l*t_h+t_l*ss;
+ /* 2/(3log2)*(ss+...) */
+ p_h = u+v;
+ __LO(p_h) = 0;
+ p_l = v-(p_h-u);
+ z_h = cp_h*p_h; /* cp_h+cp_l = 2/(3*log2) */
+ z_l = cp_l*p_h+p_l*cp+dp_l[k];
+ /* log2(ax) = (ss+..)*2/(3*log2) = n + dp_h + z_h + z_l */
+ t = (double)n;
+ t1 = (((z_h+z_l)+dp_h[k])+t);
+ __LO(t1) = 0;
+ t2 = z_l-(((t1-t)-dp_h[k])-z_h);
+ }
+
+ /* split up y into y1+y2 and compute (y1+y2)*(t1+t2) */
+ y1 = y;
+ __LO(y1) = 0;
+ p_l = (y-y1)*t1+y*t2;
+ p_h = y1*t1;
+ z = p_l+p_h;
+ j = __HI(z);
+ i = __LO(z);
+ if (j>=0x40900000) { /* z >= 1024 */
+ if(((j-0x40900000)|i)!=0) /* if z > 1024 */
+ return s*huge*huge; /* overflow */
+ else {
+ if(p_l+ovt>z-p_h) return s*huge*huge; /* overflow */
+ }
+ } else if((j&0x7fffffff)>=0x4090cc00 ) { /* z <= -1075 */
+ if(((j-0xc090cc00)|i)!=0) /* z < -1075 */
+ return s*tiny*tiny; /* underflow */
+ else {
+ if(p_l<=z-p_h) return s*tiny*tiny; /* underflow */
+ }
+ }
+ /*
+ * compute 2**(p_h+p_l)
+ */
+ i = j&0x7fffffff;
+ k = (i>>20)-0x3ff;
+ n = 0;
+ if(i>0x3fe00000) { /* if |z| > 0.5, set n = [z+0.5] */
+ n = j+(0x00100000>>(k+1));
+ k = ((n&0x7fffffff)>>20)-0x3ff; /* new k for n */
+ t = zero;
+ __HI(t) = (n&~(0x000fffff>>k));
+ n = ((n&0x000fffff)|0x00100000)>>(20-k);
+ if(j<0) n = -n;
+ p_h -= t;
+ }
+ t = p_l+p_h;
+ __LO(t) = 0;
+ u = t*lg2_h;
+ v = (p_l-(t-p_h))*lg2+t*lg2_l;
+ z = u+v;
+ w = v-(z-u);
+ t = z*z;
+ t1 = z - t*(P1+t*(P2+t*(P3+t*(P4+t*P5))));
+ r = (z*t1)/(t1-two)-(w+z*w);
+ z = one-(r-z);
+ j = __HI(z);
+ j += (n<<20);
+ if((j>>20)<=0) z = scalbn(z,n); /* subnormal output */
+ else __HI(z) += (n<<20);
+ return s*z;
+}
+#endif /* HAVE_POW */
+
+#define PROCFS_ENTRY_SIZE 40 /* encompass any size of entry for pid */
+
+#if defined(IS_DARWIN) /* No procfs on Mac OS X */
+int
+__pmProcessExists(pid_t pid)
+{
+ struct kinfo_proc kp;
+ size_t len = sizeof(kp);
+ int mib[4];
+
+ mib[0] = CTL_KERN;
+ mib[1] = KERN_PROC;
+ mib[2] = KERN_PROC_PID;
+ mib[3] = pid;
+ if (sysctl(mib, 4, &kp, &len, NULL, 0) == -1)
+ return 0;
+ return (len > 0);
+}
+#elif defined(IS_FREEBSD)
+int
+__pmProcessExists(pid_t pid)
+{
+ /*
+ * kill(.., 0) returns -1 if the process exists.
+ */
+ if (kill(pid, 0) == -1)
+ return 1;
+ else
+ return 0;
+}
+#elif defined(HAVE_PROCFS)
+#define PROCFS "/proc"
+#define PROCFS_PATH_SIZE (sizeof(PROCFS)+PROCFS_ENTRY_SIZE)
+int
+__pmProcessExists(pid_t pid)
+{
+ char proc_buf[PROCFS_PATH_SIZE];
+ snprintf(proc_buf, sizeof(proc_buf), "%s/%" FMT_PID, PROCFS, pid);
+ return (access(proc_buf, F_OK) == 0);
+}
+#elif !defined(IS_MINGW)
+!bozo!
+#endif
+
+#if defined(HAVE_KILL)
+int
+__pmProcessTerminate(pid_t pid, int force)
+{
+ return kill(pid, force ? SIGKILL : SIGTERM);
+}
+#elif !defined(IS_MINGW)
+!bozo!
+#endif
+
+#if defined(HAVE_SBRK)
+int
+__pmProcessDataSize(unsigned long *size)
+{
+ static void *base;
+
+ if (size && base)
+ *size = (sbrk(0) - base) / 1024;
+ else {
+ base = sbrk(0);
+ if (size)
+ *size = 0;
+ }
+ return 0;
+}
+#elif !defined(IS_MINGW)
+#warning "Platform does not define a process datasize interface?"
+int __pmProcessDataSize(unsigned long *) { return -1; }
+#endif
+
+#if !defined(IS_MINGW)
+int
+__pmProcessRunTimes(double *usr, double *sys)
+{
+ struct tms tms;
+ double ticks = (double)sysconf(_SC_CLK_TCK);
+
+ if (times(&tms) == (clock_t)-1) {
+ *usr = *sys = 0.0;
+ return -1;
+ }
+ *usr = (double)tms.tms_utime / ticks;
+ *sys = (double)tms.tms_stime / ticks;
+ return 0;
+}
+#endif
+
+#if !defined(IS_MINGW)
+pid_t
+__pmProcessCreate(char **argv, int *infd, int *outfd)
+{
+ int in[2];
+ int out[2];
+ pid_t pid;
+
+ if (pipe1(in) < 0)
+ return -oserror();
+ if (pipe1(out) < 0)
+ return -oserror();
+
+ pid = fork();
+ if (pid < 0) {
+ return -1;
+ }
+ else if (pid) {
+ /* parent */
+ close(in[0]);
+ close(out[1]);
+ *infd = out[0];
+ *outfd = in[1];
+ }
+ else {
+ /* child */
+ char errmsg[PM_MAXERRMSGLEN];
+ close(in[1]);
+ close(out[0]);
+ if (in[0] != 0) {
+ close(0);
+ dup2(in[0], 0);
+ close(in[0]);
+ }
+ if (out[1] != 1) {
+ close(1);
+ dup2(out[1], 1);
+ close(out[1]);
+ }
+ execvp(argv[0], argv);
+ fprintf(stderr, "execvp: %s\n", osstrerror_r(errmsg, sizeof(errmsg)));
+ exit(1);
+ }
+ return pid;
+}
+
+int
+__pmSetSignalHandler(int sig, __pmSignalHandler func)
+{
+ signal(sig, func);
+ return 0;
+}
+
+int
+__pmSetProgname(const char *program)
+{
+ char *p;
+
+ /* Trim command name of leading directory components */
+ if (program)
+ pmProgname = (char *)program;
+ for (p = pmProgname; pmProgname && *p; p++) {
+ if (*p == '/')
+ pmProgname = p+1;
+ }
+ return 0;
+}
+
+int
+__pmShutdown(void)
+{
+ int code = 0, sts;
+
+ if ((sts = __pmShutdownLocal()) < 0 && !code)
+ code = sts;
+ if ((sts = __pmShutdownCertificates()) < 0 && !code)
+ code = sts;
+ if ((sts = __pmShutdownSecureSockets()) < 0 && !code)
+ code = sts;
+ return code;
+}
+
+void *
+__pmMemoryMap(int fd, size_t sz, int writable)
+{
+ int mflags = writable ? (PROT_READ | PROT_WRITE) : PROT_READ;
+ void *addr = mmap(NULL, sz, mflags, MAP_SHARED, fd, 0);
+ if (addr == MAP_FAILED)
+ return NULL;
+ return addr;
+}
+
+void
+__pmMemoryUnmap(void *addr, size_t sz)
+{
+ munmap(addr, sz);
+}
+
+#if HAVE_TRACE_BACK_STACK
+#include <libexc.h>
+#define MAX_DEPTH 30 /* max callback procedure depth */
+#define MAX_SIZE 48 /* max function name length */
+
+void
+__pmDumpStack(FILE *f)
+{
+ __uint64_t call_addr[MAX_DEPTH];
+ char *call_fn[MAX_DEPTH];
+ char names[MAX_DEPTH][MAX_SIZE];
+ int res;
+ int i;
+
+ for (i = 0; i < MAX_DEPTH; i++)
+ call_fn[i] = names[i];
+ res = trace_back_stack(MAX_DEPTH, call_addr, call_fn, MAX_DEPTH, MAX_SIZE);
+ for (i = 1; i < res; i++) {
+#if defined(HAVE_64BIT_PTR)
+ fprintf(f, " 0x%016llx [%s]\n", call_addr[i], call_fn[i]);
+#else
+ fprintf(f, " 0x%08lx [%s]\n", (__uint32_t)call_addr[i], call_fn[i]);
+#endif
+ }
+}
+
+#elif HAVE_BACKTRACE
+#include <execinfo.h>
+#define MAX_DEPTH 30 /* max callback procedure depth */
+
+void
+__pmDumpStack(FILE *f)
+{
+ int nframe;
+ void *buf[MAX_DEPTH];
+ char **symbols;
+ int i;
+
+ nframe = backtrace(buf, MAX_DEPTH);
+ if (nframe < 1) {
+ fprintf(f, "backtrace -> %d frames?\n", nframe);
+ return;
+ }
+ symbols = backtrace_symbols(buf, nframe);
+ if (symbols == NULL) {
+ fprintf(f, "backtrace_symbols failed!\n");
+ return;
+ }
+ for (i = 1; i < nframe; i++)
+ fprintf(f, " " PRINTF_P_PFX "%p [%s]\n", buf[i], symbols[i]);
+}
+#else /* no known mechanism, provide a stub (called unconditionally) */
+void
+__pmDumpStack(FILE *f)
+{
+ fprintf(f, "[No backtrace support available]\n");
+}
+#endif /* HAVE_BACKTRACE */
+
+#endif /* !IS_MINGW */