1    	/*
2    	 * Copyright (c) 1995-2002,2004 Silicon Graphics, Inc.  All Rights Reserved.
3    	 * 
4    	 * This library is free software; you can redistribute it and/or modify it
5    	 * under the terms of the GNU Lesser General Public License as published
6    	 * by the Free Software Foundation; either version 2.1 of the License, or
7    	 * (at your option) any later version.
8    	 * 
9    	 * This library is distributed in the hope that it will be useful, but
10   	 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11   	 * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
12   	 * License for more details.
13   	 */
14   	
15   	#include <inttypes.h>
16   	#include <sys/stat.h>
17   	#include "pmapi.h"
18   	#include "impl.h"
19   	#if defined(HAVE_SYS_WAIT_H)
20   	#include <sys/wait.h>
21   	#endif
22   	
23   	INTERN int	__pmLogReads = 0;
24   	
25   	static char	*logfilename;
26   	static int	logfilenamelen;
27   	static int	seeking_end;
28   	
29   	/*
30   	 * Suffixes and associated compresssion application for compressed filenames.
31   	 * These can appear _after_ the volume number in the name of a file for an
32   	 * archive metric log file, e.g. /var/log/pmlogger/myhost/20101219.0.bz2
33   	 */
34   	#define	USE_NONE	0
35   	#define	USE_BZIP2	1
36   	#define USE_GZIP	2
37   	static struct {
38   	    const char	*suff;
39   	    int		appl;
40   	} compress_ctl[] = {
41   	    { ".bz2",	USE_BZIP2 },
42   	    { ".bz",	USE_BZIP2 },
43   	    { ".gz",	USE_GZIP },
44   	    { ".Z",	USE_GZIP },
45   	    { ".z",	USE_GZIP }
46   	};
47   	static int	ncompress = sizeof(compress_ctl) / sizeof(compress_ctl[0]);
48   	
49   	/*
50   	 * first two fields are made to look like a pmValueSet when no values are
51   	 * present ... used to populate the pmValueSet in a pmResult when values
52   	 * for particular metrics are not available from this log record.
53   	 */
54   	typedef struct {
55   	    pmID	pc_pmid;
56   	    int		pc_numval;	/* MUST be 0 */
57   	    				/* value control for interpolation */
58   	} pmid_ctl;
59   	
60   	static __pmHashCtl	pc_hc;		/* hash control for requested metrics */
61   	
62   	#ifdef PCP_DEBUG
63   	static void
64   	printstamp(struct timeval *tp)
65   	{
66   	    static struct tm	*tmp;
67   	    time_t t = (time_t)tp->tv_sec;
68   	
69   	    tmp = localtime(&t);
70   	    fprintf(stderr, "%02d:%02d:%02d.%03d", tmp->tm_hour, tmp->tm_min, tmp->tm_sec, (int)(tp->tv_usec/1000));
71   	}
72   	
73   	static void
74   	printstamp32(__pmTimeval *tp)
75   	{
76   	    static struct tm	*tmp;
77   	    time_t		time;
78   	
79   	    time = tp->tv_sec;			/* let compiler sort out alignment */
80   	    tmp = localtime(&time);
81   	    fprintf(stderr, "%02d:%02d:%02d.%03d", tmp->tm_hour, tmp->tm_min, tmp->tm_sec, tp->tv_usec/1000);
82   	}
83   	
84   	static void
85   	dumpbuf(int nch, __pmPDU *pb)
86   	{
87   	    int		i, j;
88   	
89   	    nch /= sizeof(__pmPDU);
90   	    fprintf(stderr, "%03d: ", 0);
91   	    for (j = 0, i = 0; j < nch; j++) {
92   		if (i == 8) {
93   		    fprintf(stderr, "\n%03d: ", j);
94   		    i = 0;
95   		}
96   		fprintf(stderr, "%8x ", pb[j]);
97   		i++;
98   	    }
99   	    fputc('\n', stderr);
100  	}
101  	#endif
102  	
103  	int
104  	__pmLogChkLabel(__pmLogCtl *lcp, FILE *f, __pmLogLabel *lp, int vol)
105  	{
106  	    int		len;
107  	    int		version = UNKNOWN_VERSION;
108  	    int		xpectlen = sizeof(__pmLogLabel) + 2 * sizeof(len);
109  	    int		n;
110  	
111  	    if (vol >= 0 && vol < lcp->l_numseen && lcp->l_seen[vol]) {
112  		/* FastPath, cached result of previous check for this volume */
113  		fseek(f, (long)(sizeof(__pmLogLabel) + 2*sizeof(int)), SEEK_SET);
114  		return 0;
115  	    }
116  	
117  	    if (vol >= 0 && vol >= lcp->l_numseen) {
118  		lcp->l_seen = (int *)realloc(lcp->l_seen, (vol+1)*(int)sizeof(lcp->l_seen[0]));
119  		if (lcp->l_seen == NULL)
120  		    lcp->l_numseen = 0;
121  		else {
122  		    int 	i;
123  		    for (i = lcp->l_numseen; i < vol; i++)
124  			lcp->l_seen[i] = 0;
125  		    lcp->l_numseen = vol+1;
126  		}
127  	    }
128  	
129  	#ifdef PCP_DEBUG
130  	    if (pmDebug & DBG_TRACE_LOG)
131  		fprintf(stderr, "__pmLogChkLabel: fd=%d vol=%d", fileno(f), vol);
132  	#endif
133  	
134  	    fseek(f, (long)0, SEEK_SET);
135  	    n = (int)fread(&len, 1, sizeof(len), f);
136  	    len = ntohl(len);
137  	    if (n != sizeof(len) || len != xpectlen) {
138  		if (feof(f)) {
139  		    clearerr(f);
140  	#ifdef PCP_DEBUG
141  		    if (pmDebug & DBG_TRACE_LOG)
142  			fprintf(stderr, " file is empty\n");
143  	#endif
144  		    return PM_ERR_NODATA;
145  		}
146  		else {
147  	#ifdef PCP_DEBUG
148  		    if (pmDebug & DBG_TRACE_LOG)
149  			fprintf(stderr, " header read -> %d (expect %d) or bad header len=%d (expected %d)\n",
150  			    n, (int)sizeof(len), len, xpectlen);
151  	#endif
152  		    if (ferror(f)) {
153  			clearerr(f);
154  			return -oserror();
155  		    }
156  		    else
157  			return PM_ERR_LABEL;
158  		}
159  	    }
160  	
161  	    if ((n = (int)fread(lp, 1, sizeof(__pmLogLabel), f)) != sizeof(__pmLogLabel)) {
162  	#ifdef PCP_DEBUG
163  		if (pmDebug & DBG_TRACE_LOG)
164  		    fprintf(stderr, " bad label len=%d: expected %d\n",
165  			n, (int)sizeof(__pmLogLabel));
166  	#endif
167  		if (ferror(f)) {
168  		    clearerr(f);
169  		    return -oserror();
170  		}
171  		else
172  		    return PM_ERR_LABEL;
173  	    }
174  	    else {
175  		/* swab internal log label */
176  		lp->ill_magic = ntohl(lp->ill_magic);
177  		lp->ill_pid = ntohl(lp->ill_pid);
178  		lp->ill_start.tv_sec = ntohl(lp->ill_start.tv_sec);
179  		lp->ill_start.tv_usec = ntohl(lp->ill_start.tv_usec);
180  		lp->ill_vol = ntohl(lp->ill_vol);
181  	    }
182  	
183  	    n = (int)fread(&len, 1, sizeof(len), f);
184  	    len = ntohl(len);
185  	    if (n != sizeof(len) || len != xpectlen) {
186  	#ifdef PCP_DEBUG
187  		if (pmDebug & DBG_TRACE_LOG)
188  		    fprintf(stderr, " trailer read -> %d (expect %d) or bad trailer len=%d (expected %d)\n",
189  			n, (int)sizeof(len), len, xpectlen);
190  	#endif
191  		if (ferror(f)) {
192  		    clearerr(f);
193  		    return -oserror();
194  		}
195  		else
196  		    return PM_ERR_LABEL;
197  	    }
198  	
199  	    version = lp->ill_magic & 0xff;
200  	    if ((lp->ill_magic & 0xffffff00) != PM_LOG_MAGIC ||
201  		(version != PM_LOG_VERS01 && version != PM_LOG_VERS02) ||
202  		lp->ill_vol != vol) {
203  	#ifdef PCP_DEBUG
204  		if (pmDebug & DBG_TRACE_LOG) {
205  		    if ((lp->ill_magic & 0xffffff00) != PM_LOG_MAGIC)
206  			fprintf(stderr, " label magic 0x%x not 0x%x as expected", (lp->ill_magic & 0xffffff00), PM_LOG_MAGIC);
207  		    if (version != PM_LOG_VERS01 && version != PM_LOG_VERS02)
208  			fprintf(stderr, " label version %d not supported", version);
209  		    if (lp->ill_vol != vol)
210  			fprintf(stderr, " label volume %d not %d as expected", lp->ill_vol, vol);
211  		    fputc('\n', stderr);
212  		}
213  	#endif
214  		return PM_ERR_LABEL;
215  	    }
216  	    else {
217  		if (__pmSetVersionIPC(fileno(f), version) < 0)
218  		    return -oserror();
219  	#ifdef PCP_DEBUG
220  		if (pmDebug & DBG_TRACE_LOG)
221  		    fprintf(stderr, " [magic=%8x version=%d vol=%d pid=%d host=%s]\n",
222  			lp->ill_magic, version, lp->ill_vol, lp->ill_pid, lp->ill_hostname);
223  	#endif
224  	    }
225  	
226  	    if (vol >= 0 && vol < lcp->l_numseen)
227  		lcp->l_seen[vol] = 1;
228  	
229  	    return version;
230  	}
231  	
232  	static int
233  	popen_uncompress(const char *cmd, const char *fname, const char *suffix, int fd)
234  	{
235  	    char	pipecmd[2*MAXPATHLEN+2];
236  	    char	buffer[4096];
237  	    FILE	*finp;
238  	    ssize_t	bytes;
239  	    int		sts, infd;
240  	
241  	    snprintf(pipecmd, sizeof(pipecmd), "%s %s%s", cmd, fname, suffix);
242  	#ifdef PCP_DEBUG
243  	    if (pmDebug & DBG_TRACE_LOG)
244  		fprintf(stderr, "__pmLogOpen: uncompress using: %s\n", pipecmd);
245  	#endif
246  	
247  	    if ((finp = popen(pipecmd, "r")) == NULL)
248  		return -1;
249  	    infd = fileno(finp);
250  	
251  	    while ((bytes = read(infd, buffer, sizeof(buffer))) > 0) {
252  		if (write(fd, buffer, bytes) != bytes) {
253  		    bytes = -1;
254  		    break;
255  		}
256  	    }
257  	
258  	    if ((sts = pclose(finp)) != 0)
259  		return sts;
260  	    return (bytes == 0) ? 0 : -1;
261  	}
262  	
263  	static FILE *
264  	fopen_compress(const char *fname)
265  	{
266  	    int		sts;
267  	    int		fd;
268  	    int		i;
269  	    char	*cmd;
270  	    char	*msg;
271  	    FILE	*fp;
272  	    static char	tmpname[MAXPATHLEN];
273  	
274  	    for (i = 0; i < ncompress; i++) {
275  		snprintf(tmpname, sizeof(tmpname), "%s%s", fname, compress_ctl[i].suff);
276  		if (access(tmpname, R_OK) == 0) {
277  		    break;
278  		}
279  	    }
280  	    if (i == ncompress) {
281  		/* end up here if it does not look like a compressed file */
282  		return NULL;
283  	    }
284  	    if (compress_ctl[i].appl == USE_BZIP2)
285  		cmd = "bzip2 -dc";
286  	    else if (compress_ctl[i].appl == USE_GZIP)
287  		cmd = "gzip -dc";
288  	    else {
289  		/* botch in compress_ctl[] ... should not happen */
290  		return NULL;
291  	    }
292  	
293  	#if HAVE_MKSTEMP
294  	    snprintf(tmpname, sizeof(tmpname), "%s/XXXXXX", pmGetConfig("PCP_TMP_DIR"));
295  	    msg = tmpname;
296  	    fd = mkstemp(tmpname);
297  	#else
298  	    if ((msg = tmpnam(NULL)) == NULL)
299  		return NULL;
300  	    fd = open(msg, O_RDWR|O_CREAT|O_EXCL, 0600);
301  	#endif
302  	
303  	    sts = popen_uncompress(cmd, fname, compress_ctl[i].suff, fd);
304  	    if (sts == -1) {
305  		sts = oserror();
306  	#ifdef PCP_DEBUG
307  		if (pmDebug & DBG_TRACE_LOG)
308  		    fprintf(stderr, "__pmLogOpen: uncompress command failed: %s\n", osstrerror());
309  	#endif
310  		close(fd);
311  		unlink(msg);
312  		setoserror(sts);
313  		return NULL;
314  	    }
315  	    if (sts != 0) {
316  	#ifdef PCP_DEBUG
317  		if (pmDebug & DBG_TRACE_LOG) {
318  	#if defined(HAVE_SYS_WAIT_H)
319  		    if (WIFEXITED(sts))
320  			fprintf(stderr, "__pmLogOpen: uncompress failed, exit status: %d\n", WEXITSTATUS(sts));
321  		    else if (WIFSIGNALED(sts))
322  			fprintf(stderr, "__pmLogOpen: uncompress failed, signal: %d\n", WTERMSIG(sts));
323  		    else
324  	#endif
325  			fprintf(stderr, "__pmLogOpen: uncompress failed, system() returns: %d\n", sts);
326  		}
327  	#endif
328  		close(fd);
329  		unlink(msg);
330  		/* not a great error code, but the best we can do */
331  		setoserror(-PM_ERR_LOGREC);
332  		return NULL;
333  	    }
334  	    if ((fp = fdopen(fd, "r")) == NULL) {
335  		sts = oserror();
336  		close(fd);
337  		unlink(msg);
338  		setoserror(sts);
339  		return NULL;
340  	    }
341  	    /*
342  	     * success, unlink to avoid namespace pollution and allow O/S
343  	     * space cleanup on last close
344  	     */
345  	    unlink(msg);
346  	    return fp;
347  	}
348  	
349  	static FILE *
350  	_logpeek(__pmLogCtl *lcp, int vol)
351  	{
352  	    int		sts;
353  	    FILE	*f;
354  	    __pmLogLabel	label;
355  	
356  	    sts = (int)strlen(lcp->l_name) + 6; /* name.XXXX\0 */
357  	    if (sts > logfilenamelen) {
358  		if ((logfilename = (char *)realloc(logfilename, sts)) == NULL) {
359  		    logfilenamelen = 0;
360  		    return NULL;
361  		}
362  		logfilenamelen = sts;
363  	    }
364  	
365  	    snprintf(logfilename, sts, "%s.%d", lcp->l_name, vol);
366  	    if ((f = fopen(logfilename, "r")) == NULL) {
367  		if ((f = fopen_compress(logfilename)) == NULL)
368  		    return f;
369  	    }
370  	
371  	    if ((sts = __pmLogChkLabel(lcp, f, &label, vol)) < 0) {
372  		fclose(f);
373  		setoserror(sts);
374  		return NULL;
375  	    }
376  	    
377  	    return f;
378  	}
379  	
380  	int
381  	__pmLogChangeVol(__pmLogCtl *lcp, int vol)
382  	{
383  	    char	name[MAXPATHLEN];
384  	    int		sts;
385  	
386  	    if (lcp->l_curvol == vol)
387  		return 0;
388  	
389  	    if (lcp->l_mfp != NULL) {
390  		__pmResetIPC(fileno(lcp->l_mfp));
391  		fclose(lcp->l_mfp);
392  	    }
393  	    snprintf(name, sizeof(name), "%s.%d", lcp->l_name, vol);
394  	    if ((lcp->l_mfp = fopen(name, "r")) == NULL) {
395  		/* try for a compressed file */
396  		if ((lcp->l_mfp = fopen_compress(name)) == NULL)
397  		    return -oserror();
398  	    }
399  	
400  	    if ((sts = __pmLogChkLabel(lcp, lcp->l_mfp, &lcp->l_label, vol)) < 0)
401  		return sts;
402  	
403  	    lcp->l_curvol = vol;
404  	#ifdef PCP_DEBUG
405  	    if (pmDebug & DBG_TRACE_LOG)
406  		fprintf(stderr, "__pmLogChangeVol: change to volume %d\n", vol);
407  	#endif
408  	    return sts;
409  	}
410  	
411  	int
412  	__pmLogLoadIndex(__pmLogCtl *lcp)
413  	{
414  	    int		sts = 0;
415  	    FILE	*f = lcp->l_tifp;
416  	    int		n;
417  	    __pmLogTI	*tip;
418  	
419  	    lcp->l_numti = 0;
420  	    lcp->l_ti = NULL;
421  	
422  	    if (lcp->l_tifp != NULL) {
423  		fseek(f, (long)(sizeof(__pmLogLabel) + 2*sizeof(int)), SEEK_SET);
424  		for ( ; ; ) {
425  		    lcp->l_ti = (__pmLogTI *)realloc(lcp->l_ti, (1 + lcp->l_numti) * sizeof(__pmLogTI));
426  		    if (lcp->l_ti == NULL) {
427  			sts = -oserror();
428  			break;
429  		    }
430  		    tip = &lcp->l_ti[lcp->l_numti];
431  		    n = (int)fread(tip, 1, sizeof(__pmLogTI), f);
432  	
433  	            if (n != sizeof(__pmLogTI)) {
434  			if (feof(f)) {
435  			    clearerr(f);
436  			    sts = 0; 
437  			    break;
438  			}
439  	#ifdef PCP_DEBUG
440  		  	if (pmDebug & DBG_TRACE_LOG)
441  		    	    fprintf(stderr, "__pmLogLoadIndex: bad TI entry len=%d: expected %d\n",
442  			            n, (int)sizeof(__pmLogTI));
443  	#endif
444  			if (ferror(f)) {
445  			    clearerr(f);
446  			    sts = -oserror();
447  			    break;
448  			}
449  			else {
450  			    sts = PM_ERR_LOGREC;
451  			    break;
452  			}
453  		    }
454  		    else {
455  			/* swab the temporal index record */
456  			tip->ti_stamp.tv_sec = ntohl(tip->ti_stamp.tv_sec);
457  			tip->ti_stamp.tv_usec = ntohl(tip->ti_stamp.tv_usec);
458  			tip->ti_vol = ntohl(tip->ti_vol);
459  			tip->ti_meta = ntohl(tip->ti_meta);
460  			tip->ti_log = ntohl(tip->ti_log);
461  		    }
462  	
463  		    lcp->l_numti++;
464  		}/*for*/
465  	    }/*not null*/
466  	
467  	    return sts;
468  	}
469  	
470  	
471  	const char *
472  	__pmLogName(const char *base, int vol)
473  	{
474  	    static char		*tbuf;
475  	    static int		tlen = 0;
476  	    int			len;
477  	
478  	    len = (int)strlen(base) + 8;
479  	    if (len > tlen) {
480  		if (tlen)
481  		    free(tbuf);
482  		if ((tbuf = (char *)malloc(len)) == NULL) {
483  		    __pmNoMem("__pmLogName", len, PM_FATAL_ERR);
484  		}
485  		tlen = len;
486  	    }
487  	
488  	    switch (vol) {
489  		case PM_LOG_VOL_TI:
490  		    sprintf(tbuf, "%s.index", base);	/* safe */
491  		    break;
492  		
493  		case PM_LOG_VOL_META:
494  		    sprintf(tbuf, "%s.meta", base);	/* safe */
495  		    break;
496  	
497  		default:
498  		    sprintf(tbuf, "%s.%d", base, vol);	/* safe */
499  		    break;
500  	    }
501  	
502  	    return tbuf;
503  	}
504  	
505  	FILE *
506  	__pmLogNewFile(const char *base, int vol)
507  	{
508  	    const char	*fname;
509  	    FILE	*f;
510  	    int		save_error;
511  	
512  	    fname = __pmLogName(base, vol);
513  	
514  	    if (access(fname, R_OK) != -1) {
515  		/* exists and readable ... */
516  		pmprintf("__pmLogNewFile: \"%s\" already exists, not over-written\n", fname);
517  		pmflush();
518  		setoserror(EEXIST);
519  		return NULL;
520  	    }
521  	
Event alloc_fn: Calling allocation function "fopen".
Event var_assign: Assigning: "f" = storage returned from "fopen(fname, "w")".
Also see events: [noescape][leaked_storage]
At conditional (1): "(f = fopen(fname, "w")) == NULL": Taking false branch.
522  	    if ((f = fopen(fname, "w")) == NULL) {
523  		save_error = oserror();
524  		pmprintf("__pmLogNewFile: failed to create \"%s\": %s\n", fname, osstrerror());
525  	
526  		pmflush();
527  		setoserror(save_error);
528  		return NULL;
529  	    }
530  	
Event noescape: Variable "f" is not freed or pointed-to in function "fileno".
Also see events: [alloc_fn][var_assign][leaked_storage]
At conditional (2): "(save_error = __pmSetVersionIPC(fileno(f), 2)) < 0": Taking true branch.
531  	    if ((save_error = __pmSetVersionIPC(fileno(f), PDU_VERSION)) < 0) {
532  		pmprintf("__pmLogNewFile: failed to setup \"%s\": %s\n", fname, osstrerror());
533  		pmflush();
534  		setoserror(save_error);
Event leaked_storage: Variable "f" going out of scope leaks the storage it points to.
Also see events: [alloc_fn][var_assign][noescape]
535  		return NULL;
536  	    }
537  	
538  	    return f;
539  	}
540  	
541  	int
542  	__pmLogWriteLabel(FILE *f, const __pmLogLabel *lp)
543  	{
544  	    int		len;
545  	    int		sts = 0;
546  	    __pmLogLabel outll = *lp;
547  	
548  	    len = sizeof(*lp) + 2 * sizeof(len);
549  	    len = htonl(len);
550  	
551  	    /* swab */
552  	    outll.ill_magic = htonl(outll.ill_magic);
553  	    outll.ill_pid = htonl(outll.ill_pid);
554  	    outll.ill_start.tv_sec = htonl(outll.ill_start.tv_sec);
555  	    outll.ill_start.tv_usec = htonl(outll.ill_start.tv_usec);
556  	    outll.ill_vol = htonl(outll.ill_vol);
557  	
558  	    if ((int)fwrite(&len, 1, sizeof(len), f) != sizeof(len) ||
559  		(int)fwrite(&outll, 1, sizeof(outll), f) != sizeof(outll) ||
560  	        (int)fwrite(&len, 1, sizeof(len), f) != sizeof(len)) {
561  		    sts = -oserror();
562  		    pmprintf("__pmLogWriteLabel: %s\n", osstrerror());
563  		    pmflush();
564  		    fclose(f);
565  	    }
566  	
567  	    return sts;
568  	}
569  	
570  	int
571  	__pmLogCreate(const char *host, const char *base, int log_version,
572  		      __pmLogCtl *lcp)
573  	{
574  	    int		save_error = 0;
575  	
576  	    lcp->l_minvol = lcp->l_maxvol = lcp->l_curvol = 0;
577  	    lcp->l_hashpmid.nodes = lcp->l_hashpmid.hsize = 0;
578  	    lcp->l_hashindom.nodes = lcp->l_hashindom.hsize = 0;
579  	    lcp->l_tifp = lcp->l_mdfp = lcp->l_mfp = NULL;
580  	
581  	    if ((lcp->l_tifp = __pmLogNewFile(base, PM_LOG_VOL_TI)) != NULL) {
582  		if ((lcp->l_mdfp = __pmLogNewFile(base, PM_LOG_VOL_META)) != NULL) {
583  		    if ((lcp->l_mfp = __pmLogNewFile(base, 0)) != NULL) {
584  			char *tz = __pmTimezone();
585  	                int             sts;
586  	
587  			lcp->l_label.ill_magic = PM_LOG_MAGIC | log_version;
588  			/*
589  			 * Warning	ill_hostname may be truncated, but we
590  			 *		guarantee it will be null-byte terminated
591  			 */
592  			strncpy(lcp->l_label.ill_hostname, host, PM_LOG_MAXHOSTLEN-1);
593  			lcp->l_label.ill_hostname[PM_LOG_MAXHOSTLEN-1] = '\0';
594  			lcp->l_label.ill_pid = (int)getpid();
595  			/*
596  			 * hack - how do you get the TZ for a remote host?
597  			 */
598  			strcpy(lcp->l_label.ill_tz, tz ? tz : "");
599  			lcp->l_state = PM_LOG_STATE_NEW;
600  	
601  	                /*
602  	                 * __pmLogNewFile sets the IPC version to PDU_VERSION
603  	                 * we want log_version instead
604  	                 */
605  			sts = __pmSetVersionIPC(fileno(lcp->l_tifp), log_version);
606  			if (sts < 0)
607  	                    return sts;
608  			sts = __pmSetVersionIPC(fileno(lcp->l_mdfp), log_version);
609  			if (sts < 0)
610  	                    return sts;
611  			sts = __pmSetVersionIPC(fileno(lcp->l_mfp), log_version);
612  			return sts;
613  		    }
614  		    else {
615  			save_error = oserror();
616  			unlink(__pmLogName(base, PM_LOG_VOL_TI));
617  			unlink(__pmLogName(base, PM_LOG_VOL_META));
618  			setoserror(save_error);
619  		    }
620  		}
621  		else {
622  		    save_error = oserror();
623  		    unlink(__pmLogName(base, PM_LOG_VOL_TI));
624  		    setoserror(save_error);
625  		}
626  	    }
627  	
628  	    lcp->l_tifp = lcp->l_mdfp = lcp->l_mfp = NULL;
629  	    return oserror() ? -oserror() : -EPERM;
630  	}
631  	
632  	/*
633  	 * Close the log files.
634  	 * Free up the space used by __pmLogCtl.
635  	 */
636  	
637  	void
638  	__pmLogClose(__pmLogCtl *lcp)
639  	{
640  	    if (lcp->l_tifp != NULL) {
641  		__pmResetIPC(fileno(lcp->l_tifp));
642  		fclose(lcp->l_tifp);
643  		lcp->l_tifp = NULL;
644  	    }
645  	    if (lcp->l_mdfp != NULL) {
646  		__pmResetIPC(fileno(lcp->l_mdfp));
647  		fclose(lcp->l_mdfp);
648  		lcp->l_mdfp = NULL;
649  	    }
650  	    if (lcp->l_mfp != NULL) {
651  		__pmLogCacheClear(lcp->l_mfp);
652  		__pmResetIPC(fileno(lcp->l_mfp));
653  		fclose(lcp->l_mfp);
654  		lcp->l_mfp = NULL;
655  	    }
656  	    if (lcp->l_name != NULL) {
657  		free(lcp->l_name);
658  		lcp->l_name = NULL;
659  	    }
660  	    if (lcp->l_seen != NULL) {
661  		free(lcp->l_seen);
662  		lcp->l_seen = NULL;
663  		lcp->l_numseen = 0;
664  	    }
665  	    if (lcp->l_pmns != NULL) {
666  		__pmFreePMNS(lcp->l_pmns);
667  		lcp->l_pmns = NULL;
668  	    }
669  	
670  	    if (lcp->l_ti != NULL)
671  		free(lcp->l_ti);
672  	
673  	    if (lcp->l_hashpmid.hsize != 0) {
674  		__pmHashCtl	*hcp = &lcp->l_hashpmid;
675  		__pmHashNode	*hp;
676  		__pmHashNode	*prior_hp;
677  		int		i;
678  	
679  		for (i = 0; i < hcp->hsize; i++) {
680  		    for (hp = hcp->hash[i], prior_hp = NULL; hp != NULL; hp = hp->next) {
681  			if (hp->data != NULL)
682  			    free(hp->data);
683  			if (prior_hp != NULL)
684  			    free(prior_hp);
685  			prior_hp = hp;
686  		    }
687  		    if (prior_hp != NULL)
688  			free(prior_hp);
689  		}
690  		free(hcp->hash);
691  	    }
692  	
693  	    if (lcp->l_hashindom.hsize != 0) {
694  		__pmHashCtl	*hcp = &lcp->l_hashindom;
695  		__pmHashNode	*hp;
696  		__pmHashNode	*prior_hp;
697  		__pmLogInDom	*idp;
698  		__pmLogInDom	*prior_idp;
699  		int		i;
700  	
701  		for (i = 0; i < hcp->hsize; i++) {
702  		    for (hp = hcp->hash[i], prior_hp = NULL; hp != NULL; hp = hp->next) {
703  			for (idp = (__pmLogInDom *)hp->data, prior_idp = NULL;
704  			     idp != NULL; idp = idp->next) {
705  			    if (idp->buf != NULL)
706  				free(idp->buf);
707  			    if (idp->allinbuf == 0 && idp->namelist != NULL)
708  				free(idp->namelist);
709  			    if (prior_idp != NULL)
710  				free(prior_idp);
711  			    prior_idp = idp;
712  			}
713  			if (prior_idp != NULL)
714  			    free(prior_idp);
715  			if (prior_hp != NULL)
716  			    free(prior_hp);
717  			prior_hp = hp;
718  		    }
719  		    if (prior_hp != NULL)
720  			free(prior_hp);
721  		}
722  		free(hcp->hash);
723  	    }
724  	
725  	}
726  	
727  	int
728  	__pmLogLoadLabel(__pmLogCtl *lcp, const char *name)
729  	{
730  	    int		sts;
731  	    int		blen;
732  	    int		exists = 0;
733  	    int		i;
734  	    int		sep = __pmPathSeparator();
735  	    char	*q;
736  	    char	*base;
737  	    char	*tbuf;
738  	    char	*tp;
739  	    char	*dir;
740  	    DIR		*dirp = NULL;
741  	    char	filename[MAXPATHLEN];
742  	#if defined(HAVE_READDIR64)
743  	    struct dirent64	*direntp;
744  	#else
745  	    struct dirent	*direntp;
746  	#endif
747  	
748  	    /*
749  	     * find directory name component ... copy as dirname() may clobber
750  	     * the string
751  	     */
752  	    if ((tbuf = strdup(name)) == NULL)
753  		return -oserror();
754  	    dir = dirname(tbuf);
755  	
756  	    /*
757  	     * find file name component
758  	     */
759  	    strncpy(filename, name, MAXPATHLEN);
760  	    if ((base = strdup(basename(filename))) == NULL) {
761  		sts = -oserror();
762  		free(tbuf);
763  		return sts;
764  	    }
765  	
766  	    if (access(name, R_OK) == 0) {
767  		/*
768  		 * file exists and is readable ... if name contains '.' and
769  		 * suffix is "index", "meta" or a string of digits or a string
770  		 * of digits followed by one of the compression suffixes,
771  		 * strip the suffix
772  		 */
773  		int	strip = 0;
774  		if ((q = strrchr(base, '.')) != NULL) {
775  		    if (strcmp(q, ".index") == 0) {
776  			strip = 1;
777  			goto done;
778  		    }
779  		    if (strcmp(q, ".meta") == 0) {
780  			strip = 1;
781  			goto done;
782  		    }
783  		    for (i = 0; i < ncompress; i++) {
784  			if (strcmp(q, compress_ctl[i].suff) == 0) {
785  			    char	*q2;
786  			    /*
787  			     * name ends with one of the supported compressed file
788  			     * suffixes, check for a string of digits before that,
789  			     * e.g. if base is initially "foo.0.bz2", we want it
790  			     * stripped to "foo"
791  			     */
792  			    *q = '\0';
793  			    if ((q2 = strrchr(base, '.')) == NULL) {
794  				/* no . to the left of the suffix */
795  				*q = '.';
796  				goto done;
797  			    }
798  			    q = q2;
799  			    break;
800  			}
801  		    }
802  		    if (q[1] != '\0') {
803  			char	*end;
804  			/*
805  			 * Below we don't care about the value from strtol(),
806  			 * we're interested in updating the pointer "end".
807  			 * The messiness is thanks to gcc and glibc ... strtol()
808  			 * is marked __attribute__((warn_unused_result)) ...
809  			 * to avoid warnings on all platforms, assign to a
810  			 * dummy variable that is explicitly marked unused.
811  			 */
812  			long	tmpl __attribute__((unused));
813  			tmpl = strtol(q+1, &end, 10);
814  			if (*end == '\0') strip = 1;
815  		    }
816  		}
817  	done:
818  		if (strip) {
819  		    *q = '\0';
820  		}
821  	    }
822  	
823  	    snprintf(filename, sizeof(filename), "%s%c%s", dir, sep, base);
824  	    if ((lcp->l_name = strdup(filename)) == NULL) {
825  		sts = -oserror();
826  		free(tbuf);
827  		free(base);
828  		return sts;
829  	    }
830  	
831  	    lcp->l_minvol = -1;
832  	    lcp->l_tifp = lcp->l_mdfp = lcp->l_mfp = NULL;
833  	    lcp->l_ti = NULL;
834  	    lcp->l_hashpmid.nodes = lcp->l_hashpmid.hsize = 0;
835  	    lcp->l_hashindom.nodes = lcp->l_hashindom.hsize = 0;
836  	    lcp->l_numseen = 0; lcp->l_seen = NULL;
837  	    lcp->l_pmns = NULL;
838  	
839  	    blen = (int)strlen(base);
840  	    if ((dirp = opendir(dir)) != NULL) {
841  	#if defined(HAVE_READDIR64)
842  		while ((direntp = readdir64(dirp)) != NULL)
843  	#else
844  		while ((direntp = readdir(dirp)) != NULL)
845  	#endif
846  		{
847  		    if (strncmp(base, direntp->d_name, blen) != 0)
848  			continue;
849  		    if (direntp->d_name[blen] != '.')
850  			continue;
851  	#ifdef PCP_DEBUG
852  		    if (pmDebug & DBG_TRACE_LOG) {
853  			snprintf(filename, sizeof(filename), "%s%c%s", dir, sep, direntp->d_name);
854  			fprintf(stderr, "__pmLogOpen: inspect file \"%s\"\n", filename);
855  		    }
856  	#endif
857  		    tp = &direntp->d_name[blen+1];
858  		    if (strcmp(tp, "index") == 0) {
859  			exists = 1;
860  			snprintf(filename, sizeof(filename), "%s%c%s", dir, sep, direntp->d_name);
861  			if ((lcp->l_tifp = fopen(filename, "r")) == NULL) {
862  			    sts = -oserror();
863  			    goto cleanup;
864  			}
865  		    }
866  		    else if (strcmp(tp, "meta") == 0) {
867  			exists = 1;
868  			snprintf(filename, sizeof(filename), "%s%c%s", dir, sep, direntp->d_name);
869  			if ((lcp->l_mdfp = fopen(filename, "r")) == NULL) {
870  			    sts = -oserror();
871  			    goto cleanup;
872  			}
873  		    }
874  		    else {
875  			char	*q;
876  			int	vol;
877  			vol = (int)strtol(tp, &q, 10);
878  			if (*q != '0') {
879  			    /* may have one of the trailing compressed file suffixes */
880  			    int		i;
881  			    for (i = 0; i < ncompress; i++) {
882  				if (strcmp(q, compress_ctl[i].suff) == 0) {
883  				    /* match */
884  				    *q = '\0';
885  				    break;
886  				}
887  			    }
888  			}
889  			if (*q == '\0') {
890  			    exists = 1;
891  			    if (lcp->l_minvol == -1) {
892  				lcp->l_minvol = vol;
893  				lcp->l_maxvol = vol;
894  			    }
895  			    else {
896  				if (vol < lcp->l_minvol)
897  				    lcp->l_minvol = vol;
898  				if (vol > lcp->l_maxvol)
899  				    lcp->l_maxvol = vol;
900  			    }
901  			}
902  		    }
903  		}
904  		closedir(dirp);
905  		dirp = NULL;
906  	    }
907  	    else {
908  	#ifdef PCP_DEBUG
909  		sts = -oserror();
910  		if (pmDebug & DBG_TRACE_LOG)
911  		    fprintf(stderr, "__pmLogOpen: cannot scan directory \"%s\": %s\n", dir, pmErrStr(sts));
912  		goto cleanup;
913  		
914  	#endif
915  	    }
916  	
917  	    if (lcp->l_minvol == -1 || lcp->l_mdfp == NULL) {
918  	#ifdef PCP_DEBUG
919  		if (pmDebug & DBG_TRACE_LOG) {
920  		    if (lcp->l_minvol == -1)
921  			fprintf(stderr, "__pmLogOpen: Not found: data file \"%s.0\" (or similar)\n", base);
922  		    if (lcp->l_mdfp == NULL)
923  			fprintf(stderr, "__pmLogOpen: Not found: metadata file \"%s.meta\"\n", base);
924  		}
925  	#endif
926  		if (exists)
927  		    sts = PM_ERR_LOGFILE;
928  		else
929  		    sts = -ENOENT;
930  		goto cleanup;
931  	    }
932  	    free(tbuf);
933  	    free(base);
934  	    return 0;
935  	
936  	cleanup:
937  	    if (dirp != NULL)
938  		closedir(dirp);
939  	    __pmLogClose(lcp);
940  	    free(tbuf);
941  	    free(base);
942  	    return sts;
943  	}
944  	
945  	int
946  	__pmLogOpen(const char *name, __pmContext *ctxp)
947  	{
948  	    __pmLogCtl	*lcp = ctxp->c_archctl->ac_log;
949  	    __pmLogLabel label;
950  	    int		version;
951  	    int		sts;
952  	
953  	    if ((sts = __pmLogLoadLabel(lcp, name)) < 0)
954  		return sts;
955  	
956  	    lcp->l_curvol = -1;
957  	    if ((sts = __pmLogChangeVol(lcp, lcp->l_minvol)) < 0)
958  		goto cleanup;
959  	    else
960  		version = sts;
961  	
962  	    ctxp->c_origin = lcp->l_label.ill_start;
963  	
964  	    if (lcp->l_tifp) {
965  		sts = __pmLogChkLabel(lcp, lcp->l_tifp, &label, PM_LOG_VOL_TI);
966  		if (sts < 0)
967  		    goto cleanup;
968  		else if (sts != version) {
969  		    /* mismatch between meta & actual data versions! */
970  		    sts = PM_ERR_LABEL;
971  		    goto cleanup;
972  		}
973  	
974  		if (lcp->l_label.ill_pid != label.ill_pid ||
975  			strcmp(lcp->l_label.ill_hostname, label.ill_hostname) != 0) {
976  		    sts = PM_ERR_LABEL;
977  		    goto cleanup;
978  		}
979  	    }
980  	
981  	    if ((sts = __pmLogChkLabel(lcp, lcp->l_mdfp, &label, PM_LOG_VOL_META)) < 0)
982  		goto cleanup;
983  	    else if (sts != version) {	/* version mismatch between meta & ti */
984  		sts = PM_ERR_LABEL;
985  		goto cleanup;
986  	    }
987  	
988  	    if ((sts = __pmLogLoadMeta(lcp)) < 0)
989  		goto cleanup;
990  	
991  	    if ((sts = __pmLogLoadIndex(lcp)) < 0)
992  		goto cleanup;
993  	
994  	    if (lcp->l_label.ill_pid != label.ill_pid ||
995  		strcmp(lcp->l_label.ill_hostname, label.ill_hostname) != 0) {
996  		    sts = PM_ERR_LABEL;
997  		    goto cleanup;
998  	    }
999  	    
1000 	    lcp->l_refcnt = 0;
1001 	    lcp->l_physend = -1;
1002 	
1003 	    ctxp->c_mode = (ctxp->c_mode & 0xffff0000) | PM_MODE_FORW;
1004 	
1005 	    return 0;
1006 	
1007 	cleanup:
1008 	    __pmLogClose(lcp);
1009 	    return sts;
1010 	}
1011 	
1012 	void
1013 	__pmLogPutIndex(const __pmLogCtl *lcp, const __pmTimeval *tp)
1014 	{
1015 	    static __pmLogTI	ti;
1016 	    __pmLogTI		oti;
1017 	
1018 	    if (lcp->l_tifp == NULL || lcp->l_mdfp == NULL || lcp->l_mfp == NULL) {
1019 		/*
1020 		 * archive not really created (failed in __pmLogCreate) ...
1021 		 * nothing to be done
1022 		 */
1023 		return;
1024 	    }
1025 	
1026 	    if (tp == NULL) {
1027 		struct timeval	tmp;
1028 	
1029 		__pmtimevalNow(&tmp);
1030 		ti.ti_stamp.tv_sec = (__int32_t)tmp.tv_sec;
1031 		ti.ti_stamp.tv_usec = (__int32_t)tmp.tv_usec;
1032 	    }
1033 	    else
1034 		ti.ti_stamp = *tp;		/* struct assignment */
1035 	    ti.ti_vol = lcp->l_curvol;
1036 	    fflush(lcp->l_mdfp);
1037 	    fflush(lcp->l_mfp);
1038 	
1039 	    if (sizeof(off_t) > sizeof(__pm_off_t)) {
1040 		/* check for overflow of the offset ... */
1041 		off_t	tmp;
1042 	
1043 		tmp = ftell(lcp->l_mdfp);
1044 		ti.ti_meta = (__pm_off_t)tmp;
1045 		if (tmp != ti.ti_meta) {
1046 		    __pmNotifyErr(LOG_ERR, "__pmLogPutIndex: PCP archive file (meta) too big\n");
1047 		    exit(1);
1048 		}
1049 		tmp = ftell(lcp->l_mfp);
1050 		ti.ti_log = (__pm_off_t)tmp;
1051 		if (tmp != ti.ti_log) {
1052 		    __pmNotifyErr(LOG_ERR, "__pmLogPutIndex: PCP archive file (data) too big\n");
1053 		    exit(1);
1054 		}
1055 	    }
1056 	    else {
1057 		ti.ti_meta = (__pm_off_t)ftell(lcp->l_mdfp);
1058 		ti.ti_log = (__pm_off_t)ftell(lcp->l_mfp);
1059 	    }
1060 	
1061 	    oti.ti_stamp.tv_sec = htonl(ti.ti_stamp.tv_sec);
1062 	    oti.ti_stamp.tv_usec = htonl(ti.ti_stamp.tv_usec);
1063 	    oti.ti_vol = htonl(ti.ti_vol);
1064 	    oti.ti_meta = htonl(ti.ti_meta);
1065 	    oti.ti_log = htonl(ti.ti_log);
1066 	    fwrite(&oti, 1, sizeof(oti), lcp->l_tifp);
1067 	    fflush(lcp->l_tifp);
1068 	}
1069 	
1070 	int
1071 	__pmLogPutResult(__pmLogCtl *lcp, __pmPDU *pb)
1072 	{
1073 	    /*
1074 	     * This is a bit tricky ...
1075 	     *
1076 	     *  Input
1077 	     *  :---------:----------:----------:----------------
1078 	     *  | int len | int from | int from | timestamp, .... pmResult
1079 	     *  :---------:----------:----------:----------------
1080 	     *  ^
1081 	     *  |
1082 	     *  pb
1083 	     *
1084 	     *  Output
1085 	     *  :---------:----------:----------:---------------- .........:---------:
1086 	     *  | int len | int from | int len  | timestamp, .... pmResult | int len |
1087 	     *  :---------:----------:----------:---------------- .........:---------:
1088 	     *                       ^
1089 	     *                       |
1090 	     *                       start
1091 	     */
1092 	    int			sz;
1093 	    int			sts = 0;
1094 	    __pmPDUHdr		*php = (__pmPDUHdr *)pb;
1095 	
1096 	    if (lcp->l_state == PM_LOG_STATE_NEW) {
1097 		int		i;
1098 		__pmTimeval	*tvp;
1099 		/*
1100 		 * first result, do the label record
1101 		 */
1102 		i = sizeof(__pmPDUHdr) / sizeof(__pmPDU);
1103 		tvp = (__pmTimeval *)&pb[i];
1104 		lcp->l_label.ill_start.tv_sec = ntohl(tvp->tv_sec);
1105 		lcp->l_label.ill_start.tv_usec = ntohl(tvp->tv_usec);
1106 		lcp->l_label.ill_vol = PM_LOG_VOL_TI;
1107 		__pmLogWriteLabel(lcp->l_tifp, &lcp->l_label);
1108 		lcp->l_label.ill_vol = PM_LOG_VOL_META;
1109 		__pmLogWriteLabel(lcp->l_mdfp, &lcp->l_label);
1110 		lcp->l_label.ill_vol = 0;
1111 		__pmLogWriteLabel(lcp->l_mfp, &lcp->l_label);
1112 		lcp->l_state = PM_LOG_STATE_INIT;
1113 	    }
1114 	
1115 	#ifdef PCP_DEBUG
1116 	    if (pmDebug & DBG_TRACE_LOG) {
1117 		fprintf(stderr, "__pmLogPutResult: pdubuf=" PRINTF_P_PFX "%p len=%d posn=%ld\n", pb, php->len, (long)ftell(lcp->l_mfp));
1118 	    }
1119 	#endif
1120 	
1121 	    php->from = php->len - (int)sizeof(__pmPDUHdr) + 2 * (int)sizeof(int);
1122 	    sz = php->from - (int)sizeof(int);
1123 	
1124 	    /* swab */
1125 	    php->len = htonl(php->len);
1126 	    php->type = htonl(php->type);
1127 	    php->from = htonl(php->from);
1128 	
1129 	    if ((int)fwrite(&php->from, 1, sz, lcp->l_mfp) != sz)
1130 		sts = -oserror();
1131 	    else
1132 	    if ((int)fwrite(&php->from, 1, sizeof(int), lcp->l_mfp) != sizeof(int))
1133 		sts = -oserror();
1134 	
1135 	    /* unswab */
1136 	    php->len = ntohl(php->len);
1137 	    php->type = ntohl(php->type);
1138 	    php->from = ntohl(php->from);
1139 	
1140 	    return sts;
1141 	}
1142 	
1143 	/*
1144 	 * check if PDU buffer seems even half-way reasonable ...
1145 	 * only used when trying to locate end of archive.
1146 	 * return 0 for OK, -1 for bad.
1147 	 */
1148 	static int
1149 	paranoidCheck(int len, __pmPDU *pb)
1150 	{
1151 	    int			numpmid;
1152 	    size_t		hdrsz;		/* bytes for the PDU head+tail */
1153 	    int			i;
1154 	    int			j;
1155 	    int			vsize;		/* size of vlist_t's in PDU buffer */
1156 	    int			vbsize;		/* size of pmValueBlocks */
1157 	    int			numval;		/* number of values */
1158 	    int			valfmt;
1159 	
1160 	    struct result_t {			/* from p_result.c */
1161 		__pmPDUHdr		hdr;
1162 		__pmTimeval		timestamp;	/* when returned */
1163 		int			numpmid;	/* no. of PMIDs to follow */
1164 		__pmPDU			data[1];	/* zero or more */
1165 	    }			*pp;
1166 	    struct vlist_t {			/* from p_result.c */
1167 		pmID			pmid;
1168 		int			numval;		/* no. of vlist els to follow, or error */
1169 		int			valfmt;		/* insitu or pointer */
1170 		__pmValue_PDU		vlist[1];	/* zero or more */
1171 	    }			*vlp;
1172 	
1173 	    /*
1174 	     * to start with, need space for result_t with no data (__pmPDU)
1175 	     * ... this is the external size, which consists of
1176 	     * <header len>
1177 	     * <timestamp> (2 words)
1178 	     * <numpmid>
1179 	     * <trailer len>
1180 	     *
1181 	     * it is confusing because *pb and result_t include the fake
1182 	     * __pmPDUHdr which is not really in the external file
1183 	     */
1184 	    hdrsz = 5 * sizeof(__pmPDU);
1185 	
1186 	    if (len < hdrsz) {
1187 	#ifdef PCP_DEBUG
1188 		if (pmDebug & DBG_TRACE_LOG) {
1189 		    fprintf(stderr, "\nparanoidCheck: len=%d, min len=%d\n",
1190 			len, (int)hdrsz);
1191 		    dumpbuf(len, &pb[3]); /* skip first 3 words, start @ timestamp */
1192 		}
1193 	#endif
1194 		return -1;
1195 	    }
1196 	
1197 	    pp = (struct result_t *)pb;
1198 	    numpmid = ntohl(pp->numpmid);
1199 	
1200 	    /*
1201 	     * This is a re-implementation of much of __pmDecodeResult()
1202 	     */
1203 	
1204 	    if (numpmid < 1) {
1205 		if (len != hdrsz) {
1206 	#ifdef PCP_DEBUG
1207 		    if (pmDebug & DBG_TRACE_LOG) {
1208 			fprintf(stderr, "\nparanoidCheck: numpmid=%d len=%d, expected len=%d\n",
1209 			    numpmid, len, (int)hdrsz);
1210 			dumpbuf(len, &pb[3]); /* skip first 3 words, start @ timestamp */
1211 		    }
1212 	#endif
1213 		    return -1;
1214 		}
1215 	    }
1216 	
1217 	    /*
1218 	     * Calculate vsize and vbsize from the original PDU buffer ...
1219 	     * :---------:-----------:----------------:--------------------:
1220 	     * : numpmid : timestamp : ... vlists ... : .. pmValueBocks .. :
1221 	     * :---------:-----------:----------------:--------------------:
1222 	     *                        <---  vsize ---> <---   vbsize   --->
1223 	     *                              bytes             bytes
1224 	     */
1225 	
1226 	    vsize = vbsize = 0;
1227 	    for (i = 0; i < numpmid; i++) {
1228 		vlp = (struct vlist_t *)&pp->data[vsize/sizeof(__pmPDU)];
1229 		vsize += sizeof(vlp->pmid) + sizeof(vlp->numval);
1230 		if (len < hdrsz + vsize + vbsize) {
1231 	#ifdef PCP_DEBUG
1232 		    if (pmDebug & DBG_TRACE_LOG) {
1233 			fprintf(stderr, "\nparanoidCheck: vset[%d] len=%d, need len>=%d (%d+%d+%d)\n",
1234 			    i, len, (int)(hdrsz + vsize + vbsize), (int)hdrsz, vsize, vbsize);
1235 			dumpbuf(len, &pb[3]); /* skip first 3 words, start @ timestamp */
1236 		    }
1237 	#endif
1238 		    return -1;
1239 		}
1240 		numval = ntohl(vlp->numval);
1241 		if (numval > 0) {
1242 	#ifdef DESPERATE
1243 		    pmID		pmid;
1244 	#endif
1245 		    valfmt = ntohl(vlp->valfmt);
1246 		    if (valfmt != PM_VAL_INSITU &&
1247 			valfmt != PM_VAL_DPTR &&
1248 			valfmt != PM_VAL_SPTR) {
1249 	#ifdef PCP_DEBUG
1250 			if (pmDebug & DBG_TRACE_LOG) {
1251 			    fprintf(stderr, "\nparanoidCheck: vset[%d] bad valfmt=%d\n",
1252 				i, valfmt);
1253 			    dumpbuf(len, &pb[3]); /* skip first 3 words, start @ timestamp */
1254 			}
1255 	#endif
1256 			return -1;
1257 		    }
1258 	#ifdef DESPERATE
1259 		    if (i == 0) fputc('\n', stderr);
1260 		    pmid = __ntohpmID(vlp->pmid);
1261 		    fprintf(stderr, "vlist[%d] pmid: %s numval: %d valfmt: %d\n",
1262 					i, pmIDStr(pmid), numval, valfmt);
1263 	#endif
1264 		    vsize += sizeof(vlp->valfmt) + numval * sizeof(__pmValue_PDU);
1265 		    if (valfmt != PM_VAL_INSITU) {
1266 			for (j = 0; j < numval; j++) {
1267 			    int			index = (int)ntohl((long)vlp->vlist[j].value.pval);
1268 			    pmValueBlock	*pduvbp;
1269 			    int			vlen;
1270 			    
1271 			    if (index < 0 || index * sizeof(__pmPDU) > len) {
1272 	#ifdef PCP_DEBUG
1273 				if (pmDebug & DBG_TRACE_LOG) {
1274 				    fprintf(stderr, "\nparanoidCheck: vset[%d] val[%d], bad pval index=%d not in range 0..%d\n",
1275 					i, j, index, (int)(len / sizeof(__pmPDU)));
1276 				    dumpbuf(len, &pb[3]); /* skip first 3 words, start @ timestamp */
1277 				}
1278 	#endif
1279 				return -1;
1280 			    }
1281 			    pduvbp = (pmValueBlock *)&pb[index];
1282 			    __ntohpmValueBlock(pduvbp);
1283 			    vlen = pduvbp->vlen;
1284 			    __htonpmValueBlock(pduvbp);		/* restore pdubuf! */
1285 			    if (vlen < sizeof(__pmPDU)) {
1286 	#ifdef PCP_DEBUG
1287 				if (pmDebug & DBG_TRACE_LOG) {
1288 				    fprintf(stderr, "\nparanoidCheck: vset[%d] val[%d], bad vlen=%d\n",
1289 					i, j, vlen);
1290 				    dumpbuf(len, &pb[3]); /* skip first 3 words, start @ timestamp */
1291 				}
1292 	#endif
1293 				return -1;
1294 			    }
1295 			    vbsize += PM_PDU_SIZE_BYTES(vlen);
1296 			}
1297 		    }
1298 		}
1299 	    }
1300 	
1301 	    return 0;
1302 	}
1303 	
1304 	static int
1305 	paranoidLogRead(__pmLogCtl *lcp, int mode, FILE *peekf, pmResult **result)
1306 	{
1307 	    int		sts;
1308 	    seeking_end = 1;
1309 	    sts = __pmLogRead(lcp, mode, peekf, result);
1310 	    seeking_end = 0;
1311 	    return sts;
1312 	}
1313 	
1314 	/*
1315 	 * read next forward or backward from the log
1316 	 *
1317 	 * by default (peekf == NULL) use lcp->l_mfp and roll volume
1318 	 * at end of file if another volume is available
1319 	 *
1320 	 * if peekf != NULL, use this stream, and do not roll volume
1321 	 */
1322 	int
1323 	__pmLogRead(__pmLogCtl *lcp, int mode, FILE *peekf, pmResult **result)
1324 	{
1325 	    int		head;
1326 	    int		rlen;
1327 	    int		trail;
1328 	    int		sts;
1329 	    long	offset;
1330 	    __pmPDU	*pb;
1331 	    FILE	*f;
1332 	    int		n;
1333 	
1334 	    /*
1335 	     * Strip any XTB data from mode, its not used here
1336 	     */
1337 	    mode &= __PM_MODE_MASK;
1338 	
1339 	    if (peekf != NULL)
1340 		f = peekf;
1341 	    else
1342 		f = lcp->l_mfp;
1343 	
1344 	    offset = ftell(f);
1345 	#ifdef PCP_DEBUG
1346 	    if (pmDebug & DBG_TRACE_LOG) {
1347 		fprintf(stderr, "__pmLogRead: fd=%d%s mode=%s vol=%d posn=%ld ",
1348 		    fileno(f), peekf == NULL ? "" : " (peek)",
1349 		    mode == PM_MODE_FORW ? "forw" : "back",
1350 		    lcp->l_curvol, (long)offset);
1351 	    }
1352 	#endif
1353 	
1354 	    if (mode == PM_MODE_BACK) {
1355 	       for ( ; ; ) {
1356 		   if (offset <= sizeof(__pmLogLabel) + 2 * sizeof(int)) {
1357 	#ifdef PCP_DEBUG
1358 			if (pmDebug & DBG_TRACE_LOG)
1359 			    fprintf(stderr, "BEFORE start\n");
1360 	#endif
1361 			if (peekf == NULL) {
1362 			    int		vol = lcp->l_curvol-1;
1363 			    while (vol >= lcp->l_minvol) {
1364 				if (__pmLogChangeVol(lcp, vol) >= 0) {
1365 				    f = lcp->l_mfp;
1366 				    fseek(f, 0L, SEEK_END);
1367 				    offset = ftell(f);
1368 	#ifdef PCP_DEBUG
1369 				    if (pmDebug & DBG_TRACE_LOG) {
1370 					fprintf(stderr, "vol=%d posn=%ld ",
1371 					    lcp->l_curvol, (long)offset);
1372 				    }
1373 	#endif
1374 				    break;
1375 				}
1376 				vol--;
1377 			    }
1378 			    if (vol < lcp->l_minvol)
1379 				return PM_ERR_EOL;
1380 			}
1381 			else
1382 			    return PM_ERR_EOL;
1383 		    }
1384 		    else {
1385 			fseek(f, -(long)sizeof(head), SEEK_CUR);
1386 			break;
1387 		    }
1388 		}
1389 	    }
1390 	
1391 	again:
1392 	    n = (int)fread(&head, 1, sizeof(head), f);
1393 	    head = ntohl(head); /* swab head */
1394 	    if (n != sizeof(head)) {
1395 		if (feof(f)) {
1396 		    /* no more data ... looks like End of Archive volume */
1397 		    clearerr(f);
1398 	#ifdef PCP_DEBUG
1399 		    if (pmDebug & DBG_TRACE_LOG)
1400 			fprintf(stderr, "AFTER end\n");
1401 	#endif
1402 		    fseek(f, offset, SEEK_SET);
1403 		    if (peekf == NULL) {
1404 			int	vol = lcp->l_curvol+1;
1405 			while (vol <= lcp->l_maxvol) {
1406 			    if (__pmLogChangeVol(lcp, vol) >= 0) {
1407 				f = lcp->l_mfp;
1408 				goto again;
1409 			    }
1410 			    vol++;
1411 			}
1412 		    }
1413 		    return PM_ERR_EOL;
1414 		}
1415 	
1416 	#ifdef PCP_DEBUG
1417 		if (pmDebug & DBG_TRACE_LOG)
1418 		    fprintf(stderr, "\nError: header fread got %d expected %d\n", n, (int)sizeof(head));
1419 	#endif
1420 		if (ferror(f)) {
1421 		    /* I/O error */
1422 		    clearerr(f);
1423 		    return -oserror();
1424 		}
1425 		else
1426 		    /* corrupted archive */
1427 		    return PM_ERR_LOGREC;
1428 	    }
1429 	
1430 	    /*
1431 	     * This is pretty ugly (forward case shown backwards is similar) ...
1432 	     *
1433 	     *  Input
1434 	     *                         head    <--- rlen bytes -- ...--->   tail
1435 	     *  :---------:---------:---------:---------------- .........:---------:
1436 	     *  |   ???   |   ???   | int len | timestamp, .... pmResult | int len |
1437 	     *  :---------:---------:---------:---------------- .........:---------:
1438 	     *  ^                             ^
1439 	     *  |                             |
1440 	     *  pb                            read into here
1441 	     *
1442 	     *  Decode
1443 	     *  <----  __pmPDUHdr  ----------->
1444 	     *  :---------:---------:---------:---------------- .........:
1445 	     *  |   ???   |   ???   |   ???   | timestamp, .... pmResult |
1446 	     *  :---------:---------:---------:---------------- .........:
1447 	     *  ^
1448 	     *  |
1449 	     *  pb
1450 	     *
1451 	     * Note: cannot volume switch in the middle of a log record
1452 	     */
1453 	
1454 	    rlen = head - 2 * (int)sizeof(head);
1455 	    if (rlen < 0 || (mode == PM_MODE_BACK && rlen > offset)) {
1456 		/*
1457 		 * corrupted! usually means a truncated log ...
1458 		 */
1459 	#ifdef PCP_DEBUG
1460 		if (pmDebug & DBG_TRACE_LOG)
1461 		    fprintf(stderr, "\nError: truncated log? rlen=%d (offset %d)\n",
1462 			rlen, (int)offset);
1463 	#endif
1464 		    return PM_ERR_LOGREC;
1465 	    }
1466 	    if ((pb = __pmFindPDUBuf(rlen + (int)sizeof(__pmPDUHdr))) == NULL) {
1467 	#ifdef PCP_DEBUG
1468 		if (pmDebug & DBG_TRACE_LOG)
1469 		    fprintf(stderr, "\nError: __pmFindPDUBuf(%d) %s\n",
1470 			(int)(rlen + sizeof(__pmPDUHdr)),
1471 			osstrerror());
1472 	#endif
1473 		fseek(f, offset, SEEK_SET);
1474 		return -oserror();
1475 	    }
1476 	
1477 	    if (mode == PM_MODE_BACK)
1478 		fseek(f, -(long)(sizeof(head) + rlen), SEEK_CUR);
1479 	
1480 	    if ((n = (int)fread(&pb[3], 1, rlen, f)) != rlen) {
1481 		/* data read failed */
1482 	#ifdef PCP_DEBUG
1483 		if (pmDebug & DBG_TRACE_LOG)
1484 		    fprintf(stderr, "\nError: data fread got %d expected %d\n", n, rlen);
1485 	#endif
1486 		fseek(f, offset, SEEK_SET);
1487 		if (ferror(f)) {
1488 		    /* I/O error */
1489 		    clearerr(f);
1490 		    return -oserror();
1491 		}
1492 		clearerr(f);
1493 	
1494 		/* corrupted archive */
1495 		return PM_ERR_LOGREC;
1496 	    }
1497 	    else {
1498 		/* swab pdu buffer - done later in __pmDecodeResult */
1499 	    }
1500 	
1501 	    if (mode == PM_MODE_BACK)
1502 		fseek(f, -(long)(rlen + sizeof(head)), SEEK_CUR);
1503 	
1504 	    if ((n = (int)fread(&trail, 1, sizeof(trail), f)) != sizeof(trail)) {
1505 	#ifdef PCP_DEBUG
1506 		if (pmDebug & DBG_TRACE_LOG)
1507 		    fprintf(stderr, "\nError: trailer fread got %d expected %d\n", n, (int)sizeof(trail));
1508 	#endif
1509 		fseek(f, offset, SEEK_SET);
1510 		if (ferror(f)) {
1511 		    /* I/O error */
1512 		    clearerr(f);
1513 		    return -oserror();
1514 		}
1515 		clearerr(f);
1516 	
1517 		/* corrupted archive */
1518 		return PM_ERR_LOGREC;
1519 	    }
1520 	    else {
1521 		/* swab trail */
1522 		trail = ntohl(trail);
1523 	    }
1524 	
1525 	    if (trail != head) {
1526 	#ifdef PCP_DEBUG
1527 		if (pmDebug & DBG_TRACE_LOG)
1528 		    fprintf(stderr, "\nError: record length mismatch: header (%d) != trailer (%d)\n", head, trail);
1529 	#endif
1530 		return PM_ERR_LOGREC;
1531 	    }
1532 	
1533 	    if (seeking_end && paranoidCheck(head, pb) == -1)
1534 		return PM_ERR_LOGREC;
1535 	
1536 	    if (mode == PM_MODE_BACK)
1537 		fseek(f, -(long)sizeof(trail), SEEK_CUR);
1538 	
1539 	    __pmOverrideLastFd(fileno(f));
1540 	    sts = __pmDecodeResult(pb, result); /* also swabs the result */
1541 	    /* note, PDU buffer (pb) is now pinned */
1542 	
1543 	#ifdef PCP_DEBUG
1544 	    if (pmDebug & DBG_TRACE_LOG) {
1545 		head -= sizeof(head) + sizeof(trail);
1546 		fprintf(stderr, "@");
1547 		if (sts >= 0) {
1548 		    __pmTimeval	tmp;
1549 		    printstamp(&(*result)->timestamp);
1550 		    tmp.tv_sec = (__int32_t)(*result)->timestamp.tv_sec;
1551 		    tmp.tv_usec = (__int32_t)(*result)->timestamp.tv_usec;
1552 		    fprintf(stderr, " (t=%.6f)", __pmTimevalSub(&tmp, &lcp->l_label.ill_start));
1553 		}
1554 		else
1555 		    fprintf(stderr, "unknown time");
1556 		fprintf(stderr, " len=header+%d+trailer\n", head);
1557 	    }
1558 	#endif
1559 	
1560 	    /* exported to indicate how efficient we are ... */
1561 	    __pmLogReads++;
1562 	
1563 	    if (sts < 0) {
1564 		__pmUnpinPDUBuf(pb);
1565 		return PM_ERR_LOGREC;
1566 	    }
1567 	#ifdef PCP_DEBUG
1568 	    if (pmDebug & DBG_TRACE_PDU) {
1569 		fprintf(stderr, "__pmLogRead timestamp=");
1570 		printstamp(&(*result)->timestamp);
1571 		fprintf(stderr, " " PRINTF_P_PFX "%p ... " PRINTF_P_PFX "%p", &pb[3], &pb[head/sizeof(__pmPDU)+3]);
1572 		fputc('\n', stderr);
1573 		dumpbuf(rlen, &pb[3]);		/* see above to explain "3" */
1574 	    }
1575 	#endif
1576 	
1577 	    return 0;
1578 	}
1579 	
1580 	static int
1581 	check_all_derived(int numpmid, pmID pmidlist[])
1582 	{
1583 	    int	i;
1584 	
1585 	    /*
1586 	     * Special case ... if we ONLY have derived metrics in the input
1587 	     * pmidlist then all the derived metrics must be constant
1588 	     * expressions, so skip all the processing.
1589 	     * Derived metrics have domain == DYNAMIC_PMID and item != 0.
1590 	     * This rare, but avoids reading to the end of an archive
1591 	     * for no good reason.
1592 	     */
1593 	
1594 	    for (i = 0; i < numpmid; i++) {
1595 		if (pmid_domain(pmidlist[i]) != DYNAMIC_PMID ||
1596 		    pmid_item(pmidlist[i]) == 0)
1597 		    return 0;
1598 	    }
1599 	    return 1;
1600 	}
1601 	
1602 	int
1603 	__pmLogFetch(__pmContext *ctxp, int numpmid, pmID pmidlist[], pmResult **result)
1604 	{
1605 	    int		i;
1606 	    int		j;
1607 	    int		u;
1608 	    int		all_derived;
1609 	    int		sts = 0;
1610 	    int		found;
1611 	    double	tdiff;
1612 	    pmResult	*newres;
1613 	    pmDesc	desc;
1614 	    int		kval;
1615 	    __pmHashNode	*hp;
1616 	    pmid_ctl	*pcp;
1617 	    int		nskip;
1618 	    __pmTimeval	tmp;
1619 	    int		ctxp_mode = ctxp->c_mode & __PM_MODE_MASK;
1620 	
1621 	    if (ctxp_mode == PM_MODE_INTERP) {
1622 		return __pmLogFetchInterp(ctxp, numpmid, pmidlist, result);
1623 	    }
1624 	
1625 	    all_derived = check_all_derived(numpmid, pmidlist);
1626 	
1627 	    /* re-establish position */
1628 	    __pmLogChangeVol(ctxp->c_archctl->ac_log, ctxp->c_archctl->ac_vol);
1629 	    fseek(ctxp->c_archctl->ac_log->l_mfp, 
1630 		    (long)ctxp->c_archctl->ac_offset, SEEK_SET);
1631 	
1632 	more:
1633 	
1634 	    found = 0;
1635 	    nskip = 0;
1636 	    *result = NULL;
1637 	    while (!found) {
1638 		if (ctxp->c_archctl->ac_serial == 0) {
1639 		    /*
1640 		     * no serial access, so need to make sure we are
1641 		     * starting in the correct place
1642 		     */
1643 		    int		tmp_mode;
1644 		    nskip = 0;
1645 		    if (ctxp_mode == PM_MODE_FORW)
1646 			tmp_mode = PM_MODE_BACK;
1647 		    else
1648 			tmp_mode = PM_MODE_FORW;
1649 		    while (__pmLogRead(ctxp->c_archctl->ac_log, tmp_mode, NULL, result) >= 0) {
1650 			nskip++;
1651 			tmp.tv_sec = (__int32_t)(*result)->timestamp.tv_sec;
1652 			tmp.tv_usec = (__int32_t)(*result)->timestamp.tv_usec;
1653 			tdiff = __pmTimevalSub(&tmp, &ctxp->c_origin);
1654 			if ((tdiff < 0 && ctxp_mode == PM_MODE_FORW) ||
1655 			    (tdiff > 0 && ctxp_mode == PM_MODE_BACK)) {
1656 			    pmFreeResult(*result);
1657 			    *result = NULL;
1658 			    break;
1659 			}
1660 			else if (tdiff == 0) {
1661 			    /* exactly the one we wanted */
1662 			    found = 1;
1663 			    break;
1664 			}
1665 			pmFreeResult(*result);
1666 			*result = NULL;
1667 		    }
1668 		    ctxp->c_archctl->ac_serial = 1;
1669 	#ifdef PCP_DEBUG
1670 		    if (pmDebug & DBG_TRACE_LOG) {
1671 			if (nskip) {
1672 			    fprintf(stderr, "__pmLogFetch: ctx=%d skip reverse %d to ",
1673 				pmWhichContext(), nskip);
1674 			    if (*result  != NULL)
1675 				printstamp(&(*result)->timestamp);
1676 			    else
1677 				fprintf(stderr, "unknown time");
1678 			    fprintf(stderr, ", found=%d\n", found);
1679 			}
1680 	#ifdef DESPERATE
1681 			else
1682 			    fprintf(stderr, "__pmLogFetch: ctx=%d no skip reverse\n",
1683 				pmWhichContext());
1684 	#endif
1685 		    }
1686 	#endif
1687 		    nskip = 0;
1688 		}
1689 		if (found)
1690 		    break;
1691 		if ((sts = __pmLogRead(ctxp->c_archctl->ac_log, ctxp->c_mode, NULL, result)) < 0)
1692 		    break;
1693 		tmp.tv_sec = (__int32_t)(*result)->timestamp.tv_sec;
1694 		tmp.tv_usec = (__int32_t)(*result)->timestamp.tv_usec;
1695 		tdiff = __pmTimevalSub(&tmp, &ctxp->c_origin);
1696 		if ((tdiff < 0 && ctxp_mode == PM_MODE_FORW) ||
1697 		    (tdiff > 0 && ctxp_mode == PM_MODE_BACK)) {
1698 			nskip++;
1699 			pmFreeResult(*result);
1700 			*result = NULL;
1701 			continue;
1702 		}
1703 		found = 1;
1704 	#ifdef PCP_DEBUG
1705 		if (pmDebug & DBG_TRACE_LOG) {
1706 		    if (nskip) {
1707 			fprintf(stderr, "__pmLogFetch: ctx=%d skip %d to ",
1708 			    pmWhichContext(), nskip);
1709 			    printstamp(&(*result)->timestamp);
1710 			    fputc('\n', stderr);
1711 			}
1712 	#ifdef DESPERATE
1713 		    else
1714 			fprintf(stderr, "__pmLogFetch: ctx=%d no skip\n",
1715 			    pmWhichContext());
1716 	#endif
1717 		}
1718 	#endif
1719 	    }
1720 	    if (found) {
1721 		ctxp->c_origin.tv_sec = (__int32_t)(*result)->timestamp.tv_sec;
1722 		ctxp->c_origin.tv_usec = (__int32_t)(*result)->timestamp.tv_usec;
1723 	    }
1724 	
1725 	    if (*result != NULL && (*result)->numpmid == 0) {
1726 		/*
1727 		 * mark record, and not interpolating ...
1728 		 * if pmFetchArchive(), return it
1729 		 * otherwise keep searching
1730 		 */
1731 		if (numpmid == 0)
1732 		    newres = *result;
1733 		else {
1734 		    pmFreeResult(*result);
1735 		    goto more;
1736 		}
1737 	    }
1738 	    else if (found) {
1739 		if (numpmid > 0) {
1740 		    /*
1741 		     * not necesssarily after them all, so cherry-pick the metrics
1742 		     * we wanted ..
1743 		     * there are two tricks here ...
1744 		     * (1) pmValueSets for metrics requested, but not in the pmResult
1745 		     *     from the log are assigned using the first two fields in the
1746 		     *     pmid_ctl struct -- since these are allocated once as
1747 		     *	   needed, and never free'd, we have to make sure pmFreeResult
1748 		     *     finds a pmValueSet in a pinned pdu buffer ... this means
1749 		     *     we must find at least one real value from the log to go
1750 		     *     with any "unavailable" results
1751 		     * (2) real pmValueSets can be ignored, they are in a pdubuf
1752 		     *     and will be reclaimed when the buffer is unpinned in
1753 		     *     pmFreeResult
1754 		     */
1755 	
1756 		    i = (int)sizeof(pmResult) + numpmid * (int)sizeof(pmValueSet *);
1757 		    if ((newres = (pmResult *)malloc(i)) == NULL) {
1758 			__pmNoMem("__pmLogFetch.newres", i, PM_FATAL_ERR);
1759 		    }
1760 		    newres->numpmid = numpmid;
1761 		    newres->timestamp = (*result)->timestamp;
1762 		    u = 0;
1763 		    for (j = 0; j < numpmid; j++) {
1764 			hp = __pmHashSearch((int)pmidlist[j], &pc_hc);
1765 			if (hp == NULL) {
1766 			    /* first time we've been asked for this one */
1767 			    if ((pcp = (pmid_ctl *)malloc(sizeof(pmid_ctl))) == NULL) {
1768 				__pmNoMem("__pmLogFetch.pmid_ctl", sizeof(pmid_ctl), PM_FATAL_ERR);
1769 			    }
1770 			    pcp->pc_pmid = pmidlist[j];
1771 			    pcp->pc_numval = 0;
1772 			    sts = __pmHashAdd((int)pmidlist[j], (void *)pcp, &pc_hc);
1773 			    if (sts < 0)
1774 				return sts;
1775 			}
1776 			else
1777 			    pcp = (pmid_ctl *)hp->data;
1778 			for (i = 0; i < (*result)->numpmid; i++) {
1779 			    if (pmidlist[j] == (*result)->vset[i]->pmid) {
1780 				/* match */
1781 				newres->vset[j] = (*result)->vset[i];
1782 				u++;
1783 				break;
1784 			    }
1785 			}
1786 			if (i == (*result)->numpmid) {
1787 			    /*
1788 			     * requested metric not returned from the log, construct
1789 			     * a "no values available" pmValueSet from the pmid_ctl
1790 			     */
1791 			    newres->vset[j] = (pmValueSet *)pcp;
1792 			}
1793 		    }
1794 		    if (u == 0 && !all_derived) {
1795 			/*
1796 			 * not one of our pmids was in the log record, try
1797 			 * another log record ...
1798 			 */
1799 			pmFreeResult(*result);
1800 			free(newres);
1801 			goto more;
1802 		    }
1803 		    /*
1804 		     * *result malloc'd in __pmLogRead, but vset[]'s are either in
1805 		     * pdubuf or the pmid_ctl struct
1806 		     */
1807 		    free(*result);
1808 		    *result = newres;
1809 		}
1810 		else
1811 		    /* numpmid == 0, pmFetchArchive() call */
1812 		    newres = *result;
1813 		/*
1814 		 * Apply instance profile filtering ...
1815 		 * Note. This is a little strange, as in the numpmid == 0,
1816 		 *       pmFetchArchive() case, this for-loop is not executed ...
1817 		 *       this is correct, the instance profile is ignored for
1818 		 *       pmFetchArchive()
1819 		 */
1820 		for (i = 0; i < numpmid; i++) {
1821 		    if (newres->vset[i]->numval <= 0) {
1822 			/*
1823 			 * no need to xlate numval for an error ... already done
1824 			 * below __pmLogRead() in __pmDecodeResult() ... also xlate
1825 			 * here would have been skipped in the pmFetchArchive() case
1826 			 */
1827 			continue;
1828 		    }
1829 		    sts = __pmLogLookupDesc(ctxp->c_archctl->ac_log, newres->vset[i]->pmid, &desc);
1830 		    if (sts < 0) {
1831 			__pmNotifyErr(LOG_WARNING, "__pmLogFetch: missing pmDesc for pmID %s: %s",
1832 				    pmIDStr(desc.pmid), pmErrStr(sts));
1833 			pmFreeResult(newres);
1834 			break;
1835 		    }
1836 		    if (desc.indom == PM_INDOM_NULL)
1837 			/* no instance filtering to be done for these ones */
1838 			continue;
1839 	
1840 		    /*
1841 		     * scan instances, keeping those "in" the instance profile
1842 		     *
1843 		     * WARNING
1844 		     *		This compresses the pmValueSet INSITU, and since
1845 		     *		these are in a pdu buffer, it trashes the the
1846 		     *		pdu buffer and means there is no clever way of
1847 		     *		re-using the pdu buffer to satisfy multiple
1848 		     *		pmFetch requests
1849 		     *		Fortunately, stdio buffering means copying to
1850 		     *		make additional pdu buffers is not too expensive.
1851 		     */
1852 		    kval = 0;
1853 		    for (j = 0; j < newres->vset[i]->numval; j++) {
1854 			if (__pmInProfile(desc.indom, ctxp->c_instprof, newres->vset[i]->vlist[j].inst)) {
1855 			    if (kval != j)
1856 				 /* struct assignment */
1857 				 newres->vset[i]->vlist[kval] = newres->vset[i]->vlist[j];
1858 			    kval++;
1859 			}
1860 		    }
1861 		    newres->vset[i]->numval = kval;
1862 		}
1863 	    }
1864 	
1865 	    /* remember your position in this context */
1866 	    ctxp->c_archctl->ac_offset = ftell(ctxp->c_archctl->ac_log->l_mfp);
1867 	    ctxp->c_archctl->ac_vol = ctxp->c_archctl->ac_log->l_curvol;
1868 	
1869 	    return sts;
1870 	}
1871 	
1872 	/*
1873 	 * error handling wrapper around __pmLogChangeVol() to deal with
1874 	 * missing volumes ... return lcp->l_ti[] index for entry matching
1875 	 * success
1876 	 */
1877 	static int
1878 	VolSkip(__pmLogCtl *lcp, int mode,  int j)
1879 	{
1880 	    int		vol = lcp->l_ti[j].ti_vol;
1881 	
1882 	    while (lcp->l_minvol <= vol && vol <= lcp->l_maxvol) {
1883 		if (__pmLogChangeVol(lcp, vol) >= 0)
1884 		    return j;
1885 	#ifdef PCP_DEBUG
1886 		if (pmDebug & DBG_TRACE_LOG) {
1887 		    fprintf(stderr, "VolSkip: Skip missing vol %d\n", vol);
1888 		}
1889 	#endif
1890 		if (mode == PM_MODE_FORW) {
1891 		    for (j++; j < lcp->l_numti; j++)
1892 			if (lcp->l_ti[j].ti_vol != vol)
1893 			    break;
1894 		    if (j == lcp->l_numti)
1895 			return PM_ERR_EOL;
1896 		    vol = lcp->l_ti[j].ti_vol;
1897 		}
1898 		else {
1899 		    for (j--; j >= 0; j--)
1900 			if (lcp->l_ti[j].ti_vol != vol)
1901 			    break;
1902 		    if (j < 0)
1903 			return PM_ERR_EOL;
1904 		    vol = lcp->l_ti[j].ti_vol;
1905 		}
1906 	    }
1907 	    return PM_ERR_EOL;
1908 	}
1909 	
1910 	void
1911 	__pmLogSetTime(__pmContext *ctxp)
1912 	{
1913 	    __pmLogCtl	*lcp = ctxp->c_archctl->ac_log;
1914 	    int		mode;
1915 	
1916 	    mode = ctxp->c_mode & __PM_MODE_MASK; /* strip XTB data */
1917 	
1918 	    if (mode == PM_MODE_INTERP)
1919 		mode = ctxp->c_delta > 0 ? PM_MODE_FORW : PM_MODE_BACK;
1920 	
1921 	#ifdef PCP_DEBUG
1922 	    if (pmDebug & DBG_TRACE_LOG) {
1923 		fprintf(stderr, "__pmLogSetTime(%d) ", pmWhichContext());
1924 		printstamp32(&ctxp->c_origin);
1925 		fprintf(stderr, " delta=%d", ctxp->c_delta);
1926 	    }
1927 	#endif
1928 	
1929 	    if (lcp->l_numti) {
1930 		/* we have a temporal index, use it! */
1931 		int		i;
1932 		int		j = -1;
1933 		int		toobig = 0;
1934 		int		match = 0;
1935 		int		numti = lcp->l_numti;
1936 		__pmLogTI	*tip = lcp->l_ti;
1937 		double		t_hi;
1938 		double		t_lo;
1939 		struct stat	sbuf;
1940 	
1941 		sbuf.st_size = -1;
1942 	
1943 		for (i = 0; i < numti; i++, tip++) {
1944 		    if (tip->ti_vol < lcp->l_minvol)
1945 			/* skip missing preliminary volumes */
1946 			continue;
1947 		    if (tip->ti_vol == lcp->l_maxvol) {
1948 			/* truncated check for last volume */
1949 			if (sbuf.st_size < 0) {
1950 			    FILE	*f = _logpeek(lcp, lcp->l_maxvol);
1951 	
1952 			    sbuf.st_size = 0;
1953 			    if (f != NULL) {
1954 				fstat(fileno(f), &sbuf);
1955 				fclose(f);
1956 			    }
1957 			}
1958 			if (tip->ti_log > sbuf.st_size) {
1959 			    j = i;
1960 			    toobig++;
1961 			    break;
1962 			}
1963 		    }
1964 		    t_hi = __pmTimevalSub(&tip->ti_stamp, &ctxp->c_origin);
1965 		    if (t_hi > 0) {
1966 			j = i;
1967 			break;
1968 		    }
1969 		    else if (t_hi == 0) {
1970 			j = i;
1971 			match = 1;
1972 			break;
1973 		    }
1974 		}
1975 		if (i == numti)
1976 		    j = numti;
1977 	
1978 		ctxp->c_archctl->ac_serial = 1;
1979 	
1980 		if (match) {
1981 		    j = VolSkip(lcp, mode, j);
1982 		    if (j < 0)
1983 			return;
1984 		    fseek(lcp->l_mfp, (long)lcp->l_ti[j].ti_log, SEEK_SET);
1985 		    if (mode == PM_MODE_BACK)
1986 			ctxp->c_archctl->ac_serial = 0;
1987 	#ifdef PCP_DEBUG
1988 		    if (pmDebug & DBG_TRACE_LOG) {
1989 			fprintf(stderr, " at ti[%d]@", j);
1990 			printstamp32(&lcp->l_ti[j].ti_stamp);
1991 		    }
1992 	#endif
1993 		}
1994 		else if (j < 1) {
1995 		    j = VolSkip(lcp, PM_MODE_FORW, 0);
1996 		    if (j < 0)
1997 			return;
1998 		    fseek(lcp->l_mfp, (long)lcp->l_ti[j].ti_log, SEEK_SET);
1999 	#ifdef PCP_DEBUG
2000 		    if (pmDebug & DBG_TRACE_LOG) {
2001 			fprintf(stderr, " before start ti@");
2002 			printstamp32(&lcp->l_ti[j].ti_stamp);
2003 		    }
2004 	#endif
2005 		}
2006 		else if (j == numti) {
2007 		    j = VolSkip(lcp, PM_MODE_BACK, numti-1);
2008 		    if (j < 0)
2009 			return;
2010 		    fseek(lcp->l_mfp, (long)lcp->l_ti[j].ti_log, SEEK_SET);
2011 		    if (mode == PM_MODE_BACK)
2012 			ctxp->c_archctl->ac_serial = 0;
2013 	#ifdef PCP_DEBUG
2014 		    if (pmDebug & DBG_TRACE_LOG) {
2015 			fprintf(stderr, " after end ti@");
2016 			printstamp32(&lcp->l_ti[j].ti_stamp);
2017 		    }
2018 	#endif
2019 		}
2020 		else {
2021 		    /*
2022 		     *    [j-1]             [origin]           [j]
2023 		     *      <----- t_lo -------><----- t_hi ---->
2024 		     *
2025 		     * choose closest index point.  if toobig, [j] is not
2026 		     * really valid (log truncated or incomplete)
2027 		     */
2028 		    t_hi = __pmTimevalSub(&lcp->l_ti[j].ti_stamp, &ctxp->c_origin);
2029 		    t_lo = __pmTimevalSub(&ctxp->c_origin, &lcp->l_ti[j-1].ti_stamp);
2030 		    if (t_hi <= t_lo && !toobig) {
2031 			j = VolSkip(lcp, mode, j);
2032 			if (j < 0)
2033 			    return;
2034 			fseek(lcp->l_mfp, (long)lcp->l_ti[j].ti_log, SEEK_SET);
2035 			if (mode == PM_MODE_FORW)
2036 			    ctxp->c_archctl->ac_serial = 0;
2037 	#ifdef PCP_DEBUG
2038 			if (pmDebug & DBG_TRACE_LOG) {
2039 			    fprintf(stderr, " before ti[%d]@", j);
2040 			    printstamp32(&lcp->l_ti[j].ti_stamp);
2041 			}
2042 	#endif
2043 		    }
2044 		    else {
2045 			j = VolSkip(lcp, mode, j-1);
2046 			if (j < 0)
2047 			    return;
2048 			fseek(lcp->l_mfp, (long)lcp->l_ti[j].ti_log, SEEK_SET);
2049 			if (mode == PM_MODE_BACK)
2050 			    ctxp->c_archctl->ac_serial = 0;
2051 	#ifdef PCP_DEBUG
2052 			if (pmDebug & DBG_TRACE_LOG) {
2053 			    fprintf(stderr, " after ti[%d]@", j);
2054 			    printstamp32(&lcp->l_ti[j].ti_stamp);
2055 			}
2056 	#endif
2057 		    }
2058 		    if (ctxp->c_archctl->ac_serial && mode == PM_MODE_FORW) {
2059 			/*
2060 			 * back up one record ...
2061 			 * index points to the END of the record!
2062 			 */
2063 			pmResult	*result;
2064 	#ifdef PCP_DEBUG
2065 			if (pmDebug & DBG_TRACE_LOG)
2066 			    fprintf(stderr, " back up ...\n");
2067 	#endif
2068 			if (__pmLogRead(lcp, PM_MODE_BACK, NULL, &result) >= 0)
2069 			    pmFreeResult(result);
2070 	#ifdef PCP_DEBUG
2071 			if (pmDebug & DBG_TRACE_LOG)
2072 			    fprintf(stderr, "...");
2073 	#endif
2074 		    }
2075 		}
2076 	    }
2077 	    else {
2078 		/* index either not available, or not useful */
2079 		if (mode == PM_MODE_FORW) {
2080 		    __pmLogChangeVol(lcp, lcp->l_minvol);
2081 		    fseek(lcp->l_mfp, (long)(sizeof(__pmLogLabel) + 2*sizeof(int)), SEEK_SET);
2082 		}
2083 		else if (mode == PM_MODE_BACK) {
2084 		    __pmLogChangeVol(lcp, lcp->l_maxvol);
2085 		    fseek(lcp->l_mfp, (long)0, SEEK_END);
2086 		}
2087 	
2088 	#ifdef PCP_DEBUG
2089 		if (pmDebug & DBG_TRACE_LOG)
2090 		    fprintf(stderr, " index not useful\n");
2091 	#endif
2092 	    }
2093 	
2094 	#ifdef PCP_DEBUG
2095 	    if (pmDebug & DBG_TRACE_LOG)
2096 		fprintf(stderr, " vol=%d posn=%ld serial=%d\n",
2097 		    lcp->l_curvol, (long)ftell(lcp->l_mfp), ctxp->c_archctl->ac_serial);
2098 	#endif
2099 	
2100 	    /* remember your position in this context */
2101 	    ctxp->c_archctl->ac_offset = ftell(lcp->l_mfp);
2102 	    ctxp->c_archctl->ac_vol = ctxp->c_archctl->ac_log->l_curvol;
2103 	}
2104 	
2105 	int
2106 	pmGetArchiveLabel(pmLogLabel *lp)
2107 	{
2108 	    __pmContext		*ctxp;
2109 	    ctxp = __pmHandleToPtr(pmWhichContext());
2110 	    if (ctxp == NULL || ctxp->c_type != PM_CONTEXT_ARCHIVE)
2111 		return PM_ERR_NOCONTEXT;
2112 	    else {
2113 		__pmLogLabel	*rlp;
2114 		/*
2115 		 * we have to copy the structure to hide the differences
2116 		 * between the internal __pmTimeval and the external struct timeval
2117 		 */
2118 		rlp = &ctxp->c_archctl->ac_log->l_label;
2119 		lp->ll_magic = rlp->ill_magic;
2120 		lp->ll_pid = (pid_t)rlp->ill_pid;
2121 		lp->ll_start.tv_sec = rlp->ill_start.tv_sec;
2122 		lp->ll_start.tv_usec = rlp->ill_start.tv_usec;
2123 		memcpy(lp->ll_hostname, rlp->ill_hostname, PM_LOG_MAXHOSTLEN);
2124 		memcpy(lp->ll_tz, rlp->ill_tz, sizeof(lp->ll_tz));
2125 		return 0;
2126 	    }
2127 	}
2128 	
2129 	int
2130 	pmGetArchiveEnd(struct timeval *tp)
2131 	{
2132 	    /*
2133 	     * set l_physend and l_endtime
2134 	     * at the end of ... ctxp->c_archctl->ac_log
2135 	     */
2136 	    __pmContext	*ctxp;
2137 	
2138 	    ctxp = __pmHandleToPtr(pmWhichContext());
2139 	    if (ctxp == NULL || ctxp->c_type != PM_CONTEXT_ARCHIVE)
2140 		return PM_ERR_NOCONTEXT;
2141 	    return __pmGetArchiveEnd(ctxp->c_archctl->ac_log, tp);
2142 	}
2143 	
2144 	int
2145 	__pmGetArchiveEnd(__pmLogCtl *lcp, struct timeval *tp)
2146 	{
2147 	    struct stat	sbuf;
2148 	    FILE	*f;
2149 	    long	save = 0;
2150 	    pmResult	*rp = NULL;
2151 	    pmResult	*nrp;
2152 	    int		i;
2153 	    int		sts;
2154 	    int		found;
2155 	    int		head;
2156 	    long	offset;
2157 	    int		vol;
2158 	    __pm_off_t	logend;
2159 	    __pm_off_t	physend = 0;
2160 	
2161 	    /*
2162 	     * expect things to be stable, so l_maxvol is not empty, and
2163 	     * l_physend does not change for l_maxvol ... the ugliness is
2164 	     * to handle situations where these expectations are not met
2165 	     */
2166 	    found = 0;
2167 	    sts = PM_ERR_LOGREC;	/* default error condition */
2168 	    f = NULL;
2169 	    for (vol = lcp->l_maxvol; vol >= lcp->l_minvol; vol--) {
2170 		if (lcp->l_curvol == vol) {
2171 		    f = lcp->l_mfp;
2172 		    save = ftell(f);
2173 		}
2174 		else if ((f = _logpeek(lcp, vol)) == NULL) {
2175 		    sts = -oserror();
2176 		    break;
2177 		}
2178 	
2179 		if (fstat(fileno(f), &sbuf) < 0) {
2180 		    sts = -oserror();
2181 		    break;
2182 		}
2183 	
2184 		if (vol == lcp->l_maxvol && sbuf.st_size == lcp->l_physend) {
2185 		    /* nothing changed, return cached stuff */
2186 		    tp->tv_sec = lcp->l_endtime.tv_sec;
2187 		    tp->tv_usec = lcp->l_endtime.tv_usec;
2188 		    sts = 0;
2189 		    break;
2190 		}
2191 	
2192 		/* if this volume is empty, try previous volume */
2193 		if (sbuf.st_size <= (int)sizeof(__pmLogLabel) + 2*(int)sizeof(int)) {
2194 		    if (f != lcp->l_mfp) {
2195 			fclose(f);
2196 			f = NULL;
2197 		    }
2198 		    continue;
2199 		}
2200 	
2201 		physend = (__pm_off_t)sbuf.st_size;
2202 		if (sizeof(off_t) > sizeof(__pm_off_t)) {
2203 		    if (physend != sbuf.st_size) {
2204 			__pmNotifyErr(LOG_ERR, "pmGetArchiveEnd: PCP archive file"
2205 				" (meta) too big (%"PRIi64" bytes)\n",
2206 				(uint64_t)sbuf.st_size);
2207 			exit(1);
2208 		    }
2209 		}
2210 	
2211 		/* try to read backwards for the last physical record ... */
2212 		fseek(f, (long)physend, SEEK_SET);
2213 		if (paranoidLogRead(lcp, PM_MODE_BACK, f, &rp) >= 0) {
2214 		    /* success, we are done! */
2215 		    found = 1;
2216 		    break;
2217 		}
2218 	
2219 		/*
2220 		 * failure at the physical end of file may be related to a truncted
2221 		 * block flush for a growing archive.  Scan temporal index, and use
2222 		 * last entry at or before end of physical file for this volume
2223 		 */
2224 		logend = (int)sizeof(__pmLogLabel) + 2*(int)sizeof(int);
2225 		for (i = lcp->l_numti - 1; i >= 0; i--) {
2226 		    if (lcp->l_ti[i].ti_vol != vol)
2227 			continue;
2228 		    if (lcp->l_ti[i].ti_log <= physend) {
2229 			logend = lcp->l_ti[i].ti_log;
2230 			break;
2231 		    }
2232 		}
2233 	
2234 		/*
2235 		 * Now chase it forwards from the last index entry ...
2236 		 *
2237 		 * BUG 357003 - pmchart can't read archive file
2238 		 *	turns out the index may point to the _end_ of the last
2239 		 *	valid record, so if not at start of volume, back up one
2240 		 *	record, then scan forwards.
2241 		 */
2242 		fseek(f, (long)logend, SEEK_SET);
2243 		if (logend > (int)sizeof(__pmLogLabel) + 2*(int)sizeof(int)) {
2244 		    if (paranoidLogRead(lcp, PM_MODE_BACK, f, &rp) < 0) {
2245 			/* this is badly damaged! */
2246 	#ifdef PCP_DEBUG
2247 			if (pmDebug & DBG_TRACE_LOG) {
2248 			    fprintf(stderr, "pmGetArchiveEnd: "
2249 	                            "Error reading record ending at posn=%d ti[%d]@",
2250 				    logend, i);
2251 			    printstamp32(&lcp->l_ti[i].ti_stamp);
2252 			    fputc('\n', stderr);
2253 			}
2254 	#endif
2255 			break;
2256 		    }
2257 		}
2258 	
2259 	        /* Keep reading records from "logend" until can do so no more... */
2260 		for ( ; ; ) {
2261 		    offset = ftell(f);
2262 		    if ((int)fread(&head, 1, sizeof(head), f) != sizeof(head))
2263 			/* cannot read header for log record !!?? */
2264 			break;
2265 		    head = ntohl(head);
2266 		    if (offset + head > physend)
2267 			/* last record is incomplete */
2268 			break;
2269 		    fseek(f, offset, SEEK_SET);
2270 		    if (paranoidLogRead(lcp, PM_MODE_FORW, f, &nrp) < 0)
2271 			/* this record is truncated, or bad, we lose! */
2272 			break;
2273 		    /* this one is ok, remember it as it may be the last one */
2274 		    found = 1;
2275 		    if (rp != NULL)
2276 			pmFreeResult(rp);
2277 		    rp = nrp;
2278 		}
2279 		if (found)
2280 		    break;
2281 	
2282 		/*
2283 		 * this probably means this volume contains no useful records,
2284 		 * try the previous volume
2285 		 */
2286 	    }/*for*/
2287 	
2288 	    if (f == lcp->l_mfp)
2289 		fseek(f, save, SEEK_SET); /* restore file pointer in current vol */ 
2290 	    else if (f != NULL)
2291 		/* temporary FILE * from _logpeek() */
2292 		fclose(f);
2293 	
2294 	    if (found) {
2295 		tp->tv_sec = (time_t)rp->timestamp.tv_sec;
2296 		tp->tv_usec = (int)rp->timestamp.tv_usec;
2297 		if (vol == lcp->l_maxvol) {
2298 		    lcp->l_endtime.tv_sec = (__int32_t)rp->timestamp.tv_sec;
2299 		    lcp->l_endtime.tv_usec = (__int32_t)rp->timestamp.tv_usec;
2300 		    lcp->l_physend = physend;
2301 		}
2302 		pmFreeResult(rp);
2303 		sts = 0;
2304 	    }
2305 	
2306 	    return sts;
2307 	}