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