1 /*
2 * pmlogrewrite - config-driven stream editor for PCP archives
3 *
4 * Copyright (c) 1997-2002 Silicon Graphics, Inc. All Rights Reserved.
5 * Copyright (c) 2011 Ken McDonell. All Rights Reserved.
6 *
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the
9 * Free Software Foundation; either version 2 of the License, or (at your
10 * option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
14 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15 * for more details.
16 */
17
18 #include <math.h>
19 #include <ctype.h>
20 #include <sys/stat.h>
21 #include "pmapi.h"
22 #include "impl.h"
23 #include "logger.h"
24 #include <assert.h>
25
26 global_t global;
27 indomspec_t *indom_root = NULL;
28 metricspec_t *metric_root = NULL;
29 int lineno;
30
31 /*
32 * Usage
33 */
34 static void
35 usage(void)
36 {
37 fprintf(stderr,
38 "Usage: %s [options] input-archive [output-archive]\n\
39 \n\
40 Options:\n\
41 -c config file or directory to load rules from\n\
42 -C parse config file(s) and quit (sets -v and -w also)\n\
43 -d desperate, save output archive even after error\n\
44 -i rewrite in place, input-archive will be over-written\n\
45 -q quick mode, no output if no change\n\
46 -s do scale conversion\n\
47 -v verbose\n\
48 -w emit warnings [default is silence]\n\
49 \n\
50 output-archive is required unless -i is specified\n",
51 pmProgname);
52 }
53
54 /*
55 * Global variables
56 */
57 static int first_datarec = 1; /* first record flag */
58 static char bak_base[MAXPATHLEN+1]; /* basename for backup with -i */
59
60 off_t new_log_offset; /* new log offset */
61 off_t new_meta_offset; /* new meta offset */
62
63
64 /* archive control stuff */
65 inarch_t inarch; /* input archive control */
66 outarch_t outarch; /* output archive control */
67
68 /* command line args */
69 int nconf = 0; /* number of config files */
70 char **conf = NULL; /* list of config files */
71 char *configfile = NULL; /* current config file */
72 int Cflag = 0; /* -C parse config and quit */
73 int dflag = 0; /* -d desperate */
74 int iflag = 0; /* -i in-place */
75 int qflag = 0; /* -q quick or quiet */
76 int sflag = 0; /* -s scale values */
77 int vflag = 0; /* -v verbosity */
78 int wflag = 0; /* -w emit warnings */
79
80 /*
81 * report that archive is corrupted
82 */
83 static void
84 _report(FILE *fp)
85 {
86 off_t here;
87 struct stat sbuf;
88
89 here = lseek(fileno(fp), 0L, SEEK_CUR);
90 fprintf(stderr, "%s: Error occurred at byte offset %ld into a file of",
91 pmProgname, (long)here);
92 if (fstat(fileno(fp), &sbuf) < 0)
93 fprintf(stderr, ": stat: %s\n", osstrerror());
94 else
95 fprintf(stderr, " %ld bytes.\n", (long)sbuf.st_size);
96 if (dflag)
97 fprintf(stderr, "The last record, and the remainder of this file will not be processed.\n");
98 abandon();
99 }
100
101 /*
102 * switch output volumes
103 */
104 void
105 newvolume(int vol)
106 {
107 FILE *newfp;
108
109 if ((newfp = __pmLogNewFile(outarch.name, vol)) != NULL) {
110 fclose(outarch.logctl.l_mfp);
111 outarch.logctl.l_mfp = newfp;
112 outarch.logctl.l_label.ill_vol = outarch.logctl.l_curvol = vol;
113 __pmLogWriteLabel(outarch.logctl.l_mfp, &outarch.logctl.l_label);
114 fflush(outarch.logctl.l_mfp);
115 }
116 else {
117 fprintf(stderr, "%s: __pmLogNewFile(%s,%d) Error: %s\n",
118 pmProgname, outarch.name, vol, pmErrStr(-oserror()));
119 abandon();
120 }
121 }
122
123 /* construct new archive label */
124 static void
125 newlabel(void)
126 {
127 __pmLogLabel *lp = &outarch.logctl.l_label;
128
129 /* copy magic number, pid, host and timezone */
130 lp->ill_magic = inarch.label.ll_magic;
131 lp->ill_pid = inarch.label.ll_pid;
132 if (global.flags & GLOBAL_CHANGE_HOSTNAME)
133 strncpy(lp->ill_hostname, global.hostname, PM_LOG_MAXHOSTLEN);
134 else
135 strncpy(lp->ill_hostname, inarch.label.ll_hostname, PM_LOG_MAXHOSTLEN);
136 if (global.flags & GLOBAL_CHANGE_TZ)
137 strncpy(lp->ill_tz, global.tz, PM_TZ_MAXLEN);
138 else
139 strncpy(lp->ill_tz, inarch.label.ll_tz, PM_TZ_MAXLEN);
140 }
141
142 /*
143 * write label records at the start of each physical file
144 */
145 void
146 writelabel(int do_rewind)
147 {
148 off_t old_offset;
149
150 if (do_rewind) {
|
Event negative_return_fn: |
Function "ftell(outarch.logctl.l_tifp)" returns a negative number. |
|
Event var_assign: |
Assigning: signed variable "old_offset" = "ftell". |
| Also see events: |
[negative_returns] |
151 old_offset = ftell(outarch.logctl.l_tifp);
152 rewind(outarch.logctl.l_tifp);
153 }
154 outarch.logctl.l_label.ill_vol = PM_LOG_VOL_TI;
155 __pmLogWriteLabel(outarch.logctl.l_tifp, &outarch.logctl.l_label);
|
At conditional (1): "do_rewind": Taking true branch.
|
156 if (do_rewind)
|
Event negative_returns: |
"(long)old_offset" is passed to a parameter that cannot be negative. |
| Also see events: |
[negative_return_fn][var_assign] |
157 fseek(outarch.logctl.l_tifp, (long)old_offset, SEEK_SET);
158
159 if (do_rewind) {
160 old_offset = ftell(outarch.logctl.l_mdfp);
161 rewind(outarch.logctl.l_mdfp);
162 }
163 outarch.logctl.l_label.ill_vol = PM_LOG_VOL_META;
164 __pmLogWriteLabel(outarch.logctl.l_mdfp, &outarch.logctl.l_label);
165 if (do_rewind)
166 fseek(outarch.logctl.l_mdfp, (long)old_offset, SEEK_SET);
167
168 if (do_rewind) {
169 old_offset = ftell(outarch.logctl.l_mfp);
170 rewind(outarch.logctl.l_mfp);
171 }
172 outarch.logctl.l_label.ill_vol = 0;
173 __pmLogWriteLabel(outarch.logctl.l_mfp, &outarch.logctl.l_label);
174 if (do_rewind)
175 fseek(outarch.logctl.l_mfp, (long)old_offset, SEEK_SET);
176 }
177
178 /*
179 * read next metadata record
180 */
181 static int
182 nextmeta()
183 {
184 int sts;
185 __pmLogCtl *lcp;
186
187 lcp = inarch.ctxp->c_archctl->ac_log;
188 if ((sts = _pmLogGet(lcp, PM_LOG_VOL_META, &inarch.metarec)) < 0) {
189 if (sts != PM_ERR_EOL) {
190 fprintf(stderr, "%s: Error: _pmLogGet[meta %s]: %s\n",
191 pmProgname, inarch.name, pmErrStr(sts));
192 _report(lcp->l_mdfp);
193 }
194 return -1;
195 }
196
197 return ntohl(inarch.metarec[1]);
198 }
199
200
201 /*
202 * read next log record
203 *
204 * return status is
205 * 0 ok
206 * 1 ok, but volume switched
207 * PM_ERR_EOL end of file
208 * -1 fatal error
209 */
210 static int
211 nextlog(void)
212 {
213 int sts;
214 __pmLogCtl *lcp;
215 int old_vol;
216
217
218 lcp = inarch.ctxp->c_archctl->ac_log;
219 old_vol = inarch.ctxp->c_archctl->ac_log->l_curvol;
220
221 if ((sts = __pmLogRead(lcp, PM_MODE_FORW, NULL, &inarch.rp)) < 0) {
222 if (sts != PM_ERR_EOL) {
223 fprintf(stderr, "%s: Error: __pmLogRead[log %s]: %s\n",
224 pmProgname, inarch.name, pmErrStr(sts));
225 _report(lcp->l_mfp);
226 }
227 return -1;
228 }
229
230 return old_vol == inarch.ctxp->c_archctl->ac_log->l_curvol ? 0 : 1;
231 }
232
233 #ifdef IS_MINGW
234 #define S_ISLINK(mode) 0 /* no symlink support */
235 #else
236 #ifndef S_ISLINK
237 #define S_ISLINK(mode) ((mode & S_IFMT) == S_IFLNK)
238 #endif
239 #endif
240
241 /*
242 * parse command line arguments
243 */
244 int
245 parseargs(int argc, char *argv[])
246 {
247 int c;
248 int sts;
249 int sep = __pmPathSeparator();
250 int errflag = 0;
251 struct stat sbuf;
252
253 while ((c = getopt(argc, argv, "c:CdD:iqsvw?")) != EOF) {
254 switch (c) {
255
256 case 'c': /* config file */
257 if (stat(optarg, &sbuf) < 0) {
258 fprintf(stderr, "%s: stat(%s) failed: %s\n",
259 pmProgname, optarg, osstrerror());
260 errflag++;
261 break;
262 }
263 if (S_ISREG(sbuf.st_mode) || S_ISLINK(sbuf.st_mode)) {
264 nconf++;
265 if ((conf = (char **)realloc(conf, nconf*sizeof(conf[0]))) != NULL)
266 conf[nconf-1] = optarg;
267 }
268 else if (S_ISDIR(sbuf.st_mode)) {
269 DIR *dirp;
270 struct dirent *dp;
271 char path[MAXPATHLEN+1];
272
273 if ((dirp = opendir(optarg)) == NULL) {
274 fprintf(stderr, "%s: opendir(%s) failed: %s\n", pmProgname, optarg, osstrerror());
275 errflag++;
276 }
277 else while ((dp = readdir(dirp)) != NULL) {
278 /* skip ., .. and "hidden" files */
279 if (dp->d_name[0] == '.') continue;
280 snprintf(path, sizeof(path), "%s%c%s", optarg, sep, dp->d_name);
281 if (stat(path, &sbuf) < 0) {
282 fprintf(stderr, "%s: %s: %s\n",
283 pmProgname, path, osstrerror());
284 errflag++;
285 }
286 if (S_ISREG(sbuf.st_mode) || S_ISLINK(sbuf.st_mode)) {
287 nconf++;
288 if ((conf = (char **)realloc(conf, nconf*sizeof(conf[0]))) == NULL)
289 break;
290 if ((conf[nconf-1] = strdup(path)) == NULL) {
291 fprintf(stderr, "conf[%d] strdup(%s) failed: %s\n", nconf-1, path, strerror(errno));
292 abandon();
293 }
294
295 }
296 }
297 }
298 else {
299 fprintf(stderr, "Error: -c config %s is not a file or directory\n", optarg);
300 errflag++;
301 }
302 if (nconf > 0 && conf == NULL) {
303 fprintf(stderr, "conf[%d] realloc(%d) failed: %s\n", nconf, (int)(nconf*sizeof(conf[0])), strerror(errno));
304 abandon();
305 }
306 break;
307
308 case 'C': /* parse configs and quit */
309 Cflag = 1;
310 vflag = 1;
311 wflag = 1;
312 break;
313
314 case 'd': /* desperate */
315 dflag = 1;
316 break;
317
318 case 'D': /* debug flag */
319 sts = __pmParseDebug(optarg);
320 if (sts < 0) {
321 fprintf(stderr, "%s: unrecognized debug flag specification (%s)\n",
322 pmProgname, optarg);
323 errflag++;
324 }
325 else
326 pmDebug |= sts;
327 break;
328
329 case 'i': /* in-place, over-write input archive */
330 iflag = 1;
331 break;
332
333 case 'q': /* quick or quiet */
334 qflag = 1;
335 break;
336
337 case 's': /* do scale conversions */
338 sflag = 1;
339 break;
340
341 case 'v': /* verbosity */
342 vflag++;
343 break;
344
345 case 'w': /* print warnings */
346 wflag = 1;
347 break;
348
349 case '?':
350 default:
351 errflag++;
352 break;
353 }
354 }
355
356 if (errflag == 0) {
357 if ((iflag == 0 && optind != argc-2) ||
358 (iflag == 1 && optind != argc-1))
359 errflag++;
360 }
361
362 return -errflag;
363 }
364
365 static void
366 parseconfig(char *file)
367 {
368 extern FILE * yyin;
369
370 configfile = file;
371 if ((yyin = fopen(configfile, "r")) == NULL) {
372 fprintf(stderr, "%s: Cannot open config file \"%s\": %s\n",
373 pmProgname, configfile, osstrerror());
374 exit(1);
375 }
376 if (vflag > 1)
377 fprintf(stderr, "Start configfile: %s\n", file);
378 lineno = 1;
379
380 if (yyparse() != 0)
381 exit(1);
382
383 fclose(yyin);
384 yyin = NULL;
385
386 return;
387 }
388
389 char *
390 SemStr(int sem)
391 {
392 static char buf[20];
393
394 if (sem == PM_SEM_COUNTER) snprintf(buf, sizeof(buf), "counter");
395 else if (sem == PM_SEM_INSTANT) snprintf(buf, sizeof(buf), "instant");
396 else if (sem == PM_SEM_DISCRETE) snprintf(buf, sizeof(buf), "discrete");
397 else snprintf(buf, sizeof(buf), "bad sem? %d", sem);
398
399 return buf;
400 }
401
402 static void
403 reportconfig(void)
404 {
405 indomspec_t *ip;
406 metricspec_t *mp;
407 int i;
408 int change = 0;
409
410 printf("PCP Archive Log Rewrite Specifications Summary\n");
411 change |= (global.flags != 0);
412 if (global.flags & GLOBAL_CHANGE_HOSTNAME)
413 printf("Hostname:\t%s -> %s\n", inarch.label.ll_hostname, global.hostname);
414 if (global.flags & GLOBAL_CHANGE_TZ)
415 printf("Timezone:\t%s -> %s\n", inarch.label.ll_tz, global.tz);
416 if (global.flags & GLOBAL_CHANGE_TIME) {
417 static struct tm *tmp;
418 char *sign = "";
419 time_t time;
420 if (global.time.tv_sec < 0) {
421 time = (time_t)(-global.time.tv_sec);
422 sign = "-";
423 }
424 else
425 time = (time_t)global.time.tv_sec;
426 tmp = gmtime(&time);
427 tmp->tm_hour += 24 * tmp->tm_yday;
428 if (tmp->tm_hour < 10)
429 printf("Delta:\t\t-> %s%02d:%02d:%02d.%06d\n", sign, tmp->tm_hour, tmp->tm_min, tmp->tm_sec, (int)global.time.tv_usec);
430 else
431 printf("Delta:\t\t-> %s%d:%02d:%02d.%06d\n", sign, tmp->tm_hour, tmp->tm_min, tmp->tm_sec, (int)global.time.tv_usec);
432 }
433 for (ip = indom_root; ip != NULL; ip = ip->i_next) {
434 int hdr_done = 0;
435 if (ip->new_indom != ip->old_indom) {
436 printf("\nInstance Domain: %s\n", pmInDomStr(ip->old_indom));
437 hdr_done = 1;
438 printf("pmInDom:\t-> %s\n", pmInDomStr(ip->new_indom));
439 change |= 1;
440 }
441 for (i = 0; i < ip->numinst; i++) {
442 change |= (ip->flags[i] != 0);
443 if (ip->flags[i]) {
444 if (hdr_done == 0) {
445 printf("\nInstance Domain: %s\n", pmInDomStr(ip->old_indom));
446 hdr_done = 1;
447 }
448 printf("Instance:\t\[%d] \"%s\" -> ", ip->old_inst[i], ip->old_iname[i]);
449 if (ip->flags[i] & INST_DELETE)
450 printf("DELETE\n");
451 else {
452 if (ip->flags[i] & INST_CHANGE_INST)
453 printf("[%d] ", ip->new_inst[i]);
454 else
455 printf("[%d] ", ip->old_inst[i]);
456 if (ip->flags[i] & INST_CHANGE_INAME)
457 printf("\"%s\"\n", ip->new_iname[i]);
458 else
459 printf("\"%s\"\n", ip->old_iname[i]);
460 }
461 }
462 }
463 }
464 for (mp = metric_root; mp != NULL; mp = mp->m_next) {
465 if (mp->flags != 0 || mp->ip != NULL) {
466 change |= 1;
467 printf("\nMetric: %s (%s)\n", mp->old_name, pmIDStr(mp->old_desc.pmid));
468 }
469 if (mp->flags & METRIC_CHANGE_PMID) {
470 printf("pmID:\t\t%s ->", pmIDStr(mp->old_desc.pmid));
471 printf(" %s\n", pmIDStr(mp->new_desc.pmid));
472 }
473 if (mp->flags & METRIC_CHANGE_NAME)
474 printf("Name:\t\t%s -> %s\n", mp->old_name, mp->new_name);
475 if (mp->flags & METRIC_CHANGE_TYPE) {
476 printf("Type:\t\t%s ->", pmTypeStr(mp->old_desc.type));
477 printf(" %s\n", pmTypeStr(mp->new_desc.type));
478 }
479 if (mp->flags & METRIC_CHANGE_INDOM) {
480 printf("InDom:\t\t%s ->", pmInDomStr(mp->old_desc.indom));
481 printf(" %s\n", pmInDomStr(mp->new_desc.indom));
482 if (mp->output != OUTPUT_ALL) {
483 printf("Output:\t\t");
484 switch (mp->output) {
485 case OUTPUT_ONE:
486 if (mp->old_desc.indom != PM_INDOM_NULL) {
487 printf("value for instance");
488 if (mp->one_inst != PM_IN_NULL)
489 printf(" %d", mp->one_inst);
490 if (mp->one_name != NULL)
491 printf(" \"%s\"", mp->one_name);
492 putchar('\n');
493 }
494 else
495 printf("the only value (output instance %d)\n", mp->one_inst);
496 break;
497 case OUTPUT_FIRST:
498 if (mp->old_desc.indom != PM_INDOM_NULL)
499 printf("first value\n");
500 else {
501 if (mp->one_inst != PM_IN_NULL)
502 printf("first and only value (output instance %d)\n", mp->one_inst);
503 else
504 printf("first and only value (output instance \"%s\")\n", mp->one_name);
505 }
506 break;
507 case OUTPUT_LAST:
508 if (mp->old_desc.indom != PM_INDOM_NULL)
509 printf("last value\n");
510 else
511 printf("last and only value (output instance %d)\n", mp->one_inst);
512 break;
513 case OUTPUT_MIN:
514 if (mp->old_desc.indom != PM_INDOM_NULL)
515 printf("smallest value\n");
516 else
517 printf("smallest and only value (output instance %d)\n", mp->one_inst);
518 break;
519 case OUTPUT_MAX:
520 if (mp->old_desc.indom != PM_INDOM_NULL)
521 printf("largest value\n");
522 else
523 printf("largest and only value (output instance %d)\n", mp->one_inst);
524 break;
525 case OUTPUT_SUM:
526 if (mp->old_desc.indom != PM_INDOM_NULL)
527 printf("sum value (output instance %d)\n", mp->one_inst);
528 else
529 printf("sum and only value (output instance %d)\n", mp->one_inst);
530 break;
531 case OUTPUT_AVG:
532 if (mp->old_desc.indom != PM_INDOM_NULL)
533 printf("average value (output instance %d)\n", mp->one_inst);
534 else
535 printf("average and only value (output instance %d)\n", mp->one_inst);
536 break;
537 }
538 }
539 }
540 if (mp->ip != NULL)
541 printf("Inst Changes:\t<- InDom %s", pmInDomStr(mp->ip->old_indom));
542 if (mp->flags & METRIC_CHANGE_SEM) {
543 printf("Semantics:\t%s ->", SemStr(mp->old_desc.sem));
544 printf(" %s\n", SemStr(mp->new_desc.sem));
545 }
546 if (mp->flags & METRIC_CHANGE_UNITS) {
547 printf("Units:\t\t%s ->", pmUnitsStr(&mp->old_desc.units));
548 printf(" %s", pmUnitsStr(&mp->new_desc.units));
549 if (mp->flags & METRIC_RESCALE)
550 printf(" (rescale)");
551 putchar('\n');
552 }
553 if (mp->flags & METRIC_DELETE)
554 printf("DELETE\n");
555 }
556 if (change == 0)
557 printf("No changes\n");
558 }
559
560 static int
561 anychange(void)
562 {
563 indomspec_t *ip;
564 metricspec_t *mp;
565 int i;
566
567 if (global.flags != 0)
568 return 1;
569 for (ip = indom_root; ip != NULL; ip = ip->i_next) {
570 if (ip->new_indom != ip->old_indom)
571 return 1;
572 for (i = 0; i < ip->numinst; i++) {
573 if (ip->flags[i])
574 return 1;
575 }
576 }
577 for (mp = metric_root; mp != NULL; mp = mp->m_next) {
578 if (mp->flags != 0 || mp->ip != NULL)
579 return 1;
580 }
581
582 return 0;
583 }
584
585 static int
586 fixstamp(struct timeval *tvp)
587 {
588 if (global.flags & GLOBAL_CHANGE_TIME) {
589 if (global.time.tv_sec > 0) {
590 tvp->tv_sec += global.time.tv_sec;
591 tvp->tv_usec += global.time.tv_usec;
592 if (tvp->tv_usec > 1000000) {
593 tvp->tv_sec++;
594 tvp->tv_usec -= 1000000;
595 }
596 return 1;
597 }
598 else if (global.time.tv_sec < 0) {
599 /* parser makes tv_sec < 0 and tv_usec >= 0 */
600 tvp->tv_sec += global.time.tv_sec;
601 tvp->tv_usec -= global.time.tv_usec;
602 if (tvp->tv_usec < 0) {
603 tvp->tv_sec--;
604 tvp->tv_usec += 1000000;
605 }
606 return 1;
607 }
608 }
609 return 0;
610 }
611
612 /*
613 * Link metricspec_t entries to corresponding indom_t entry if there
614 * are changes to instance identifiers or instance names (includes
615 * instance deletion)
616 */
617 static void
618 link_entries(void)
619 {
620 indomspec_t *ip;
621 metricspec_t *mp;
622 __pmHashCtl *hcp;
623 __pmHashNode *this;
624 int i;
625 int change;
626
627 hcp = &inarch.ctxp->c_archctl->ac_log->l_hashpmid;
628 for (ip = indom_root; ip != NULL; ip = ip->i_next) {
629 change = 0;
630 for (i = 0; i < ip->numinst; i++)
631 change |= (ip->flags[i] != 0);
632 if (change == 0 && ip->new_indom == ip->old_indom)
633 continue;
634
635 for (this = __pmHashWalk(hcp, W_START); this != NULL; this = __pmHashWalk(hcp, W_NEXT)) {
636 mp = start_metric((pmID)(this->key));
637 if (mp->old_desc.indom == ip->old_indom) {
638 if (change)
639 mp->ip = ip;
640 if (ip->new_indom != ip->old_indom) {
641 if (mp->flags & METRIC_CHANGE_INDOM) {
642 /* indom already changed via metric clause */
643 if (mp->new_desc.indom != ip->new_indom) {
644 char strbuf[80];
645 snprintf(strbuf, sizeof(strbuf), "%s", pmInDomStr(mp->new_desc.indom));
646 snprintf(mess, sizeof(mess), "Conflicting indom change for metric %s (%s from metric clause, %s from indom clause)", mp->old_name, strbuf, pmInDomStr(ip->new_indom));
647 yysemantic(mess);
648 }
649 }
650 else {
651 mp->flags |= METRIC_CHANGE_INDOM;
652 mp->new_desc.indom = ip->new_indom;
653 }
654 }
655 }
656 }
657 }
658 }
659
660 static void
661 check_indoms()
662 {
663 /*
664 * For each metric, make sure the output instance domain will be in
665 * the output archive.
666 * Called after link_entries(), so if an input metric is associated
667 * with an instance domain that has any instance rewriting, we're OK.
668 * The case to be checked here is a rewritten metric with an indom
669 * clause and no associated indomspec_t (so no instance domain changes,
670 * but the new indom may not match any indom in the archive.
671 */
672 metricspec_t *mp;
673 indomspec_t *ip;
674 __pmHashCtl *hcp;
675 __pmHashNode *this;
676
677
678 hcp = &inarch.ctxp->c_archctl->ac_log->l_hashindom;
679
680 for (mp = metric_root; mp != NULL; mp = mp->m_next) {
681 if (mp->ip != NULL)
682 /* associated indom has instance changes, we're OK */
683 continue;
684 if ((mp->flags & METRIC_CHANGE_INDOM) && mp->new_desc.indom != PM_INDOM_NULL) {
685 for (this = __pmHashWalk(hcp, W_START); this != NULL; this = __pmHashWalk(hcp, W_NEXT)) {
686 /*
687 * if this indom has an indomspec_t, check that, else
688 * this indom will go to the archive without change
689 */
690 for (ip = indom_root; ip != NULL; ip = ip->i_next) {
691 if (ip->old_indom == mp->old_desc.indom)
692 break;
693 }
694 if (ip == NULL) {
695 if ((pmInDom)(this->key) == mp->new_desc.indom)
696 /* we're OK */
697 break;
698 }
699 else {
700 if (ip->new_indom != ip->old_indom &&
701 ip->new_indom == mp->new_desc.indom)
702 /* we're OK */
703 break;
704 }
705 }
706 if (this == NULL) {
707 snprintf(mess, sizeof(mess), "New indom (%s) for metric %s is not in the output archive", pmInDomStr(mp->new_desc.indom), mp->old_name);
708 yysemantic(mess);
709 }
710 }
711 }
712
713 /*
714 * For each modified instance domain, make sure instances are
715 * still unique and instance names are unique to the first
716 * space.
717 */
718 for (ip = indom_root; ip != NULL; ip = ip->i_next) {
719 int i;
720 for (i = 0; i < ip->numinst; i++) {
721 int insti;
722 char *namei;
723 int j;
724 if (ip->flags[i] & INST_CHANGE_INST)
725 insti = ip->new_inst[i];
726 else
727 insti = ip->old_inst[i];
728 if (ip->flags[i] & INST_CHANGE_INAME)
729 namei = ip->new_iname[i];
730 else
731 namei = ip->old_iname[i];
732 for (j = 0; j < ip->numinst; j++) {
733 int instj;
734 char *namej;
735 if (i == j)
736 continue;
737 if (ip->flags[j] & INST_CHANGE_INST)
738 instj = ip->new_inst[j];
739 else
740 instj = ip->old_inst[j];
741 if (ip->flags[j] & INST_CHANGE_INAME)
742 namej = ip->new_iname[j];
743 else
744 namej = ip->old_iname[j];
745 if (insti == instj) {
746 snprintf(mess, sizeof(mess), "Duplicate instance id %d (\"%s\" and \"%s\") for indom %s", insti, namei, namej, pmInDomStr(ip->old_indom));
747 yysemantic(mess);
748 }
749 if (inst_name_eq(namei, namej) > 0) {
750 snprintf(mess, sizeof(mess), "Duplicate instance name \"%s\" (%d) and \"%s\" (%d) for indom %s", namei, insti, namej, instj, pmInDomStr(ip->old_indom));
751 yysemantic(mess);
752 }
753 }
754 }
755 }
756 }
757
758 static void
759 check_output()
760 {
761 /*
762 * For each metric, if there is an INDOM clause, perform some
763 * additional semantic checks and perhaps a name -> instance id
764 * mapping.
765 *
766 * Note instance renumbering happens _after_ value selction from
767 * the INDOM -> ,,,, OUTPUT clause, so all references to
768 * instance names and instance ids are relative to the
769 * "old" set.
770 */
771 metricspec_t *mp;
772 indomspec_t *ip;
773
774 for (mp = metric_root; mp != NULL; mp = mp->m_next) {
775 if ((mp->flags & METRIC_CHANGE_INDOM)) {
776 if (mp->output == OUTPUT_ONE || mp->output == OUTPUT_FIRST) {
777 /*
778 * cases here are
779 * INAME "name"
780 * => one_name == "name" and one_inst == PM_IN_NULL
781 * INST id
782 * => one_name == NULL and one_inst = id
783 */
784 if (mp->old_desc.indom != PM_INDOM_NULL && mp->output == OUTPUT_ONE) {
785 /*
786 * old metric is not singular, so one_name and one_inst
787 * are used to pick the value
788 * also map one_name -> one_inst
789 */
790 int i;
791 ip = start_indom(mp->old_desc.indom);
792 for (i = 0; i < ip->numinst; i++) {
793 if (mp->one_name != NULL) {
794 if (inst_name_eq(ip->old_iname[i], mp->one_name) > 0) {
795 mp->one_name = NULL;
796 mp->one_inst = ip->old_inst[i];
797 break;
798 }
799 }
800 else if (ip->old_inst[i] == mp->one_inst)
801 break;
802 }
803 if (i == ip->numinst) {
804 if (wflag) {
805 if (mp->one_name != NULL)
806 snprintf(mess, sizeof(mess), "Instance \"%s\" from OUTPUT clause not found in old indom %s", mp->one_name, pmInDomStr(mp->old_desc.indom));
807 else
808 snprintf(mess, sizeof(mess), "Instance %d from OUTPUT clause not found in old indom %s", mp->one_inst, pmInDomStr(mp->old_desc.indom));
809 yywarn(mess);
810 }
811 }
812 }
813 if (mp->new_desc.indom != PM_INDOM_NULL) {
814 /*
815 * new metric is not singular, so one_inst should be
816 * found in the new instance domain ... ignore one_name
817 * other than to map one_name -> one_inst if one_inst
818 * is not already known
819 */
820 int i;
821 ip = start_indom(mp->new_desc.indom);
822 for (i = 0; i < ip->numinst; i++) {
823 if (mp->one_name != NULL) {
824 if (inst_name_eq(ip->old_iname[i], mp->one_name) > 0) {
825 mp->one_name = NULL;
826 mp->one_inst = ip->old_inst[i];
827 break;
828 }
829 }
830 else if (ip->old_inst[i] == mp->one_inst)
831 break;
832 }
833 if (i == ip->numinst) {
834 if (wflag) {
835 if (mp->one_name != NULL)
836 snprintf(mess, sizeof(mess), "Instance \"%s\" from OUTPUT clause not found in new indom %s", mp->one_name, pmInDomStr(mp->new_desc.indom));
837 else
838 snprintf(mess, sizeof(mess), "Instance %d from OUTPUT clause not found in new indom %s", mp->one_inst, pmInDomStr(mp->new_desc.indom));
839 yywarn(mess);
840 }
841 }
842 /*
843 * use default rule (id 0) if INAME not found and
844 * and instance id is needed for output value
845 */
846 if (mp->old_desc.indom == PM_INDOM_NULL && mp->one_inst == PM_IN_NULL)
847 mp->one_inst = 0;
848 }
849 }
850 }
851 }
852 }
853
854
855 int
856 main(int argc, char **argv)
857 {
858 int sts;
859 int stslog; /* sts from nextlog() */
860 int stsmeta = 0; /* sts from nextmeta() */
861 int i;
862 int ti_idx; /* next slot for input temporal index */
863 int needti = 0;
864 int doneti = 0;
865 __pmTimeval tstamp; /* for last log record */
866 off_t old_log_offset = 0; /* log offset before last log record */
867 off_t old_meta_offset;
868
869 __pmSetProgname(argv[0]);
870
871 /* process cmd line args */
872 if (parseargs(argc, argv) < 0) {
873 usage();
874 exit(1);
875 }
876
877 /* input archive */
878 if (iflag == 0)
879 inarch.name = argv[argc-2];
880 else
881 inarch.name = argv[argc-1];
882 inarch.logrec = inarch.metarec = NULL;
883 inarch.mark = 0;
884 inarch.rp = NULL;
885
886 if ((inarch.ctx = pmNewContext(PM_CONTEXT_ARCHIVE, inarch.name)) < 0) {
887 fprintf(stderr, "%s: Error: cannot open archive \"%s\": %s\n",
888 pmProgname, inarch.name, pmErrStr(inarch.ctx));
889 exit(1);
890 }
891 inarch.ctxp = __pmHandleToPtr(inarch.ctx);
892 assert(inarch.ctxp != NULL);
893
894 if ((sts = pmGetArchiveLabel(&inarch.label)) < 0) {
895 fprintf(stderr, "%s: Error: cannot get archive label record (%s): %s\n", pmProgname, inarch.name, pmErrStr(sts));
896 exit(1);
897 }
898
899 if ((inarch.label.ll_magic & 0xff) != PM_LOG_VERS02) {
900 fprintf(stderr,"%s: Error: illegal version number %d in archive (%s)\n",
901 pmProgname, inarch.label.ll_magic & 0xff, inarch.name);
902 exit(1);
903 }
904
905 /* output archive */
906 if (iflag && Cflag == 0) {
907 /*
908 * -i (in place) method outline
909 *
910 * + create one temporary base filename in the same directory is
911 * the input archive, keep a copy of this name this accessed
912 * via outarch.name
913 * + create a second (and different) temporary base file name
914 * in the same directory, keep this name in bak_base[]
915 * + close the temporary file descriptors and unlink the basename
916 * files
917 * + create the output as per normal in outarch.name
918 * + rename the _input_ archive files using the _second_ temporary
919 * basename
920 * + rename the output archive files to the basename of the input
921 * archive ... if this step fails for any reason, restore the
922 * original input files
923 * + unlink all the (old) input archive files
924 */
925 char path[MAXPATHLEN+1];
926 char dname[MAXPATHLEN+1];
927 int tmp_f1; /* fd for first temp basename */
928 int tmp_f2; /* fd for second temp basename */
929 strncpy(path, argv[argc-1], sizeof(path));
930 strncpy(dname, dirname(path), sizeof(dname));
931 #if HAVE_MKSTEMP
932 sprintf(path, "%s%cXXXXXX", dname, __pmPathSeparator());
933 tmp_f1 = mkstemp(path);
934 outarch.name = strdup(path);
935 if (outarch.name == NULL) {
936 fprintf(stderr, "temp file strdup(%s) failed: %s\n", path, strerror(errno));
937 abandon();
938 }
939 sprintf(bak_base, "%s%cXXXXXX", dname, __pmPathSeparator());
940 tmp_f2 = mkstemp(bak_base);
941 #else
942 char fname[MAXPATHLEN+1];
943 char *s;
944 strncpy(path, argv[argc-1], sizeof(path));
945 strncpy(fname, basename(path)m sizeof(fname));
946 if ((s = tempnam(dname, fname)) == NULL) {
947 fprintf(stderr, "Error: first tempnam() failed: %s\n", strerror(errno));
948 abandon();
949 }
950 else {
951 outarch.name = strdup(s);
952 if (outarch.name == NULL) {
953 fprintf(stderr, "temp file strdup(%s) failed: %s\n", s, strerror(errno));
954 abandon();
955 }
956 tmp_f1 = open(outarch.name, O_WRONLY|O_CREAT|O_EXCL, 0600);
957 }
958 if ((s = tempnam(dname, fname)) == NULL) {
959 fprintf(stderr, "Error: second tempnam() failed: %s\n", strerror(errno));
960 abandon();
961 }
962 else {
963 strcpy(bak_base, s);
964 tmp_f2 = open(bak_base, O_WRONLY|O_CREAT|O_EXCL, 0600);
965 }
966 #endif
967 if (tmp_f1 < 0) {
968 fprintf(stderr, "Error: create first temp (%s) failed: %s\n", outarch.name, strerror(errno));
969 abandon();
970 }
971 if (tmp_f2 < 0) {
972 fprintf(stderr, "Error: create second temp (%s) failed: %s\n", bak_base, strerror(errno));
973 abandon();
974 }
975 close(tmp_f1);
976 close(tmp_f2);
977 unlink(outarch.name);
978 unlink(bak_base);
979 }
980 else
981 outarch.name = argv[argc-1];
982
983 /*
984 * process config file(s)
985 */
986 for (i = 0; i < nconf; i++) {
987 parseconfig(conf[i]);
988 }
989
990 /*
991 * cross-specification dependencies and semantic checks once all
992 * config files have been processed
993 */
994 link_entries();
995 check_indoms();
996 check_output();
997
998 if (vflag)
999 reportconfig();
1000
1001 if (Cflag)
1002 exit(0);
1003
1004 if (qflag && anychange() == 0)
1005 exit(0);
1006
1007 /* create output log - must be done before writing label */
1008 if ((sts = __pmLogCreate("", outarch.name, PM_LOG_VERS02, &outarch.logctl)) < 0) {
1009 fprintf(stderr, "%s: Error: __pmLogCreate(%s): %s\n",
1010 pmProgname, outarch.name, pmErrStr(sts));
1011 abandon();
1012 }
1013
1014 /* initialize and write label records */
1015 newlabel();
1016 outarch.logctl.l_state = PM_LOG_STATE_INIT;
1017 writelabel(0);
1018
1019 first_datarec = 1;
1020 ti_idx = 0;
1021
1022 /*
1023 * loop
1024 * - get next log record
1025 * - write out new/changed meta data required by this log record
1026 * - write out log
1027 * - do ti update if necessary
1028 */
1029 while (1) {
1030 static long in_offset; /* for -Dappl0 */
1031
1032 fflush(outarch.logctl.l_mdfp);
1033 old_meta_offset = ftell(outarch.logctl.l_mdfp);
1034
1035 in_offset = ftell(inarch.ctxp->c_archctl->ac_log->l_mfp);
1036 stslog = nextlog();
1037 if (stslog < 0) {
1038 #if PCP_DEBUG
1039 if (pmDebug & DBG_TRACE_APPL0)
1040 fprintf(stderr, "Log: read EOF @ offset=%ld\n", in_offset);
1041 #endif
1042 break;
1043 }
1044 if (stslog == 1) {
1045 /* volume change */
1046 if (inarch.ctxp->c_archctl->ac_log->l_curvol >= outarch.logctl.l_curvol+1)
1047 /* track input volume numbering */
1048 newvolume(inarch.ctxp->c_archctl->ac_log->l_curvol);
1049 else
1050 /*
1051 * output archive volume number is ahead, probably because
1052 * rewriting has forced an earlier volume change
1053 */
1054 newvolume(outarch.logctl.l_curvol+1);
1055 }
1056 #if PCP_DEBUG
1057 if (pmDebug & DBG_TRACE_APPL0) {
1058 struct timeval stamp;
1059 fprintf(stderr, "Log: read ");
1060 stamp.tv_sec = inarch.rp->timestamp.tv_sec;
1061 stamp.tv_usec = inarch.rp->timestamp.tv_usec;
1062 __pmPrintStamp(stderr, &stamp);
1063 fprintf(stderr, " numpmid=%d @ offset=%ld\n", inarch.rp->numpmid, in_offset);
1064 }
1065 #endif
1066
1067 if (ti_idx < inarch.ctxp->c_archctl->ac_log->l_numti) {
1068 __pmLogTI *tip = &inarch.ctxp->c_archctl->ac_log->l_ti[ti_idx];
1069 if (tip->ti_stamp.tv_sec == inarch.rp->timestamp.tv_sec &&
1070 tip->ti_stamp.tv_usec == inarch.rp->timestamp.tv_usec) {
1071 /*
1072 * timestamp on input pmResult matches next temporal index
1073 * entry for input archive ... make sure matching temporal
1074 * index entry added to output archive
1075 */
1076 needti = 1;
1077 ti_idx++;
1078 }
1079 }
1080
1081 /*
1082 * optionally rewrite timestamp in pmResult for global time
1083 * adjustment ... flows to output pmResult, indom entries in
1084 * metadata, temporal index entries and label records
1085 * */
1086 fixstamp(&inarch.rp->timestamp);
1087
1088 /*
1089 * process metadata until find an indom record with timestamp
1090 * after the current log record, or a metric record for a pmid
1091 * that is not in the current log record
1092 */
1093 for ( ; ; ) {
1094 pmID pmid; /* pmid for TYPE_DESC */
1095 pmInDom indom; /* indom for TYPE_INDOM */
1096
1097 if (stsmeta == 0) {
1098 in_offset = ftell(inarch.ctxp->c_archctl->ac_log->l_mdfp);
1099 stsmeta = nextmeta();
1100 #if PCP_DEBUG
1101 if (stsmeta < 0 && pmDebug & DBG_TRACE_APPL0)
1102 fprintf(stderr, "Metadata: read EOF @ offset=%ld\n", in_offset);
1103 #endif
1104 }
1105 if (stsmeta < 0) {
1106 break;
1107 }
1108 if (stsmeta == TYPE_DESC) {
1109 int i;
1110 pmid = __ntohpmID(inarch.metarec[2]);
1111 #if PCP_DEBUG
1112 if (pmDebug & DBG_TRACE_APPL0)
1113 fprintf(stderr, "Metadata: read PMID %s @ offset=%ld\n", pmIDStr(pmid), in_offset);
1114 #endif
1115 /*
1116 * if pmid not in next pmResult, we're done ...
1117 */
1118 for (i = 0; i < inarch.rp->numpmid; i++) {
1119 if (pmid == inarch.rp->vset[i]->pmid)
1120 break;
1121 }
1122 if (i == inarch.rp->numpmid)
1123 break;
1124 /*
1125 * rewrite if needed, delete if needed else output
1126 */
1127 do_desc();
1128 }
1129 else if (stsmeta == TYPE_INDOM) {
1130 struct timeval stamp;
1131 __pmTimeval *tvp = (__pmTimeval *)&inarch.metarec[2];
1132 indom = __ntohpmInDom((unsigned int)inarch.metarec[4]);
1133 #if PCP_DEBUG
1134 if (pmDebug & DBG_TRACE_APPL0)
1135 fprintf(stderr, "Metadata: read InDom %s @ offset=%ld\n", pmInDomStr(indom), in_offset);
1136 #endif
1137 stamp.tv_sec = ntohl(tvp->tv_sec);
1138 stamp.tv_usec = ntohl(tvp->tv_usec);
1139 if (fixstamp(&stamp)) {
1140 /* global time adjustment specified */
1141 tvp->tv_sec = htonl(stamp.tv_sec);
1142 tvp->tv_usec = htonl(stamp.tv_usec);
1143 }
1144 /* if time of indom > next pmResult stop processing metadata */
1145 if (stamp.tv_sec > inarch.rp->timestamp.tv_sec)
1146 break;
1147 if (stamp.tv_sec == inarch.rp->timestamp.tv_sec &&
1148 stamp.tv_usec > inarch.rp->timestamp.tv_usec)
1149 break;
1150 needti = 1;
1151 do_indom();
1152 }
1153 else {
1154 fprintf(stderr, "%s: Error: unrecognised meta data type: %d\n",
1155 pmProgname, stsmeta);
1156 abandon();
1157 }
1158 free(inarch.metarec);
1159 stsmeta = 0;
1160 }
1161
1162 if (first_datarec) {
1163 first_datarec = 0;
1164 /* any global time adjustment done after nextlog() above */
1165 outarch.logctl.l_label.ill_start.tv_sec = inarch.rp->timestamp.tv_sec;
1166 outarch.logctl.l_label.ill_start.tv_usec = inarch.rp->timestamp.tv_usec;
1167 /* need to fix start-time in label records */
1168 writelabel(1);
1169 needti = 1;
1170 }
1171
1172 tstamp.tv_sec = inarch.rp->timestamp.tv_sec;
1173 tstamp.tv_usec = inarch.rp->timestamp.tv_usec;
1174
1175 if (needti) {
1176 fflush(outarch.logctl.l_mdfp);
1177 fflush(outarch.logctl.l_mfp);
1178 new_meta_offset = ftell(outarch.logctl.l_mdfp);
1179 fseek(outarch.logctl.l_mdfp, (long)old_meta_offset, SEEK_SET);
1180 __pmLogPutIndex(&outarch.logctl, &tstamp);
1181 fseek(outarch.logctl.l_mdfp, (long)new_meta_offset, SEEK_SET);
1182 needti = 0;
1183 doneti = 1;
1184 }
1185 else
1186 doneti = 0;
1187
1188 old_log_offset = ftell(outarch.logctl.l_mfp);
1189
1190 if (inarch.rp->numpmid == 0)
1191 /* mark record, need index entry @ next log record */
1192 needti = 1;
1193
1194 do_result();
1195 }
1196
1197 if (!doneti) {
1198 /* Final temporal index entry */
1199 fflush(outarch.logctl.l_mfp);
1200 fseek(outarch.logctl.l_mfp, (long)old_log_offset, SEEK_SET);
1201 __pmLogPutIndex(&outarch.logctl, &tstamp);
1202 }
1203
1204 if (iflag) {
1205 if (__pmLogRename(inarch.name, bak_base) < 0)
1206 abandon();
1207 if (__pmLogRename(outarch.name, inarch.name) < 0)
1208 abandon();
1209 __pmLogRemove(bak_base);
1210 }
1211
1212 exit(0);
1213 }
1214
1215 void
1216 abandon(void)
1217 {
1218 char path[MAXNAMELEN+1];
1219 if (dflag == 0) {
1220 if (Cflag == 0 && iflag == 0)
1221 fprintf(stderr, "Archive \"%s\" not created.\n", outarch.name);
1222
1223 __pmLogRemove(outarch.name);
1224 if (iflag)
1225 __pmLogRename(bak_base, inarch.name);
1226 while (outarch.logctl.l_curvol >= 0) {
1227 snprintf(path, sizeof(path), "%s.%d", outarch.name, outarch.logctl.l_curvol);
1228 unlink(path);
1229 outarch.logctl.l_curvol--;
1230 }
1231 snprintf(path, sizeof(path), "%s.meta", outarch.name);
1232 unlink(path);
1233 snprintf(path, sizeof(path), "%s.index", outarch.name);
1234 unlink(path);
1235 }
1236 else
1237 fprintf(stderr, "Archive \"%s\" creation truncated.\n", outarch.name);
1238
1239 exit(1);
1240 }