/* * pmlogconv - convert PCP archive logs from V1 to V2 format */ #include #include #include #include #include #include "pcp/pmapi.h" #include "pcp/impl.h" #define LOG 0 #define META 1 typedef struct { /* input archive control */ int ctx; char *name; pmLogLabel label; __pmPDU *pb[2]; int pick[2]; int eof[2]; } inarch_t; static __pmLogCtl logctl; /* output archive control */ static inarch_t inarch; static __pmTimeval current; /* most recently output timestamp */ static __pmHashCtl pmid_done; static int exit_status = 0; extern int _pmLogGet(__pmLogCtl *, int, __pmPDU **); extern int _pmLogPut(FILE *, __pmPDU *); extern void _pmUnpackDesc(__pmPDU *, pmDesc *); extern void rewrite_pdu(__pmPDU *); /* * */ void writelabel_metati(int do_rewind) { if (do_rewind) rewind(logctl.l_tifp); logctl.l_label.ill_vol = PM_LOG_VOL_TI; __pmLogWriteLabel(logctl.l_tifp, &logctl.l_label); if (do_rewind) rewind(logctl.l_mdfp); logctl.l_label.ill_vol = PM_LOG_VOL_META; __pmLogWriteLabel(logctl.l_mdfp, &logctl.l_label); } void writelabel_data(void) { logctl.l_label.ill_vol = 0; __pmLogWriteLabel(logctl.l_mfp, &logctl.l_label); } static void _report(FILE *fp) { off_t here; struct stat sbuf; here = lseek(fileno(fp), 0L, SEEK_CUR); fprintf(stderr, "Error occurred at byte offset %ld into a file of", here); if (fstat(fileno(fp), &sbuf) < 0) fprintf(stderr, ": stat: %s\n", strerror(errno)); else fprintf(stderr, " %ld bytes.\n", sbuf.st_size); fprintf(stderr, "The last record, and the remainder of this file will not be merged.\n"); exit_status = 1; } /* * pick next archive record * - if next metadata is pmDesc, output this * - else choose youngest data or pmInDom metadata * - if data and pmIndom have same timestamp, choose pmInDom */ static int nextrec(void) { int sts; int pick = PM_ERR_EOL; __pmTimeval *this; __pmLogCtl *lcp; __pmContext *ctxp; if (!inarch.eof[META]) { if (inarch.pb[META] == (__pmPDU *)0) { /* refill metadata buffer */ ctxp = __pmHandleToPtr(inarch.ctx); lcp = ctxp->c_archctl->ac_log; if ((sts = _pmLogGet(lcp, PM_LOG_VOL_META, &inarch.pb[META])) < 0) { inarch.eof[META] = 1; if (sts != PM_ERR_EOL) { fprintf(stderr, "%s: Error: __pmLogRead[meta %s]: %s\n", pmProgname, inarch.name, pmErrStr(sts)); _report(lcp->l_mdfp); exit(1); } } } } if (!inarch.eof[LOG]) { if (inarch.pb[LOG] == (__pmPDU *)0) { ctxp = __pmHandleToPtr(inarch.ctx); lcp = ctxp->c_archctl->ac_log; if ((sts = _pmLogGet(lcp, 0, &inarch.pb[LOG])) < 0) { inarch.eof[LOG] = 1; if (sts != PM_ERR_EOL) { fprintf(stderr, "%s: Error: __pmLogRead[log %s]: %s\n", pmProgname, inarch.name, pmErrStr(sts)); _report(lcp->l_mfp); exit(1); } } } } if (!inarch.eof[META] && ntohl(inarch.pb[META][1]) == TYPE_DESC) { /* pmDesc entry, output this immediately */ pmDesc desc; _pmUnpackDesc(inarch.pb[META], &desc); if (__pmHashSearch((int)desc.pmid, &pmid_done) != (__pmHashNode *)0) { /* already been processed from another log, skip this one */ fprintf(stderr, "Botch: pmDesc for pmid %s seen twice in input archive\n", pmIDStr(desc.pmid)); exit(1); } __pmHashAdd((int)desc.pmid, (void *)0, &pmid_done); inarch.pick[META] = TYPE_DESC; return 0; } if (!inarch.eof[LOG]) { this = (__pmTimeval *)&inarch.pb[LOG][1]; inarch.pick[LOG] = 1; pick = 0; current.tv_sec = ntohl(this->tv_sec); current.tv_usec = ntohl(this->tv_usec); } if (!inarch.eof[META]) { this = (__pmTimeval *)&inarch.pb[META][2]; if (ntohl(this->tv_sec) < current.tv_sec || (ntohl(this->tv_sec) == current.tv_sec && ntohl(this->tv_usec) <= current.tv_usec)) { inarch.pick[LOG] = 0; inarch.pick[META] = TYPE_INDOM; pick = 0; current.tv_sec = ntohl(this->tv_sec); current.tv_usec = ntohl(this->tv_usec); } } return pick; } int main(int argc, char **argv) { int c; int sts; char *p; int errflag = 0; char *output; char *pmnsfile = NULL; int needti = 0; off_t old_log_offset; off_t new_log_offset; off_t old_meta_offset; off_t new_meta_offset; extern char *optarg; extern int optind; extern int pmDebug; /* trim cmd name of leading directory components */ pmProgname = argv[0]; for (p = pmProgname; *p; p++) { if (*p == '/') pmProgname = p+1; } while ((c = getopt(argc, argv, "D:n:?")) != EOF) { switch (c) { case 'D': /* debug flag */ sts = __pmParseDebug(optarg); if (sts < 0) { fprintf(stderr, "%s: unrecognized debug flag specification (%s)\n", pmProgname, optarg); errflag++; } else pmDebug |= sts; break; case 'n': pmnsfile = optarg; break; case '?': default: errflag++; break; } } if (errflag != 0 || pmnsfile == NULL || optind != argc-2) { fprintf(stderr, "Usage: %s [options] input-archive output-archive\n\ \n\ Options\n\ -D debug standard PCP debug flag\n\ -n pmnsfile PMNS to use (not optional)\n", pmProgname); exit(1); } if ((sts = pmLoadNameSpace(pmnsfile)) < 0) { fprintf(stderr, "%s: Error loading namespace: %s\n", pmProgname, pmErrStr(sts)); exit(1); } inarch.name = argv[optind]; if ((inarch.ctx = pmNewContext(PM_CONTEXT_ARCHIVE, inarch.name)) < 0) { fprintf(stderr, "%s: Error: cannot open archive \"%s\": %s\n", pmProgname, inarch.name, pmErrStr(inarch.ctx)); exit(1); } if ((sts = pmGetArchiveLabel(&inarch.label)) < 0) { fprintf(stderr, "%s: Error: cannot get archive label record (%s): %s\n", pmProgname, inarch.name, pmErrStr(sts)); exit(1); } inarch.pb[LOG] = inarch.pb[META] = (__pmPDU *)0; inarch.eof[LOG] = inarch.eof[META] = 0; inarch.pick[LOG] = inarch.pick[META] = 0; output = argv[argc-1]; if ((sts = __pmLogCreate("", output, PM_LOG_VERS02, &logctl)) < 0) { fprintf(stderr, "%s: Error: __pmLogCreate: %s\n", pmProgname, pmErrStr(sts)); exit(1); } logctl.l_label.ill_magic = PM_LOG_MAGIC | PM_LOG_VERS02; logctl.l_label.ill_pid = inarch.label.ll_pid; logctl.l_label.ill_start.tv_sec = inarch.label.ll_start.tv_sec; logctl.l_label.ill_start.tv_usec = inarch.label.ll_start.tv_usec; strcpy(logctl.l_label.ill_hostname, inarch.label.ll_hostname); strcpy(logctl.l_label.ill_tz, inarch.label.ll_tz); writelabel_metati(0); /* * do meta and log records at the same time ... in the case of an * equal match, the log record is processed before the corresponding * meta data */ for ( ; ; ) { if (nextrec() < 0) break; if (inarch.pick[LOG]) { old_log_offset = ftell(logctl.l_mfp); old_meta_offset = ftell(logctl.l_mdfp); if (old_log_offset == 0) { /* write label record for data file */ logctl.l_label.ill_start.tv_sec = current.tv_sec; logctl.l_label.ill_start.tv_usec = current.tv_usec; writelabel_data(); old_log_offset = ftell(logctl.l_mfp); needti = 1; } /* * ignore 2^31 check for vol switch on output archive ... * if input archive is one volume, would have been unreadable * if too large * if input archive is multivolume, we create a single * volume output, so there is a potential problem here ... * TODO - revisit this issue only if a need arises */ /* translate data record from V1 to V2 */ rewrite_pdu(inarch.pb[LOG]); /* write data record out */ if ((sts = _pmLogPut(logctl.l_mfp, inarch.pb[LOG])) < 0) { fprintf(stderr, "%s: Error: _pmLogPut: log data: %s\n", pmProgname, pmErrStr(sts)); exit(1); } /* free data record buffer */ free(inarch.pb[LOG]); inarch.pb[LOG] = (__pmPDU *)0; inarch.pick[LOG] = 0; if (needti) { fflush(logctl.l_mfp); fflush(logctl.l_mdfp); new_log_offset = ftell(logctl.l_mfp); new_meta_offset = ftell(logctl.l_mdfp); fseek(logctl.l_mfp, old_log_offset, SEEK_SET); fseek(logctl.l_mdfp, old_meta_offset, SEEK_SET); __pmLogPutIndex(&logctl, ¤t); fseek(logctl.l_mfp, new_log_offset, SEEK_SET); fseek(logctl.l_mdfp, new_log_offset, SEEK_SET); needti = 0; } } if (inarch.pick[META]) { /* write metadata out and force temporal index update if indom */ if (inarch.pick[META] == TYPE_DESC) { pmDesc desc; char **names = NULL; int numnames; char *myname; _pmUnpackDesc(inarch.pb[META], &desc); if ((numnames = pmNameAll(desc.pmid, &names)) < 0) { myname = strdup(pmIDStr(desc.pmid)); } else { myname = strdup(names[0]); free(names); } __pmLogPutDesc(&logctl, &desc, 1, &myname); free(myname); } else { if ((sts = _pmLogPut(logctl.l_mdfp, inarch.pb[META])) < 0) { fprintf(stderr, "%s: Error: _pmLogPut: meta data: %s\n", pmProgname, pmErrStr(sts)); exit(1); } needti = 1; } /* free metadata buffer */ free(inarch.pb[META]); inarch.pb[META] = (__pmPDU *)0; inarch.pick[META] = 0; } } writelabel_metati(1); exit(exit_status); }