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  	    }
Event negative_returns: "dupoldfd" is passed to a parameter that cannot be negative.
Also see events: [negative_return_fn][var_assign]
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