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
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 */
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 */
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
|
Event negative_return_fn: |
Function "ftell(logctl.l_mfp)" returns a negative number. |
|
Event var_assign: |
Assigning: signed variable "new_log_offset" = "ftell". |
| Also see events: |
[negative_returns] |
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
|
Event negative_returns: |
"(long)new_log_offset" is passed to a parameter that cannot be negative. |
| Also see events: |
[negative_return_fn][var_assign] |
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, ¤t);
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 }