1    	/*
2    	 * pmlogextract - extract desired metrics from PCP archive logs
3    	 *
4    	 * Copyright (c) 1997-2002 Silicon Graphics, Inc.  All Rights Reserved.
5    	 * 
6    	 * This program is free software; you can redistribute it and/or modify it
7    	 * under the terms of the GNU General Public License as published by the
8    	 * Free Software Foundation; either version 2 of the License, or (at your
9    	 * option) any later version.
10   	 * 
11   	 * This program is distributed in the hope that it will be useful, but
12   	 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13   	 * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14   	 * for more details.
15   	 */
16   	
17   	#include <math.h>
18   	#include <ctype.h>
19   	#include <sys/stat.h>
20   	#include "pmapi.h"
21   	#include "impl.h"
22   	#include "logger.h"
23   	
24   	#ifdef PCP_DEBUG
25   	long totalmalloc = 0;
26   	#endif
27   	
28   	static pmUnits nullunits = { 0,0,0,0,0,0 };
29   	
30   	static int desperate = 0;
31   	
32   	/*
33   	 *  Usage
34   	 */
35   	static void
36   	usage (void)
37   	{
38   	#ifdef SHARE_WITH_PMLOGMERGE
39   	    if (!strcmp(pmProgname, "pmlogmerge")) {
40   		fprintf(stderr,
41   	"Usage: %s [options] input-archive output-archive\n\
42   	\n\
43   	Options:\n\
44   	  -f             choose timezone from first archive [default is to use\n\
45   	                 timezone from last archive]\n\
46   	  -S starttime   start of the time window\n\
47   	  -s samples     terminate after this many log records have been written\n\
48   	  -T endtime     end of the time window\n\
49   	  -v samples     switch log volumes after this many samples\n\
50   	  -Z timezone    set reporting timezone\n\
51   	  -z             set reporting timezone to local time of input-archive\n",
52   		pmProgname);
53   	    }
54   	    else 
55   	#endif
56   	    {
57   		fprintf(stderr,
58   	"Usage: %s [options] input-archive output-archive\n\
59   	\n\
60   	Options:\n\
61   	  -c configfile  file to load configuration from\n\
62   	  -d             desperate, save output after fatal error\n\
63   	  -f             choose timezone from first archive [default is to use\n\
64   	                 timezone from last archive]\n\
65   	  -n pmnsfile    use an alternative PMNS\n\
66   	  -S starttime   start of the time window\n\
67   	  -s samples     terminate after this many log records have been written\n\
68   	  -T endtime     end of the time window\n\
69   	  -v samples     switch log volumes after this many samples\n\
70   	  -w             ignore day/month/year\n\
71   	  -Z timezone    set reporting timezone\n\
72   	  -z             set reporting timezone to local time of input-archive\n",
73   		pmProgname);
74   	    }
75   	}
76   	
77   	const char *
78   	metricname(pmID pmid)
79   	{
80   	    static char	*name = NULL;
81   	    if (name != NULL) {
82   		free(name);
83   		name = NULL;
84   	    }
85   	    if (pmNameID(pmid, &name) == 0)
86   		return(name);
87   	    name = NULL;
88   	    return pmIDStr(pmid);
89   	}
90   	
91   	/*
92   	 *  global constants
93   	 */
94   	#define LOG			0
95   	#define META			1
96   	#define LOG_META		2
97   	#define NUM_SEC_PER_DAY		86400
98   	
99   	#define NOT_WRITTEN		0
100  	#define MARK_FOR_WRITE		1
101  	#define WRITTEN			2
102  	
103  	/*
104  	 *  reclist_t is in logger.h
105  	 *	(list of pdu's to write out at start of time window)
106  	 */
107  	
108  	/*
109  	 *  Input archive control is in logger.h
110  	 */
111  	
112  	
113  	/*
114  	 *  PDU for pmResult (PDU_RESULT)
115  	 */
116  	typedef struct {
117  	    pmID		pmid;
118  	    int			numval;		/* no. of vlist els to follow, or err */
119  	    int			valfmt;		/* insitu or pointer */
120  	    __pmValue_PDU	vlist[1];	/* zero or more */
121  	} vlist_t;
122  	
123  	/*
124  	 *  This struct is not needed?
125  	 */
126  	typedef struct {
127  	    /* __pmPDUHdr		hdr; */
128  	    __pmPDU		hdr;
129  	    __pmTimeval		timestamp;	/* when returned */
130  	    int			numpmid;	/* no. of PMIDs to follow */
131  	    __pmPDU		data[1];	/* zero or more */
132  	} result_t;
133  	
134  	/*
135  	 *  Mark record
136  	 */
137  	typedef struct {
138  	    __pmPDU		len;
139  	    __pmPDU		type;
140  	    __pmPDU		from;
141  	    __pmTimeval		timestamp;	/* when returned */
142  	    int			numpmid;	/* zero PMIDs to follow */
143  	} mark_t;
144  	
145  	
146  	/*
147  	 *  Global variables
148  	 */
149  	static int	exit_status = 0;
150  	static int	inarchvers = PM_LOG_VERS02;	/* version of input archive */
151  	static int	outarchvers = PM_LOG_VERS02;	/* version of output archive */
152  	static int	first_datarec = 1;		/* first record flag */
153  	static int	pre_startwin = 1;		/* outside time win flag */
154  	static int	written = 0;			/* num log writes so far */
155  	int		ml_numpmid = 0;			/* num pmid in ml list */
156  	int		ml_size = 0;			/* actual size of ml array */
157  	mlist_t		*ml = NULL;			/* list of pmids with indoms */
158  	rlist_t		*rl = NULL;			/* list of pmResults */
159  	
160  	
161  	off_t		new_log_offset;			/* new log offset */
162  	off_t		new_meta_offset;		/* new meta offset */
163  	off_t		old_log_offset;			/* old log offset */
164  	off_t		old_meta_offset;		/* old meta offset */
165  	static off_t	flushsize = 100000;		/* bytes before flush */
166  	
167  	
168  	/* archive control stuff */
169  	char			*outarchname = NULL;	/* name of output archive */
170  	static __pmHashCtl	mdesc_hash;	/* pmids that have been written */
171  	static __pmHashCtl	mindom_hash;	/* indoms that have been written */
172  	static __pmLogCtl	logctl;		/* output archive control */
173  	inarch_t		*inarch;	/* input archive control(s) */
174  	int			inarchnum;	/* number of input archives */
175  	
176  	int			ilog;		/* index of earliest log */
177  	
178  	static reclist_t	*rlog;		/* log records to be written */
179  	static reclist_t	*rdesc;		/* meta desc records to be written */
180  	static reclist_t	*rindom;	/* meta indom records to be written */
181  	
182  	static __pmTimeval	curlog;		/* most recent timestamp in log */
183  	static __pmTimeval	current;	/* most recent timestamp overall */
184  	
185  	/* time window stuff */
186  	static struct timeval logstart_tval = {0,0};	/* extracted log start */
187  	static struct timeval logend_tval = {0,0};	/* extracted log end */
188  	static struct timeval winstart_tval = {0,0};	/* window start tval*/
189  	static struct timeval winend_tval = {0,0};	/* window end tval*/
190  	
191  	static double 	winstart_time = -1.0;		/* window start time */
192  	static double	winend_time = -1.0;		/* window end time */
193  	static double	logend_time = -1.0;		/* log end time */
194  	
195  	/* command line args */
196  	char	*configfile = NULL;		/* -c arg - name of config file */
197  	int	farg = 0;			/* -f arg - use first timezone */
198  	char	*pmnsfile = PM_NS_DEFAULT;	/* -n arg - alternate namespace */
199  	int	sarg = -1;			/* -s arg - finish after X samples */
200  	char	*Sarg = NULL;			/* -S arg - window start */
201  	char	*Targ = NULL;			/* -T arg - window end */
202  	int	varg = -1;			/* -v arg - switch log vol every X */
203  	int	warg = 0;			/* -w arg - ignore day/month/year */
204  	int	zarg = 0;			/* -z arg - use archive timezone */
205  	char	*tz = NULL;			/* -Z arg - use timezone from user */
206  	
207  	/* cmd line args that could exist, but don't (needed for pmParseTimeWin) */
208  	char	*Aarg = NULL;			/* -A arg - non-existent */
209  	char	*Oarg = NULL;			/* -O arg - non-existent */
210  	
211  	/*--- START FUNCTIONS -------------------------------------------------------*/
212  	
213  	extern int _pmLogGet(__pmLogCtl *, int, __pmPDU **);
214  	extern int _pmLogPut(FILE *, __pmPDU *);
215  	extern void insertresult(rlist_t **, pmResult *);
216  	extern pmResult *searchmlist(pmResult *);
217  	
218  	/*
219  	 *  convert timeval to double
220  	 */
221  	double
222  	tv2double(struct timeval *tv)
223  	{
224  	    return tv->tv_sec + (double)tv->tv_usec / 1000000.0;
225  	}
226  	
227  	static void
228  	abandon()
229  	{
230  	    char    fname[MAXNAMELEN];
231  	    if (desperate == 0) {
232  		fprintf(stderr, "Archive \"%s\" not created.\n", outarchname);
233  		while (logctl.l_curvol >= 0) {
234  		    snprintf(fname, sizeof(fname), "%s.%d", outarchname, logctl.l_curvol);
235  		    unlink(fname);
236  		    logctl.l_curvol--;
237  		}
238  		snprintf(fname, sizeof(fname), "%s.meta", outarchname);
239  		unlink(fname);
240  		snprintf(fname, sizeof(fname), "%s.index", outarchname);
241  		unlink(fname);
242  	    }
243  	    exit(1);
244  	}
245  	
246  	
247  	/*
248  	 *  report that archive is corrupted
249  	 */
250  	static void
251  	_report(FILE *fp)
252  	{
253  	    off_t	here;
254  	    struct stat	sbuf;
255  	
256  	    here = lseek(fileno(fp), 0L, SEEK_CUR);
257  	    fprintf(stderr, "%s: Error occurred at byte offset %ld into a file of",
258  		    pmProgname, (long int)here);
259  	    if (fstat(fileno(fp), &sbuf) < 0)
260  		fprintf(stderr, ": stat: %s\n", osstrerror());
261  	    else
262  		fprintf(stderr, " %ld bytes.\n", (long int)sbuf.st_size);
263  	    fprintf(stderr, "The last record, and the remainder of this file will not be extracted.\n");
264  	    abandon();
265  	}
266  	
267  	
268  	/*
269  	 *  switch output volumes
270  	 */
271  	static void
272  	newvolume(char *base, __pmTimeval *tvp)
273  	{
274  	    FILE		*newfp;
275  	    int			nextvol = logctl.l_curvol + 1;
276  	
277  	    if ((newfp = __pmLogNewFile(base, nextvol)) != NULL) {
278  		struct timeval	stamp;
279  		fclose(logctl.l_mfp);
280  		logctl.l_mfp = newfp;
281  		logctl.l_label.ill_vol = logctl.l_curvol = nextvol;
282  		__pmLogWriteLabel(logctl.l_mfp, &logctl.l_label);
283  		fflush(logctl.l_mfp);
284  		stamp.tv_sec = ntohl(tvp->tv_sec);
285  		stamp.tv_usec = ntohl(tvp->tv_usec);
286  		fprintf(stderr, "%s: New log volume %d, at ", pmProgname, nextvol);
287  		__pmPrintStamp(stderr, &stamp);
288  		fputc('\n', stderr);
289  	    }
290  	    else {
291  		fprintf(stderr, "%s: Error: volume %d: %s\n",
292  			pmProgname, nextvol, pmErrStr(-oserror()));
293  		abandon();
294  	    }
295  	    flushsize = 100000;
296  	}
297  	
298  	
299  	/*
300  	 * construct new external label, and check label records from
301  	 * input archives
302  	 */
303  	static void
304  	newlabel(void)
305  	{
306  	    int		i;
307  	    inarch_t	*iap;
308  	    __pmLogLabel	*lp = &logctl.l_label;
309  	
310  	    /* set outarch to inarch[0] to start off with */
311  	    iap = &inarch[0];
312  	
313  	    /* check version number */
314  	    inarchvers = iap->label.ll_magic & 0xff;
315  	    outarchvers = inarchvers;
316  	
317  	    if (inarchvers != PM_LOG_VERS01 && inarchvers != PM_LOG_VERS02) {
318  		fprintf(stderr,"%s: Error: illegal version number %d in archive (%s)\n",
319  			pmProgname, inarchvers, iap->name);
320  		abandon();
321  	    }
322  	
323  	    /* copy magic number, pid, host and timezone */
324  	    lp->ill_magic = iap->label.ll_magic;
325  	    lp->ill_pid = (int)getpid();
326  	    strncpy(lp->ill_hostname, iap->label.ll_hostname, PM_LOG_MAXHOSTLEN);
327  	    if (farg) {
328  		/*
329  		 * use timezone from first archive ... this is the OLD default
330  		 */
331  		strcpy(lp->ill_tz, iap->label.ll_tz);
332  	    }
333  	    else {
334  		/*
335  		 * use timezone from last archive ... this is the NEW default
336  		 */
337  		strcpy(lp->ill_tz, inarch[inarchnum-1].label.ll_tz);
338  	    }
339  	
340  	    /* reset outarch as appropriate, depending on other input archives */
341  	    for (i=0; i<inarchnum; i++) {
342  		iap = &inarch[i];
343  	
344  		/* Ensure all archives of the same version number */
345  	        if ((iap->label.ll_magic & 0xff) != inarchvers) {
346  		    fprintf(stderr, 
347  			"%s: Error: input archives with different version numbers\n"
348  			"archive: %s version: %d\n"
349  			"archive: %s version: %d\n",
350  			    pmProgname, inarch[0].name, inarchvers,
351  			    iap->name, (iap->label.ll_magic & 0xff));
352  		    abandon();
353  	        }
354  	
355  		/* Ensure all archives of the same host */
356  		if (strcmp(lp->ill_hostname, iap->label.ll_hostname) != 0) {
357  		    fprintf(stderr,"%s: Error: host name mismatch for input archives\n",
358  			    pmProgname);
359  		    fprintf(stderr, "archive: %s host: %s\n",
360  			    inarch[0].name, inarch[0].label.ll_hostname);
361  		    fprintf(stderr, "archive: %s host: %s\n",
362  			    iap->name, iap->label.ll_hostname);
363  		    abandon();
364  		}
365  	
366  		/* Ensure all archives of the same timezone */
367  		if (strcmp(lp->ill_tz, iap->label.ll_tz) != 0) {
368  		    fprintf(stderr,
369  			"%s: Warning: timezone mismatch for input archives\n",
370  			    pmProgname);
371  		    if (farg) {
372  			fprintf(stderr, "archive: %s timezone: %s [will be used]\n",
373  			    inarch[0].name, lp->ill_tz);
374  			fprintf(stderr, "archive: %s timezone: %s [will be ignored]\n",
375  			    iap->name, iap->label.ll_tz);
376  		    }
377  		    else {
378  			fprintf(stderr, "archive: %s timezone: %s [will be used]\n",
379  			    inarch[inarchnum-1].name, lp->ill_tz);
380  			fprintf(stderr, "archive: %s timezone: %s [will be ignored]\n",
381  			    iap->name, iap->label.ll_tz);
382  		    }
383  		}
384  	    } /*for(i)*/
385  	}
386  	
387  	
388  	/*
389  	 *  
390  	 */
391  	void
392  	writelabel_metati(int do_rewind)
393  	{
394  	    if (do_rewind) rewind(logctl.l_tifp);
395  	    logctl.l_label.ill_vol = PM_LOG_VOL_TI;
396  	    __pmLogWriteLabel(logctl.l_tifp, &logctl.l_label);
397  	
398  	    if (do_rewind) rewind(logctl.l_mdfp);
399  	    logctl.l_label.ill_vol = PM_LOG_VOL_META;
400  	    __pmLogWriteLabel(logctl.l_mdfp, &logctl.l_label);
401  	}
402  	
403  	
404  	/*
405  	 *  
406  	 */
407  	void
408  	writelabel_data(void)
409  	{
410  	    logctl.l_label.ill_vol = 0;
411  	    __pmLogWriteLabel(logctl.l_mfp, &logctl.l_label);
412  	}
413  	
414  	
415  	/* --- Start of reclist functions --- */
416  	
417  	/*
418  	 *  make a reclist_t record
419  	 */
420  	static reclist_t *
421  	mk_reclist_t(void)
422  	{
423  	    reclist_t	*rec;
424  	
425  	    if ((rec = (reclist_t *)malloc(sizeof(reclist_t))) == NULL) {
426  		fprintf(stderr, "%s: Error: cannot malloc space for record list.\n",
427  			pmProgname);
428  		abandon();
429  	    }
430  	#ifdef PCP_DEBUG
431  	    if (pmDebug & DBG_TRACE_APPL0) {
432  	        totalmalloc += sizeof(reclist_t);
433  	        printf ("mk_reclist_t: allocated %d\n", (int)sizeof(reclist_t));
434  	    }
435  	#endif
436  	    rec->pdu = NULL;
437  	    rec->desc.pmid = PM_ID_NULL;
438  	    rec->desc.type = PM_TYPE_NOSUPPORT;
439  	    rec->desc.indom = PM_IN_NULL;
440  	    rec->desc.sem = 0;
441  	    rec->desc.units = nullunits;	/* struct assignment */
442  	    rec->written = NOT_WRITTEN;
443  	    rec->ptr = NULL;
444  	    rec->next = NULL;
445  	    return(rec);
446  	}
447  	
448  	/*
449  	 * find indom in indomreclist - if it isn't in the list then add it in
450  	 * with no pdu buffer
451  	 */
452  	static reclist_t *
453  	findnadd_indomreclist(int indom)
454  	{
455  	    reclist_t	*curr;
456  	
457  	    if (rindom == NULL) {
458  		rindom = mk_reclist_t();
459  		rindom->desc.pmid = PM_ID_NULL;
460  		rindom->desc.type = PM_TYPE_NOSUPPORT;
461  		rindom->desc.indom = indom;
462  		rindom->desc.sem = 0;
463  		rindom->desc.units = nullunits;	/* struct assignment */
464  		return(rindom);
465  	    }
466  	    else {
467  		curr = rindom;
468  	
469  		/* find matching record or last record */
470  		while (curr->next != NULL && curr->desc.indom != indom)
471  		    curr = curr->next;
472  	
473  		if (curr->desc.indom == indom) {
474  		    /* we have found a matching record - return the pointer */
475  		    return(curr);
476  		}
477  		else {
478  		    /* we have not found a matching record - append new record */
479  		    curr->next = mk_reclist_t();
480  		    curr = curr->next;
481  		    curr->desc.pmid = PM_ID_NULL;
482  		    curr->desc.type = PM_TYPE_NOSUPPORT;
483  		    curr->desc.indom = indom;
484  		    curr->desc.sem = 0;
485  		    curr->desc.units = nullunits;	/* struct assignment */
486  		    return(curr);
487  		}
488  	    }
489  	
490  	}
491  	
492  	/*
493  	 *  append a new record to the log record list
494  	 */
495  	void
496  	append_logreclist(int i)
497  	{
498  	    inarch_t	*iap;
499  	    reclist_t	*curr;
500  	
501  	    iap = &inarch[i];
502  	
503  	    if (rlog == NULL) {
504  		rlog = mk_reclist_t();
505  		rlog->pdu = iap->pb[LOG];
506  	    }
507  	    else {
508  		curr = rlog;
509  	
510  		/* find matching record or last record */
511  		while (curr->next != NULL &&
512  			curr->pdu[4] != iap->pb[LOG][4]) curr = curr->next;
513  	
514  		if (curr->pdu[4] == iap->pb[LOG][4]) {
515  		    /* LOG: discard old record; insert new record */
516  		    __pmUnpinPDUBuf(curr->pdu);
517  		    curr->pdu = iap->pb[LOG];
518  		}
519  		else {
520  		    curr->next = mk_reclist_t();
521  		    curr = curr->next;
522  		    curr->pdu = iap->pb[LOG];
523  		}
524  	    } /*else*/
525  	
526  	    iap->pb[LOG] = NULL;
527  	}
528  	
529  	/*
530  	 *  append a new record to the desc meta record list if not seen
531  	 *  before, else check the desc meta record is semantically the
532  	 *  same as the last desc meta record for this pmid from this source
533  	 */
534  	void
535  	update_descreclist(int i)
536  	{
537  	    inarch_t	*iap;
538  	    reclist_t	*curr;
539  	    pmUnits	pmu;
540  	    pmUnits	*pmup;
541  	
542  	    iap = &inarch[i];
543  	
544  	    if (rdesc == NULL) {
545  		/* first time */
546  		curr = rdesc = mk_reclist_t();
547  	    }
548  	    else {
549  		curr = rdesc;
550  		/* find matching record or last record */
551  		while (curr->next != NULL && curr->desc.pmid != __ntohpmID(iap->pb[META][2]))
552  		    curr = curr->next;
553  	
554  	#ifdef PCP_DEBUG
555  		if (pmDebug & DBG_TRACE_APPL1) {
556  		    fprintf(stderr, "update_descreclist: pmid: last/match %s", metricname(curr->desc.pmid));
557  		    fprintf(stderr, " new %s", metricname(__ntohpmID(iap->pb[META][2])));
558  		    fputc('\n', stderr);
559  		}
560  	#endif
561  	    }
562  	
563  	    if (curr->desc.pmid == __ntohpmID(iap->pb[META][2])) {
564  	#ifdef PCP_DEBUG
565  		if (pmDebug & DBG_TRACE_APPL1) {
566  		    fprintf(stderr, " type: old %s", pmTypeStr(curr->desc.type));
567  		    fprintf(stderr, " new %s", pmTypeStr(ntohl(iap->pb[META][3])));
568  		    fprintf(stderr, " indom: old %s", pmInDomStr(curr->desc.indom));
569  		    fprintf(stderr, " new %s", pmInDomStr(__ntohpmInDom(iap->pb[META][4])));
570  		    fprintf(stderr, " sem: old %d", curr->desc.sem);
571  		    fprintf(stderr, " new %d", (int)ntohl(iap->pb[META][5]));
572  		    fprintf(stderr, " units: old %s", pmUnitsStr(&curr->desc.units));
573  		    pmup = (pmUnits *)&iap->pb[META][6];
574  		    pmu = __ntohpmUnits(*pmup);
575  		    fprintf(stderr, " new %s", pmUnitsStr(&pmu));
576  		    fputc('\n', stderr);
577  		}
578  	#endif
579  		if (curr->desc.type != ntohl(iap->pb[META][3])) {
580  		    fprintf(stderr,
581  			"%s: Error: metric %s: type changed from",
582  			pmProgname, metricname(curr->desc.pmid));
583  		    fprintf(stderr, " %s", pmTypeStr(curr->desc.type));
584  		    fprintf(stderr, " to %s!\n", pmTypeStr(ntohl(iap->pb[META][3])));
585  		    abandon();
586  		}
587  		if (curr->desc.indom != __ntohpmInDom(iap->pb[META][4])) {
588  		    fprintf(stderr,
589  			"%s: Error: metric %s: indom changed from",
590  			pmProgname, metricname(curr->desc.pmid));
591  		    fprintf(stderr, " %s", pmInDomStr(curr->desc.indom));
592  		    fprintf(stderr, " to %s!\n", pmInDomStr(__ntohpmInDom(iap->pb[META][4])));
593  		    abandon();
594  		}
595  		if (curr->desc.sem != ntohl(iap->pb[META][5])) {
596  		    fprintf(stderr,
597  			"%s: Error: metric %s: semantics changed from",
598  			pmProgname, metricname(curr->desc.pmid));
599  		    fprintf(stderr, " %d", curr->desc.sem);
600  		    fprintf(stderr, " to %d!\n", (int)ntohl(iap->pb[META][5]));
601  		    abandon();
602  		}
603  		pmup = (pmUnits *)&iap->pb[META][6];
604  		pmu = __ntohpmUnits(*pmup);
605  		if (curr->desc.units.dimSpace != pmu.dimSpace ||
606  		    curr->desc.units.dimTime != pmu.dimTime ||
607  		    curr->desc.units.dimCount != pmu.dimCount ||
608  		    curr->desc.units.scaleSpace != pmu.scaleSpace ||
609  		    curr->desc.units.scaleTime != pmu.scaleTime ||
610  		    curr->desc.units.scaleCount != pmu.scaleCount) {
611  		    fprintf(stderr,
612  			"%s: Error: metric %s: units changed from",
613  			pmProgname, metricname(curr->desc.pmid));
614  		    fprintf(stderr, " %s", pmUnitsStr(&curr->desc.units));
615  		    fprintf(stderr, " to %s!\n", pmUnitsStr(&pmu));
616  		    abandon();
617  		}
618  		/* not adding, so META: discard new record */
619  		free(iap->pb[META]);
620  		iap->pb[META] = NULL;
621  	    }
622  	    else {
623  		/* append new record */
624  		curr->next = mk_reclist_t();
625  		curr = curr->next;
626  		curr->pdu = iap->pb[META];
627  		curr->desc.pmid = __ntohpmID(iap->pb[META][2]);
628  		curr->desc.type = ntohl(iap->pb[META][3]);
629  		curr->desc.indom = __ntohpmInDom(iap->pb[META][4]);
630  		curr->desc.sem = ntohl(iap->pb[META][5]);
631  		pmup =(pmUnits *)&iap->pb[META][6];
632  		curr->desc.units = __ntohpmUnits(*pmup);
633  		curr->ptr = findnadd_indomreclist(curr->desc.indom);
634  		iap->pb[META] = NULL;
635  	    }
636  	}
637  	
638  	/*
639  	 *  append a new record to the indom meta record list
640  	 */
641  	void
642  	append_indomreclist(int i)
643  	{
644  	    inarch_t	*iap;
645  	    reclist_t	*curr;
646  	    reclist_t	*rec;
647  	
648  	    iap = &inarch[i];
649  	
650  	    if (rindom == NULL) {
651  		rindom = mk_reclist_t();
652  		rindom->pdu = iap->pb[META];
653  		rindom->desc.pmid = PM_ID_NULL;
654  		rindom->desc.type = PM_TYPE_NOSUPPORT;
655  		rindom->desc.indom = __ntohpmInDom(iap->pb[META][4]);
656  		rindom->desc.sem = 0;
657  		rindom->desc.units = nullunits;	/* struct assignment */
658  	    }
659  	    else {
660  		curr = rindom;
661  	
662  		/* find matching record or last record */
663  		while (curr->next != NULL && curr->desc.indom != __ntohpmInDom(iap->pb[META][4])) {
664  		    curr = curr->next;
665  		}
666  	
667  		if (curr->desc.indom == __ntohpmInDom(iap->pb[META][4])) {
668  		    if (curr->pdu == NULL) {
669  			/* insert new record */
670  			curr->pdu = iap->pb[META];
671  		    }
672  		    else {
673  			/* do NOT discard old record; insert new record */
674  			rec = mk_reclist_t();
675  			rec->pdu = iap->pb[META];
676  			rec->desc.pmid = PM_ID_NULL;
677  			rec->desc.type = PM_TYPE_NOSUPPORT;
678  			rec->desc.indom = __ntohpmInDom(iap->pb[META][4]);
679  			rec->desc.sem = 0;
680  			rec->desc.units = nullunits;	/* struct assignment */
681  			rec->next = curr->next;
682  			curr->next = rec;
683  		    }
684  		}
685  		else {
686  		    /* append new record */
687  		    curr->next = mk_reclist_t();
688  		    curr = curr->next;
689  		    curr->pdu = iap->pb[META];
690  		    curr->desc.pmid = PM_ID_NULL;
691  		    curr->desc.type = PM_TYPE_NOSUPPORT;
692  		    curr->desc.indom = __ntohpmInDom(iap->pb[META][4]);
693  		    curr->desc.sem = 0;
694  		    curr->desc.units = nullunits;	/* struct assignment */
695  		}
696  	    } /*else*/
697  	
698  	    iap->pb[META] = NULL;
699  	}
700  	
701  	/*
702  	 *  write out one desc/indom record
703  	 */
704  	void
705  	write_rec(reclist_t *rec)
706  	{
707  	    int		sts;
708  	
709  	    if (rec->written == MARK_FOR_WRITE) {
710  		if (rec->pdu == NULL) {
711  		    fprintf(stderr, "%s: Fatal Error!\n", pmProgname);
712  		    fprintf(stderr,"    record is marked for write, but pdu is NULL\n");
713  		    abandon();
714  		}
715  	
716  		/* write out the pdu ; exit if write failed */
717  		if ((sts = _pmLogPut(logctl.l_mdfp, rec->pdu)) < 0) {
718  		    fprintf(stderr, "%s: Error: _pmLogPut: meta data : %s\n",
719  			    pmProgname, pmErrStr(sts));
720  		    abandon();
721  		}
722  		/* META: free PDU buffer */
723  		free(rec->pdu);
724  		rec->pdu = NULL;
725  		rec->written = WRITTEN;
726  	    }
727  	    else {
728  		fprintf(stderr,
729  			"%s : Warning: attempting to write out meta record (%d,%d)\n",
730  			pmProgname, rec->desc.pmid, rec->desc.indom);
731  		fprintf(stderr, "        when it is not marked for writing (%d)\n",
732  			rec->written);
733  	    }
734  	}
735  	
736  	void
737  	write_metareclist(pmResult *result, int *needti)
738  	{
739  	    int			i;
740  	    reclist_t		*curr_desc;	/* current desc record */
741  	    reclist_t		*curr_indom;	/* current indom record */
742  	    reclist_t   	*othr_indom;	/* other indom record */
743  	    pmID		pmid;
744  	    pmInDom		indom;
745  	    struct timeval	*this;		/* ptr to timestamp in result */
746  	
747  	    this = &result->timestamp;
748  	
749  	    /* if pmid in result matches a pmid in desc then write desc
750  	     */
751  	    for (i=0; i<result->numpmid; i++) {
752  		pmid = result->vset[i]->pmid;
753  		indom = PM_IN_NULL;
754  		curr_indom = NULL;
755  	
756  		curr_desc = rdesc;
757  		while (curr_desc != NULL && curr_desc->desc.pmid != pmid)
758  		    curr_desc = curr_desc->next;
759  	
760  		if (curr_desc == NULL) {
761  		    /* descriptor has not been found - this is bad
762  		     */
763  		    fprintf(stderr, "%s: Error: meta data (TYPE_DESC) for pmid %s has not been found.\n", pmProgname, pmIDStr(pmid));
764  		    abandon();
765  		}
766  		else {
767  		    /* descriptor has been found
768  		     */
769  		    if (curr_desc->written == WRITTEN) {
770  			/* descriptor has been written before (no need to write again)
771  			 * but still need to check indom
772  			 */
773  			indom = curr_desc->desc.indom;
774  			curr_indom = curr_desc->ptr;
775  		    }
776  		    else if (curr_desc->pdu == NULL) {
777  			/* descriptor is in list, has not been written, but no pdu
778  			 *  - this is bad
779  			 */
780  			fprintf(stderr, "%s: Error: missing pdu for pmid %s\n",
781  				pmProgname, pmIDStr(pmid));
782  		        abandon();
783  		    }
784  		    else {
785  			/* descriptor is in list, has not been written, and has pdu
786  			 * write!
787  			 */
788  			curr_desc->written = MARK_FOR_WRITE;
789  			write_rec(curr_desc);
790  			indom = curr_desc->desc.indom;
791  			curr_indom = curr_desc->ptr;
792  		    }
793  		}
794  	
795  		/* descriptor has been found and written,
796  		 * now go and find & write the indom
797  		 */
798  		if (indom != PM_INDOM_NULL) {
799  		    /* there may be more than one indom in the list, so we need
800  		     * to traverse the entire list
801  		     *	- we can safely ignore all indoms after the current timestamp
802  		     *	- we want the latest indom at, or before the current timestamp
803  		     *  - all others before the current timestamp can be discarded(?)
804  		     */
805  		    othr_indom = NULL;
806  		    while (curr_indom != NULL && curr_indom->desc.indom == indom) {
807  			if (curr_indom->written != WRITTEN) {
808  			    if (curr_indom->pdu == NULL) {
809  				/* indom is in list, has not been written,
810  				 * but has no pdu - this is possible and acceptable
811  				 * behaviour; do nothing
812  				 */
813  		    	    }
814  			    else if (ntohl(curr_indom->pdu[2]) < this->tv_sec ||
815  				(ntohl(curr_indom->pdu[2]) == this->tv_sec &&
816  				ntohl(curr_indom->pdu[3]) <= this->tv_usec))
817  			    {
818  				/* indom is in list, indom has pdu
819  				 * and timestamp in pdu suits us
820  				 */
821  				if (othr_indom == NULL) {
822  				    othr_indom = curr_indom;
823  				}
824  				else if (ntohl(othr_indom->pdu[2]) < ntohl(curr_indom->pdu[2]) ||
825  					 (ntohl(othr_indom->pdu[2]) == ntohl(curr_indom->pdu[2]) &&
826  					 ntohl(othr_indom->pdu[3]) <= ntohl(curr_indom->pdu[3])))
827  				{
828  				    /* we already have a perfectly good indom,
829  				     * but curr_indom has a better timestamp
830  				     */
831  				    othr_indom = curr_indom;
832  				}
833  			    }
834  			}
835  			curr_indom = curr_indom->next;
836  		    } /*while()*/
837  	
838  		    if (othr_indom != NULL) {
839  			othr_indom->written = MARK_FOR_WRITE;
840  			othr_indom->pdu[2] = htonl(this->tv_sec);
841  			othr_indom->pdu[3] = htonl(this->tv_usec);
842  	
843  			/* make sure to set needti, when writing out the indom
844  			 */
845  			*needti = 1;
846  			write_rec(othr_indom);
847  		    }
848  		}
849  	    } /*for(i)*/
850  	}
851  	
852  	/* --- End of reclist functions --- */
853  	
854  	/*
855  	 *  create a mark record
856  	 */
857  	__pmPDU *
858  	_createmark(void)
859  	{
860  	    mark_t	*markp;
861  	
862  	    markp = (mark_t *)malloc(sizeof(mark_t));
863  	    if (markp == NULL) {
864  		fprintf(stderr, "%s: Error: mark_t malloc: %s\n",
865  			pmProgname, osstrerror());
866  		abandon();
867  	    }
868  	#ifdef PCP_DEBUG
869  	    if (pmDebug & DBG_TRACE_APPL0) {
870  	        totalmalloc += sizeof(mark_t);
871  	        printf ("_createmark : allocated %d\n", (int)sizeof(mark_t));
872  	    }
873  	#endif
874  	
875  	    markp->len = (int)sizeof(mark_t);
876  	    markp->type = markp->from = 0;
877  	    markp->timestamp = current;
878  	    markp->timestamp.tv_usec += 1000;	/* + 1msec */
879  	    if (markp->timestamp.tv_usec > 1000000) {
880  		markp->timestamp.tv_usec -= 1000000;
881  		markp->timestamp.tv_sec++;
882  	    }
883  	    markp->numpmid = 0;
884  	    return((__pmPDU *)markp);
885  	}
886  	
887  	void
888  	checklogtime(__pmTimeval *this, int i)
889  	{
890  	    if ((curlog.tv_sec == 0 && curlog.tv_usec == 0) ||
891  		(curlog.tv_sec > this->tv_sec ||
892  		(curlog.tv_sec == this->tv_sec && curlog.tv_usec > this->tv_usec))) {
893  		    ilog = i;
894  		    curlog.tv_sec = this->tv_sec;
895  		    curlog.tv_usec = this->tv_usec;
896  	    }
897  	}
898  	
899  	
900  	/*
901  	 * pick next meta record - if all meta is at EOF return -1
902  	 * (normally this function returns 0)
903  	 */
904  	static int
905  	nextmeta(void)
906  	{
907  	    int		i;
908  	    int		j;
909  	    int		want;
910  	    int		numeof = 0;
911  	    int		sts;
912  	    pmID	pmid;			/* pmid for TYPE_DESC */
913  	    pmInDom	indom;			/* indom for TYPE_INDOM */
914  	    __pmLogCtl	*lcp;
915  	    __pmContext	*ctxp;
916  	    inarch_t	*iap;			/* pointer to input archive control */
917  	    __pmHashNode	*hnp;
918  	
919  	    for (i=0; i<inarchnum; i++) {
920  		iap = &inarch[i];
921  	
922  		/* if at the end of meta file then skip this archive
923  		 */
924  		if (iap->eof[META]) {
925  		    ++numeof;
926  		    continue;
927  		}
928  	
929  		/* we should never already have a meta record
930  		 */
931  		if (iap->pb[META] != NULL) {
932  		    fprintf(stderr, "%s: Fatal Error!\n", pmProgname);
933  		    fprintf(stderr, "    iap->pb[META] is not NULL\n");
934  		    abandon();
935  		}
936  		ctxp = __pmHandleToPtr(iap->ctx);
937  		lcp = ctxp->c_archctl->ac_log;
938  	
939  	againmeta:
940  		/* get next meta record */
941  	
942  		if ((sts = _pmLogGet(lcp, PM_LOG_VOL_META, &iap->pb[META])) < 0) {
943  		    iap->eof[META] = 1;
944  		    ++numeof;
945  		    if (sts != PM_ERR_EOL) {
946  			fprintf(stderr, "%s: Error: _pmLogGet[meta %s]: %s\n",
947  				pmProgname, iap->name, pmErrStr(sts));
948  			_report(lcp->l_mdfp);
949  		    }
950  		    continue;
951  		}
952  	
953  		/* pmDesc entries, if not seen before & wanted,
954  		 *	then append to desc list
955  		 */
956  		if (ntohl(iap->pb[META][1]) == TYPE_DESC) {
957  		    pmid = __ntohpmID(iap->pb[META][2]);
958  	
959  		    /* if ml is defined, then look for pmid in the list
960  		     * if pmid is not in the list then discard it immediately
961  		     */
962  		    want = 0;
963  		    if (ml == NULL)
964  			want = 1;
965  		    else {
966  			for (j=0; j<ml_numpmid; j++) {
967  			    if (pmid == ml[j].idesc->pmid)
968  				want = 1;
969  			}
970  		    }
971  	
972  		    if (want) {
973  			if ((hnp = __pmHashSearch((int)pmid, &mdesc_hash)) == NULL) {
974  			    __pmHashAdd((int)pmid, NULL, &mdesc_hash);
975  			}
976  			/*
977  			 * update the desc list (add first time, check on subsequent
978  			 * sightings of desc for this pmid from this source
979  			 * update_descreclist() sets pb[META] to NULL
980  			 */
981  			update_descreclist(i);
982  		    }
983  		    else {
984  			/* not wanted */
985  			free(iap->pb[META]);
986  			iap->pb[META] = NULL;
987  			goto againmeta;
988  		    }
989  		}
990  		else if (ntohl(iap->pb[META][1]) == TYPE_INDOM) {
991  		    /* if ml is defined, then look for instance domain in the list
992  		     * if indom is not in the list then discard it immediately
993  		     */
994  		    indom = __ntohpmInDom(iap->pb[META][4]);
995  		    want = 0;
996  		    if (ml == NULL)
997  		        want = 1;
998  		    else {
999  		        for (j=0; j<ml_numpmid; j++) {
1000 			    if (indom == ml[j].idesc->indom)
1001 			        want = 1;
1002 		        }
1003 		    }
1004 	
1005 		    if (want) {
1006 		        if (__pmHashSearch((int)indom, &mindom_hash) == NULL) {
1007 			    /* meta record has never been seen ... add it to the list */
1008 			    __pmHashAdd((int)indom, NULL, &mindom_hash);
1009 		        }
1010 			/* add to indom list */
1011 			/* append_indomreclist() sets pb[META] to NULL
1012 			 * append_indomreclist() may unpin the pdu buffer
1013 			 */
1014 			append_indomreclist(i);
1015 		    }
1016 		    else {
1017 		        /* META: don't want this meta */
1018 		        free(iap->pb[META]);
1019 		        iap->pb[META] = NULL;
1020 		        goto againmeta;
1021 		    }
1022 		}
1023 		else {
1024 		    fprintf(stderr, "%s: Error: unrecognised meta data type: %d\n",
1025 			    pmProgname, (int)ntohl(iap->pb[META][1]));
1026 		    abandon();
1027 		}
1028 	
1029 	    }
1030 	
1031 	    if (numeof == inarchnum) return(-1);
1032 	    return(0);
1033 	}
1034 	
1035 	
1036 	/*
1037 	 * read in next log record for every archive
1038 	 */
1039 	static int
1040 	nextlog(void)
1041 	{
1042 	    int		i;
1043 	    int		eoflog = 0;	/* number of log files at eof */
1044 	    int		sts;
1045 	    double	curtime;
1046 	    __pmLogCtl	*lcp;
1047 	    __pmContext	*ctxp;
1048 	    inarch_t	*iap;
1049 	
1050 	
1051 	    for (i=0; i<inarchnum; i++) {
1052 		iap = &inarch[i];
1053 	
1054 		/* if at the end of log file then skip this archive
1055 		 */
1056 		if (iap->eof[LOG]) {
1057 		    ++eoflog;
1058 		    continue;
1059 		}
1060 	
1061 		/* if we already have a log record then skip this archive
1062 		 */
1063 		if (iap->_Nresult != NULL) {
1064 		    continue;
1065 		}
1066 	
1067 		/* if mark has been written out, then log is at EOF
1068 		 */
1069 		if (iap->mark) {
1070 		    iap->eof[LOG] = 1;
1071 		    ++eoflog;
1072 		    continue;
1073 		}
1074 	
1075 	againlog:
1076 		ctxp = __pmHandleToPtr(iap->ctx);
1077 		lcp = ctxp->c_archctl->ac_log;
1078 	
Event deref_ptr_in_call: Dereferencing pointer "iap->_result". [details]
Also see events: [deref_ptr][check_after_deref]
1079 		if ((sts=__pmLogRead(lcp,PM_MODE_FORW,NULL,&iap->_result)) < 0) {
1080 		    if (sts != PM_ERR_EOL) {
1081 			fprintf(stderr, "%s: Error: __pmLogRead[log %s]: %s\n",
1082 				pmProgname, iap->name, pmErrStr(sts));
1083 			_report(lcp->l_mfp);
1084 		    }
1085 		    /* if the first data record has not been written out, then
1086 		     * do not generate a mark record, and you may as well ignore
1087 		     * this archive
1088 		     */
1089 		    if (first_datarec) {
1090 			iap->mark = 1;
1091 			iap->eof[LOG] = 1;
1092 			++eoflog;
1093 		    }
1094 		    else {
1095 			iap->mark = 1;
1096 			iap->pb[LOG] = _createmark();
1097 		    }
1098 		    continue;
1099 		}
1100 	
1101 	
1102 		/* set current log time - this is only done so that we can
1103 		 * determine whether to keep or discard the log
1104 		 */
Event deref_ptr: Directly dereferencing pointer "iap->_result".
Also see events: [deref_ptr_in_call][check_after_deref]
1105 		curtime = iap->_result->timestamp.tv_sec +
1106 				(double)iap->_result->timestamp.tv_usec / 1000000.0;
1107 	
1108 		/* if log time is greater than (or equal to) the current window
1109 		 * start time, then we may want it
1110 		 *	(irrespective of the current window end time)
1111 		 */
1112 		if (curtime < winstart_time) {
1113 		    /* log is not in time window - discard result and get next record
1114 		     */
Event check_after_deref: Dereferencing "iap->_result" before a null check.
Also see events: [deref_ptr_in_call][deref_ptr]
1115 		    if (iap->_result != NULL) {
1116 			pmFreeResult(iap->_result);
1117 			iap->_result = NULL;
1118 		    }
1119 		    goto againlog;
1120 		}
1121 	        else {
1122 	            /* log is within time window - check whether we want this record
1123 	             */
1124 	            if (ml == NULL) {
1125 	                /* ml is NOT defined, we want everything
1126 	                 */
1127 	                iap->_Nresult = iap->_result;
1128 	            }
1129 	            else {
1130 	                /* ml is defined, need to search metric list for wanted pmid's
1131 	                 *   (searchmlist may return a NULL pointer - this is fine)
1132 	                 */
1133 	                iap->_Nresult = searchmlist(iap->_result);
1134 	            }
1135 	
1136 	            if (iap->_Nresult == NULL) {
1137 	                /* dont want any of the metrics in _result, try again
1138 	                 */
1139 	                if (iap->_result != NULL) {
1140 	                        pmFreeResult(iap->_result);
1141 	                        iap->_result = NULL;
1142 	                }
1143 	                goto againlog;
1144 	            }
1145 		}
1146 	    } /*for(i)*/
1147 	
1148 	    /* if we are here, then each archive control struct should either
1149 	     * be at eof, or it should have a _result, or it should have a mark PDU
1150 	     * (if we have a _result, we may want all/some/none of the pmid's in it)
1151 	     */
1152 	
1153 	    if (eoflog == inarchnum) return(-1);
1154 	    return 0;
1155 	}
1156 	
1157 	/*
1158 	 * parse command line arguments
1159 	 */
1160 	int
1161 	parseargs(int argc, char *argv[])
1162 	{
1163 	    int			c;
1164 	    int			sts;
1165 	    int			errflag = 0;
1166 	    char		*endnum;
1167 	    struct stat		sbuf;
1168 	
1169 	    while ((c = getopt(argc, argv, "c:D:dfn:S:s:T:v:wZ:z?")) != EOF) {
1170 		switch (c) {
1171 	
1172 		case 'c':	/* config file */
1173 		    configfile = optarg;
1174 		    if (stat(configfile, &sbuf) < 0) {
1175 			fprintf(stderr, "%s: %s - invalid file\n",
1176 				pmProgname, configfile);
1177 			errflag++;
1178 		    }
1179 		    break;
1180 	
1181 	
1182 		case 'D':	/* debug flag */
1183 		    sts = __pmParseDebug(optarg);
1184 		    if (sts < 0) {
1185 			fprintf(stderr, "%s: unrecognized debug flag specification (%s)\n",
1186 			    pmProgname, optarg);
1187 			errflag++;
1188 		    }
1189 		    else
1190 			pmDebug |= sts;
1191 		    break;
1192 	
1193 		case 'd':	/* desperate to save output archive, even after error */
1194 		    desperate = 1;
1195 		    break;
1196 	
1197 		case 'f':	/* use timezone from first archive */
1198 		    farg = 1;
1199 		    break;
1200 	
1201 		case 'n':	/* namespace */
1202 				/* if namespace is reassigned from config file,
1203 				 * must reload namespace */
1204 		    pmnsfile = optarg;
1205 		    break;
1206 	
1207 		case 's':	/* number of samples to write out */
1208 		    sarg = (int)strtol(optarg, &endnum, 10);
1209 		    if (*endnum != '\0' || sarg < 0) {
1210 			fprintf(stderr, "%s: -s requires numeric argument\n",
1211 				pmProgname);
1212 			errflag++;
1213 		    }
1214 		    break;
1215 	
1216 		case 'S':	/* start time for extracting */
1217 		    Sarg = optarg;
1218 		    break;
1219 	
1220 		case 'T':	/* end time for extracting */
1221 		    Targ = optarg;
1222 		    break;
1223 	
1224 		case 'v':	/* number of samples per volume */
1225 		    varg = (int)strtol(optarg, &endnum, 10);
1226 		    if (*endnum != '\0' || varg < 0) {
1227 			fprintf(stderr, "%s: -v requires numeric argument\n",
1228 				pmProgname);
1229 			errflag++;
1230 		    }
1231 		    break;
1232 	
1233 		case 'w':	/* ignore day/month/year */
1234 		    warg++;
1235 		    break;
1236 	
1237 		case 'Z':	/* use timezone from command line */
1238 		    if (zarg) {
1239 			fprintf(stderr, "%s: at most one of -Z and/or -z allowed\n",
1240 				pmProgname);
1241 			errflag++;
1242 	
1243 		    }
1244 		    tz = optarg;
1245 		    break;
1246 	
1247 		case 'z':	/* use timezone from archive */
1248 		    if (tz != NULL) {
1249 			fprintf(stderr, "%s: at most one of -Z and/or -z allowed\n",
1250 				pmProgname);
1251 			errflag++;
1252 		    }
1253 		    zarg++;
1254 		    break;
1255 	
1256 		case '?':
1257 		default:
1258 		    errflag++;
1259 		    break;
1260 		}
1261 	    }
1262 	
1263 	    if (warg) {
1264 		if (Sarg == NULL || Targ == NULL) {
1265 		    fprintf(stderr, "%s: Warning: -w flag requires that both -S and -T are specified.\nIgnoring -w flag.\n", pmProgname);
1266 		    warg = 0;
1267 		}
1268 	    }
1269 	
1270 	
1271 	    if (errflag == 0 && optind > argc-2) {
1272 		fprintf(stderr, "%s: Error: insufficient arguments\n", pmProgname);
1273 		errflag++;
1274 	    }
1275 	
1276 	    return(-errflag);
1277 	}
1278 	
1279 	int
1280 	parseconfig(void)
1281 	{
1282 	    int		errflag = 0;
1283 	    extern FILE * yyin;
1284 	
1285 	    if ((yyin = fopen(configfile, "r")) == NULL) {
1286 		fprintf(stderr, "%s: Cannot open config file \"%s\": %s\n",
1287 			pmProgname, configfile, osstrerror());
1288 		exit(1);
1289 	    }
1290 	
1291 	    if (yyparse() != 0)
1292 		exit(1);
1293 	
1294 	    fclose(yyin);
1295 	    yyin = NULL;
1296 	
1297 	    return(-errflag);
1298 	}
1299 	
1300 	void
1301 	adminarch(void)
1302 	{
1303 	    int		i;
1304 	    int		sts;
1305 	
1306 	    if (pmnsfile != PM_NS_DEFAULT || inarchvers == PM_LOG_VERS01) {
1307 		if ((sts = pmLoadNameSpace (pmnsfile)) < 0) {
1308 		    fprintf(stderr, "%s: Error: cannot load name space: %s\n",
1309 			    pmProgname, pmErrStr(sts));
1310 		    exit(1);
1311 		}
1312 	
1313 		for (i=0; i<inarchnum; i++) {
1314 		    if ((sts = pmUseContext(inarch[i].ctx)) < 0) {
1315 			fprintf(stderr,
1316 			    "%s: Error: cannot use context (%d) from archive \"%s\"\n",
1317 				pmProgname, inarch[i].ctx, inarch[i].name);
1318 			exit(1);
1319 		    }
1320 	
1321 		    if ((sts = pmTrimNameSpace ()) < 0) {
1322 			fprintf(stderr, "%s: Error: cannot trim name space: %s\n", 
1323 				pmProgname, pmErrStr(sts));
1324 			exit(1);
1325 		    }
1326 		}
1327 	    }
1328 	}
1329 	
1330 	/*
1331 	 *  we are within time window ... return 0
1332 	 *  we are outside of time window & mk new window ... return 1
1333 	 *  we are outside of time window & exit ... return -1
1334 	 */
1335 	static int
1336 	checkwinend(double now)
1337 	{
1338 	    int		i;
1339 	    int		sts;
1340 	    double	tmptime;
1341 	    inarch_t	*iap;
1342 	    __pmPDU	*markpdu;	/* mark b/n time windows */
1343 	
1344 	    if (winend_time < 0 || now <= winend_time)
1345 		return(0);
1346 	
1347 	    /* we have reached the end of a window
1348 	     *	- if warg is not set, then we have finished (break)
1349 	     *	- otherwise, calculate start and end of next window,
1350 	     *		     set pre_startwin, discard logs before winstart,
1351 	     * 		     and write out mark
1352 	     */
1353 	    if (!warg)
1354 		return(-1);
1355 	
1356 	    winstart_time += NUM_SEC_PER_DAY;
1357 	    winend_time += NUM_SEC_PER_DAY;
1358 	    pre_startwin = 1;
1359 	
1360 	    /* if start of next window is later than max termination
1361 	     * then bail out here
1362 	     */
1363 	    if (winstart_time > logend_time)
1364 		    return(-1);
1365 	
1366 	    ilog = -1;
1367 	    for (i=0; i<inarchnum; i++) {
1368 		iap = &inarch[i];
1369 		if (iap->_Nresult != NULL) {
1370 		    tmptime = iap->_Nresult->timestamp.tv_sec
1371 				+ (double)iap->_Nresult->timestamp.tv_usec/1000000.0;
1372 		    if (tmptime < winstart_time) {
1373 			/* free _result and _Nresult
1374 			 */
1375 			if (iap->_result != iap->_Nresult && iap->_Nresult != NULL) {
1376 			    free(iap->_Nresult);
1377 			}
1378 			if (iap->_result != NULL) {
1379 			    pmFreeResult(iap->_result);
1380 			    iap->_result = NULL;
1381 			}
1382 			iap->_Nresult = NULL;
1383 			iap->pb[LOG] = NULL;
1384 		    }
1385 		}
1386 		if (iap->pb[LOG] != NULL) {
1387 		    tmptime = ntohl(iap->pb[LOG][3]) + (double)ntohl(iap->pb[LOG][4])/1000000.0;
1388 		    if (tmptime < winstart_time) {
1389 			/* free PDU buffer ... it is probably a mark
1390 			 * and has not been pinned
1391 			 */
1392 			free(iap->pb[LOG]);
1393 			iap->pb[LOG] = NULL;
1394 		    }
1395 		}
1396 	    } /*for(i)*/
1397 	
1398 	    /* must create "mark" record and write it out */
1399 	    /* (need only one mark record) */
1400 	    markpdu = _createmark();
1401 	    if ((sts = __pmLogPutResult(&logctl, markpdu)) < 0) {
1402 		fprintf(stderr, "%s: Error: __pmLogPutResult: log data: %s\n",
1403 			pmProgname, pmErrStr(sts));
1404 		abandon();
1405 	    }
1406 	    written++;
1407 	    free(markpdu);
1408 	    return(1);
1409 	}
1410 	
1411 	
1412 	/*
1413 	 *
1414 	 */
1415 	void
1416 	writerlist(rlist_t **rlready, double mintime)
1417 	{
1418 	    int		sts;
1419 	    int		needti;		/* need to flush/update */
1420 	    double	titime;		/* time of last temporal index write */
1421 	    double	restime;	/* time of result */
1422 	    rlist_t	*elm;		/* element of rlready to be written out */
1423 	    __pmPDU	*pb;		/* pdu buffer */
1424 	    __pmTimeval	this;		/* timeval of this record */
1425 	    unsigned long	peek_offset;
1426 	
1427 	    needti = 0;
1428 	    titime = 0.0;
1429 	
1430 	    while (*rlready != NULL) {
1431 		this.tv_sec = (*rlready)->res->timestamp.tv_sec;
1432 		this.tv_usec = (*rlready)->res->timestamp.tv_usec;
1433 		restime = this.tv_sec + (double)this.tv_usec / 1000000.0;
1434 	
1435 	        if (restime > mintime)
1436 		    break;
1437 	
1438 		/* get the first element from the list
1439 		 */
1440 		elm = *rlready;
1441 		*rlready = elm->next;
1442 	
1443 		/* if this is the first record (for output archive) then do some
1444 		 * admin stuff
1445 		 */
1446 		if (first_datarec) {
1447 		    first_datarec = 0;
1448 		    logctl.l_label.ill_start.tv_sec = elm->res->timestamp.tv_sec;
1449 		    logctl.l_label.ill_start.tv_usec = elm->res->timestamp.tv_usec;
1450 	            logctl.l_state = PM_LOG_STATE_INIT;
1451 	            writelabel_data();
1452 	        }
1453 	
1454 		/* if we are in a pre_startwin state, and we are writing
1455 		 * something out, then we are not in a pre_startwin state any more
1456 		 * (it also means that there may be some discrete metrics to be
1457 		 * written out)
1458 		 */
1459 		if (pre_startwin)
1460 		    pre_startwin = 0;
1461 	
1462 	
1463 		/* convert log record to a pdu
1464 		 */
1465 		if (outarchvers == 1)
1466 		    sts = __pmEncodeResult(PDU_OVERRIDE1, elm->res, &pb);
1467 		else
1468 		    sts = __pmEncodeResult(PDU_OVERRIDE2, elm->res, &pb);
1469 	
1470 		if (sts < 0) {
1471 		    fprintf(stderr, "%s: Error: __pmEncodeResult: %s\n",
1472 			    pmProgname, pmErrStr(sts));
1473 		    abandon();
1474 		}
1475 	
1476 		/* __pmEncodeResult doesn't pin the PDU buffer, so we have to
1477 		 */
1478 		__pmPinPDUBuf(pb);
1479 	
1480 	        /* switch volumes if required */
1481 	        if (varg > 0) {
1482 	            if (written > 0 && (written % varg) == 0) {
1483 	                newvolume(outarchname, (__pmTimeval *)&pb[3]);
1484 		    }
1485 	        }
1486 		/*
1487 		 * Even without a -v option, we may need to switch volumes
1488 		 * if the data file exceeds 2^31-1 bytes
1489 		 */
1490 		peek_offset = ftell(logctl.l_mfp);
1491 		peek_offset += ((__pmPDUHdr *)pb)->len - sizeof(__pmPDUHdr) + 2*sizeof(int);
1492 		if (peek_offset > 0x7fffffff) {
1493 		    newvolume(outarchname, (__pmTimeval *)&pb[3]);
1494 		}
1495 	
1496 		/* write out the descriptor and instance domain pdu's first
1497 		 */
1498 		write_metareclist(elm->res, &needti);
1499 	
1500 	
1501 		/* write out log record */
1502 		old_log_offset = ftell(logctl.l_mfp);
1503 		if ((sts = __pmLogPutResult(&logctl, pb)) < 0) {
1504 		    fprintf(stderr, "%s: Error: __pmLogPutResult: log data: %s\n",
1505 			    pmProgname, pmErrStr(sts));
1506 		    abandon();
1507 		}
1508 		written++;
1509 	
1510 	
1511 		/* check whether we need to write TI (temporal index) */
1512 		if (old_log_offset == 0 ||
1513 		    old_log_offset == sizeof(__pmLogLabel)+2*sizeof(int) ||
1514 		    ftell(logctl.l_mfp) > flushsize)
1515 			needti = 1;
1516 	
1517 		/* make sure that we do not write out the temporal index more
1518 		 * than once for the same timestamp
1519 		 */
1520 		if (needti && titime >= restime)
1521 		    needti = 0;
1522 	
1523 		/* flush/update */
1524 		if (needti) {
1525 		    titime = restime;
1526 	
1527 		    fflush(logctl.l_mfp);
1528 		    fflush(logctl.l_mdfp);
1529 	
1530 		    if (old_log_offset == 0)
1531 			old_log_offset = sizeof(__pmLogLabel)+2*sizeof(int);
1532 	
1533 	            new_log_offset = ftell(logctl.l_mfp);
1534 	            new_meta_offset = ftell(logctl.l_mdfp);
1535 	
1536 	            fseek(logctl.l_mfp, (long)old_log_offset, SEEK_SET);
1537 	            fseek(logctl.l_mdfp, (long)old_meta_offset, SEEK_SET);
1538 	
1539 	            __pmLogPutIndex(&logctl, &this);
1540 	
1541 	            fseek(logctl.l_mfp, (long)new_log_offset, SEEK_SET);
1542 	            fseek(logctl.l_mdfp, (long)new_meta_offset, SEEK_SET);
1543 	
1544 	            old_log_offset = ftell(logctl.l_mfp);
1545 	            old_meta_offset = ftell(logctl.l_mdfp);
1546 	
1547 	            flushsize = ftell(logctl.l_mfp) + 100000;
1548 	        }
1549 	
1550 		/* LOG: free PDU buffer */
1551 		__pmUnpinPDUBuf(pb);
1552 		pb = NULL;
1553 	
1554 		elm->res = NULL;
1555 		elm->next = NULL;
1556 		free(elm);
1557 	
1558 	    } /*while(*rlready)*/
1559 	}
1560 	
1561 	
1562 	/*
1563 	 *  mark record has been created and assigned to iap->pb[LOG]
1564 	 *  write it out
1565 	 */
1566 	void
1567 	writemark(inarch_t *iap)
1568 	{
1569 	    int		sts;
1570 	    mark_t      *p = (mark_t *)iap->pb[LOG];
1571 	
1572 	    if (!iap->mark) {
1573 		fprintf(stderr, "%s: Fatal Error!\n", pmProgname);
1574 		fprintf(stderr, "    writemark called, but mark not set\n");
1575 		abandon();
1576 	    }
1577 	
1578 	    if (p == NULL) {
1579 		fprintf(stderr, "%s: Fatal Error!\n", pmProgname);
1580 		fprintf(stderr, "    writemark called, but no pdu\n");
1581 		abandon();
1582 	    }
1583 	
1584 	    p->timestamp.tv_sec = htonl(p->timestamp.tv_sec);
1585 	    p->timestamp.tv_usec = htonl(p->timestamp.tv_usec);
1586 	
1587 	    if ((sts = __pmLogPutResult(&logctl, iap->pb[LOG])) < 0) {
1588 		fprintf(stderr, "%s: Error: __pmLogPutResult: log data: %s\n",
1589 			pmProgname, pmErrStr(sts));
1590 		abandon();
1591 	    }
1592 	    written++;
1593 	    free(iap->pb[LOG]);
1594 	    iap->pb[LOG] = NULL;
1595 	}
1596 	
1597 	/*--- END FUNCTIONS ---------------------------------------------------------*/
1598 	/*
1599 	 * cni == currently not implemented
1600 	 */
1601 	
1602 	int
1603 	main(int argc, char **argv)
1604 	{
1605 	    int		i;
1606 	    int		j;
1607 	    int		sts;
1608 	    int		stslog;			/* sts from nextlog() */
1609 	    int		stsmeta;		/* sts from nextmeta() */
1610 	
1611 	    char	*msg;
1612 	
1613 	    double 	now = 0.0;		/* the current time */
1614 	    double 	mintime = 0.0;
1615 	    double 	tmptime = 0.0;
1616 	
1617 	    __pmTimeval		tstamp;		/* temporary timestamp */
1618 	    inarch_t		*iap;		/* ptr to archive control */
1619 	    rlist_t		*rlready;	/* list of results ready for writing */
1620 	    struct timeval	unused;
1621 	
1622 	
1623 	    rlog = NULL;	/* list of log records to write */
1624 	    rdesc = NULL;	/* list of meta desc records to write */
1625 	    rindom = NULL;	/* list of meta indom records to write */
1626 	    rlready = NULL;
1627 	
1628 	
1629 	    __pmSetProgname(argv[0]);
1630 	
1631 	    /* process cmd line args */
1632 	    if (parseargs(argc, argv) < 0) {
1633 		usage();
1634 		exit(1);
1635 	    }
1636 	
1637 	
1638 	    /* input  archive names are argv[optind] ... argv[argc-2]) */
1639 	    /* output archive name  is  argv[argc-1]) */
1640 	
1641 	    /* output archive */
1642 	    outarchname = argv[argc-1];
1643 	
1644 	    /* input archive(s) */
1645 	    inarchnum = argc - 1 - optind;
1646 	    inarch = (inarch_t *) malloc(inarchnum * sizeof(inarch_t));
1647 	    if (inarch == NULL) {
1648 		fprintf(stderr, "%s: Error: mallco inarch: %s\n",
1649 			pmProgname, osstrerror());
1650 		exit(1);
1651 	    }
1652 	#ifdef PCP_DEBUG
1653 	    if (pmDebug & DBG_TRACE_APPL0) {
1654 	        totalmalloc += (inarchnum * sizeof(inarch_t));
1655 	        printf ("main        : allocated %d\n",
1656 				(int)(inarchnum * sizeof(inarch_t)));
1657 	    }
1658 	#endif
1659 	
1660 	
1661 	    for (i=0; i<inarchnum; i++, optind++) {
1662 		iap = &inarch[i];
1663 	
1664 		iap->name = argv[optind];
1665 	
1666 		iap->pb[LOG] = iap->pb[META] = NULL;
1667 		iap->eof[LOG] = iap->eof[META] = 0;
1668 		iap->mark = 0;
1669 		iap->_result = NULL;
1670 		iap->_Nresult = NULL;
1671 	
1672 		if ((iap->ctx = pmNewContext(PM_CONTEXT_ARCHIVE, iap->name)) < 0) {
1673 		    fprintf(stderr, "%s: Error: cannot open archive \"%s\": %s\n",
1674 			    pmProgname, iap->name, pmErrStr(iap->ctx));
1675 		    exit(1);
1676 		}
1677 	
1678 		if ((sts = pmUseContext(iap->ctx)) < 0) {
1679 		    fprintf(stderr, "%s: Error: cannot use context (%s): %s\n",
1680 			    pmProgname, iap->name, pmErrStr(sts));
1681 		    exit(1);
1682 		}
1683 	
1684 		if ((sts = pmGetArchiveLabel(&iap->label)) < 0) {
1685 		    fprintf(stderr, "%s: Error: cannot get archive label record (%s): %s\n", pmProgname, iap->name, pmErrStr(sts));
1686 		    exit(1);
1687 		}
1688 	
1689 		if ((sts = pmGetArchiveEnd(&unused)) < 0) {
1690 		    fprintf(stderr, "%s: Error: cannot get end of archive (%s): %s\n",
1691 			    pmProgname, iap->name, pmErrStr(sts));
1692 		    exit(1);
1693 		}
1694 	
1695 		if (i == 0) {
1696 		    /* start time */
1697 		    logstart_tval.tv_sec = iap->label.ll_start.tv_sec;
1698 		    logstart_tval.tv_usec = iap->label.ll_start.tv_usec;
1699 	
1700 		    /* end time */
1701 		    logend_tval.tv_sec = unused.tv_sec;
1702 		    logend_tval.tv_usec = unused.tv_usec;
1703 		}
1704 		else {
1705 		    /* get the earlier start time */
1706 		    if (logstart_tval.tv_sec > iap->label.ll_start.tv_sec ||
1707 			(logstart_tval.tv_sec == iap->label.ll_start.tv_sec &&
1708 			logstart_tval.tv_usec > iap->label.ll_start.tv_usec)) {
1709 			    logstart_tval.tv_sec = iap->label.ll_start.tv_sec;
1710 			    logstart_tval.tv_usec = iap->label.ll_start.tv_usec;
1711 		    }
1712 	
1713 		    /* get the later end time */
1714 		    if (logend_tval.tv_sec < unused.tv_sec ||
1715 			(logend_tval.tv_sec == unused.tv_sec &&
1716 			logend_tval.tv_usec < unused.tv_usec)) {
1717 			    logend_tval.tv_sec = unused.tv_sec;
1718 			    logend_tval.tv_usec = unused.tv_usec;
1719 		    }
1720 		}
1721 	    } /*for(i)*/
1722 	
1723 	    logctl.l_label.ill_start.tv_sec = logstart_tval.tv_sec;
1724 	    logctl.l_label.ill_start.tv_usec = logstart_tval.tv_usec;
1725 	
1726 	
1727 	    /* admin archive loads the namespace if required, and trims it
1728 	     * according to the context of each archive
1729 	     */
1730 	    adminarch();
1731 	
1732 	
1733 	    /* process config file
1734 	     *	- this includes a list of metrics and their instances
1735 	     */
1736 	    if (configfile != NULL) {
1737 		if (parseconfig() < 0) {
1738 		    usage();
1739 		    exit(1);
1740 		}
1741 	    }
1742 	
1743 	    if (zarg) {
1744 		/* use TZ from metrics source (input-archive) */
1745 		if ((sts = pmNewZone(inarch[0].label.ll_tz)) < 0) {
1746 		    fprintf(stderr, "%s: Cannot set context timezone: %s\n",
1747 			    pmProgname, pmErrStr(sts));
1748 	            exit_status = 1;
1749 	            goto cleanup;
1750 		}
1751 		printf("Note: timezone set to local timezone of host \"%s\" from archive\n\n", inarch[0].label.ll_hostname);
1752 	    }
1753 	    else if (tz != NULL) {
1754 		/* use TZ as specified by user */
1755 		if ((sts = pmNewZone(tz)) < 0) {
1756 		    fprintf(stderr, "%s: Cannot set timezone to \"%s\": %s\n",
1757 			    pmProgname, tz, pmErrStr(sts));
1758 		    exit_status = 1;
1759 		    goto cleanup;
1760 		}
1761 		printf("Note: timezone set to \"TZ=%s\"\n\n", tz);
1762 	    }
1763 	    else {
1764 		char	*tz;
1765 	        tz = __pmTimezone();
1766 		/* use TZ from local host */
1767 		if ((sts = pmNewZone(tz)) < 0) {
1768 		    fprintf(stderr, "%s: Cannot set local host's timezone: %s\n",
1769 			    pmProgname, pmErrStr(sts));
1770 		    exit_status = 1;
1771 		    goto cleanup;
1772 		}
1773 	    }
1774 	
1775 	
1776 	    /* create output log - must be done before writing label */
1777 	    if ((sts = __pmLogCreate("", outarchname, outarchvers, &logctl)) < 0) {
1778 		fprintf(stderr, "%s: Error: __pmLogCreate: %s\n",
1779 			pmProgname, pmErrStr(sts));
1780 		exit(1);
1781 	    }
1782 	
1783 	    /* This must be done after log is created:
1784 	     *		- checks that archive version, host, and timezone are ok
1785 	     *		- set archive version, host, and timezone of output archive
1786 	     */
1787 	    newlabel();
1788 	
1789 	    /* write label record */
1790 	    writelabel_metati(0);
1791 	
1792 	
1793 	    /* set winstart and winend timevals */
1794 	    sts = pmParseTimeWindow(Sarg, Targ, Aarg, Oarg,
1795 				    &logstart_tval, &logend_tval,
1796 				    &winstart_tval, &winend_tval, &unused, &msg);
1797 	    if (sts < 0) {
1798 		fprintf(stderr, "%s: Invalid time window specified: %s\n",
1799 			pmProgname, msg);
1800 		abandon();
1801 	    }
1802 	    winstart_time = tv2double(&winstart_tval);
1803 	    winend_time = tv2double(&winend_tval);
1804 	    logend_time = tv2double(&logend_tval);
1805 	
1806 	    if (warg) {
1807 		if (winstart_time + NUM_SEC_PER_DAY < winend_time) {
1808 		    fprintf(stderr, "%s: Warning: -S and -T must specify a time window within\nthe same day, for -w to be used.  Ignoring -w flag.\n", pmProgname);
1809 		    warg = 0;
1810 		}
1811 	    }
1812 	
1813 	    ilog = -1;
1814 	    written = 0;
1815 	    curlog.tv_sec = 0;
1816 	    curlog.tv_usec = 0;
1817 	    current.tv_sec = 0;
1818 	    current.tv_usec = 0;
1819 	    first_datarec = 1;
1820 	    pre_startwin = 1;
1821 	
1822 	    /* get all meta data first
1823 	     * nextmeta() should return 0 (will return -1 when all meta is eof)
1824 	     */
1825 	    do {
1826 		stsmeta = nextmeta();
1827 	    } while (stsmeta >= 0);
1828 	
1829 	
1830 	    /* get log record - choose one with earliest timestamp
1831 	     * write out meta data (required by this log record)
1832 	     * write out log
1833 	     * do ti update if necessary
1834 	     */
1835 	    while (sarg == -1 || written < sarg) {
1836 		ilog = -1;
1837 		curlog.tv_sec = 0;
1838 		curlog.tv_usec = 0;
1839 		old_meta_offset = ftell(logctl.l_mdfp);
1840 	
1841 		/* nextlog() resets ilog, and curlog (to the smallest timestamp)
1842 		 */
1843 		stslog = nextlog();
1844 	
1845 		if (stslog < 0)
1846 		    break;
1847 	
1848 		/* find the _Nresult (or mark pdu) with the earliest timestamp;
1849 		 * set ilog
1850 		 * (this is a bit more complex when tflag is specified)
1851 		 */
1852 		mintime = 0.0;
1853 		for (i=0; i<inarchnum; i++) {
1854 		    if (inarch[i]._Nresult != NULL) {
1855 			tstamp.tv_sec = inarch[i]._Nresult->timestamp.tv_sec;
1856 			tstamp.tv_usec = inarch[i]._Nresult->timestamp.tv_usec;
1857 			checklogtime(&tstamp, i);
1858 	
1859 			if (ilog == i) {
1860 			    tmptime = curlog.tv_sec + (double)curlog.tv_usec/1000000.0;
1861 			    if (mintime <= 0 || mintime > tmptime)
1862 			        mintime = tmptime;
1863 			}
1864 		    }
1865 		    else if (inarch[i].pb[LOG] != NULL) {
1866 			tstamp.tv_sec = inarch[i].pb[LOG][3]; /* no swab needed */
1867 			tstamp.tv_usec = inarch[i].pb[LOG][4]; /* no swab needed */
1868 			checklogtime(&tstamp, i);
1869 	
1870 			if (ilog == i) {
1871 			    tmptime = curlog.tv_sec + (double)curlog.tv_usec/1000000.0;
1872 			    if (mintime <= 0 || mintime > tmptime)
1873 			        mintime = tmptime;
1874 			}
1875 		    }
1876 		}
1877 	
1878 		/* now     == the earliest timestamp of the archive(s)
1879 		 *		and/or mark records
1880 		 * mintime == now or timestamp of the earliest mark
1881 		 *		(whichever is smaller)
1882 		 */
1883 		now = curlog.tv_sec + (double)curlog.tv_usec / 1000000.0;
1884 	
1885 		/* note - mark (after last archive) will be created, but this
1886 		 * break, will prevent it from being written out
1887 		 */
1888 		if (now > logend_time)
1889 		    break;
1890 	
1891 		sts = checkwinend(now);
1892 		if (sts < 0)
1893 		    break;
1894 		if (sts > 0)
1895 		    continue;
1896 	
1897 		current.tv_sec = (long)now;
1898 		current.tv_usec = (now - (double)current.tv_sec) * 1000000.0;
1899 	
1900 		/* prepare to write out log record
1901 		 */
1902 		if (ilog < 0 || ilog >= inarchnum) {
1903 		    fprintf(stderr, "%s: Fatal Error!\n", pmProgname);
1904 		    fprintf(stderr, "    log file index = %d\n", ilog);
1905 		    abandon();
1906 		}
1907 	
1908 		iap = &inarch[ilog];
1909 		if (iap->mark)
1910 		    writemark(iap);
1911 		else {
1912 		    /* result is to be written out, but there is no _Nresult
1913 		     */
1914 		    if (iap->_Nresult == NULL) {
1915 			fprintf(stderr, "%s: Fatal Error!\n", pmProgname);
1916 			fprintf(stderr, "    pick == LOG and _Nresult = NULL\n");
1917 			abandon();
1918 		    }
1919 		    insertresult(&rlready, iap->_Nresult);
1920 		    writerlist(&rlready, now);
1921 	
1922 		    /* writerlist frees elm (elements of rlready) but does not
1923 		     * free _result & _Nresult
1924 		     */
1925 	
1926 		    /* free _result & _Nresult
1927 		     *	_Nresult may contain space that was allocated
1928 		     *	in __pmStuffValue this space has PM_VAL_SPTR format,
1929 		     *	and has to be freed first
1930 		     *	(in order to avoid memory leaks)
1931 		     */
1932 		    if (iap->_result != iap->_Nresult && iap->_Nresult != NULL) {
1933 			pmValueSet	*vsetp;
1934 			for (i=0; i<iap->_Nresult->numpmid; i++) {
1935 			    vsetp = iap->_Nresult->vset[i];
1936 			    if (vsetp->valfmt == PM_VAL_SPTR) {
1937 				for (j=0; j<vsetp->numval; j++) {
1938 				    free(vsetp->vlist[j].value.pval);
1939 				}
1940 			    }
1941 			}
1942 			free(iap->_Nresult);
1943 		    }
1944 		    if (iap->_result != NULL)
1945 			pmFreeResult(iap->_result);
1946 		    iap->_result = NULL;
1947 		    iap->_Nresult = NULL;
1948 		}
1949 	    } /*while()*/
1950 	
1951 	    if (first_datarec) {
1952 	        fprintf(stderr, "%s: Warning: no qualifying records found.\n",
1953 	                pmProgname);
1954 	cleanup:
1955 		abandon();
1956 	    }
1957 	    else {
1958 		/* write the last time stamp */
1959 		fflush(logctl.l_mfp);
1960 		fflush(logctl.l_mdfp);
1961 	
1962 		if (old_log_offset == 0)
1963 		    old_log_offset = sizeof(__pmLogLabel)+2*sizeof(int);
1964 	
1965 		new_log_offset = ftell(logctl.l_mfp);
1966 		new_meta_offset = ftell(logctl.l_mdfp);
1967 	
1968 	#if 0
1969 		fprintf(stderr, "*** last tstamp: \n\tmintime=%g \n\ttmptime=%g \n\tlogend_time=%g \n\twinend_time=%g \n\tcurrent=%d.%06d\n",
1970 		    mintime, tmptime, logend_time, winend_time, current.tv_sec, current.tv_usec);
1971 	#endif
1972 	
1973 		fseek(logctl.l_mfp, old_log_offset, SEEK_SET);
1974 		__pmLogPutIndex(&logctl, &current);
1975 	
1976 	
1977 		/* need to fix up label with new start-time */
1978 		writelabel_metati(1);
1979 	    }
1980 	#ifdef PCP_DEBUG
1981 	    if (pmDebug & DBG_TRACE_APPL0) {
1982 	        printf ("main        : total allocated %ld\n", totalmalloc);
1983 	    }
1984 	#endif
1985 	
1986 	    exit(exit_status);
1987 	    return(0);
1988 	}