1 /* 2 * Copyright (c) 2017-2019 The DragonFly Project. All rights reserved. 3 * 4 * This code is derived from software contributed to The DragonFly Project 5 * by Matthew Dillon <dillon@backplane.com> 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in 15 * the documentation and/or other materials provided with the 16 * distribution. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 21 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 22 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 23 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, 24 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 25 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 26 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 27 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 28 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 */ 31 32 #include "kcollect.h" 33 34 #include <ndbm.h> 35 #include <fcntl.h> 36 #include <errno.h> 37 38 #define SLEEP_INTERVAL 60 /* minimum is KCOLLECT_INTERVAL */ 39 #define DATA_BASE_INDEX 8 /* up to 8 headers */ 40 41 #define DISPLAY_TIME_ONLY "%H:%M:%S" 42 #define DISPLAY_FULL_DATE "%F %H:%M:%S" 43 #define HDR_BASE "HEADER" 44 #define HDR_STRLEN 6 45 46 #define HDR_FMT_INDEX 0 47 #define HDR_FMT_TITLE 1 48 #define HDR_FMT_HOST 2 49 50 #define HOST_NAME_MAX sysconf(_SC_HOST_NAME_MAX) 51 52 static void format_output(uintmax_t, char, uintmax_t, char *); 53 static void dump_text(kcollect_t *, size_t, size_t, const char *); 54 static const char *get_influx_series(const char *); 55 static void dump_influxdb(kcollect_t *, size_t, size_t, const char *); 56 57 static void (*dumpfn)(kcollect_t *, size_t, size_t, const char *); 58 59 static void dump_dbm(kcollect_t *, size_t, const char *); 60 static void load_dbm(const char *datafile, kcollect_t **, size_t *); 61 static int rec_comparator(const void *, const void *); 62 static void dump_fields(kcollect_t *); 63 static void adjust_fields(kcollect_t *, const char *); 64 static void restore_headers(kcollect_t *, const char *); 65 static int str2unix(const char *, const char*); 66 static kcollect_t *load_kernel(kcollect_t *, kcollect_t *, size_t *); 67 68 FILE *OutFP; 69 int UseGMT; 70 int OutputWidth = 1024; 71 int OutputHeight = 1024; 72 int SmoothOpt; 73 int LoadedFromDB; 74 int HostnameMismatch; 75 int Fflag; 76 77 int 78 main(int ac, char **av) 79 { 80 kcollect_t *ary_base; 81 kcollect_t *ary; 82 size_t bytes = 0; 83 size_t count; 84 size_t total_count; 85 const char *datafile = NULL; 86 const char *fields = NULL; 87 int cmd = 't'; 88 int ch; 89 int keepalive = 0; 90 int last_ticks; 91 int loops = 0; 92 int maxtime = 0; 93 const char *dbmFile = NULL; 94 int fromFile = 0; 95 96 OutFP = stdout; 97 dumpfn = dump_text; 98 99 sysctlbyname("kern.collect_data", NULL, &bytes, NULL, 0); 100 if (bytes == 0) { 101 fprintf(stderr, "kern.collect_data not available\n"); 102 exit(1); 103 } 104 105 while ((ch = getopt(ac, av, "o:O:b:d:r:fFlsgt:xw:GW:H:")) != -1) { 106 char *suffix; 107 108 switch(ch) { 109 case 'o': 110 fields = optarg; 111 break; 112 case 'O': 113 if ((strncasecmp("influxdb", optarg, 16) == 0)) { 114 dumpfn = dump_influxdb; 115 } else if (strncasecmp("text", optarg, 16) == 0) { 116 dumpfn = dump_text; 117 } else { 118 fprintf(stderr, "Bad output text format %s\n", optarg); 119 exit(1); 120 } 121 break; 122 case 'b': 123 datafile = optarg; 124 cmd = 'b'; 125 break; 126 case 'd': 127 dbmFile = optarg; 128 fromFile = 1; 129 break; 130 case 'r': 131 datafile = optarg; 132 cmd = 'r'; 133 break; 134 case 'f': 135 keepalive = 1; 136 break; 137 case 'F': 138 Fflag = 1; 139 keepalive = 1; 140 break; 141 case 'l': 142 cmd = 'l'; 143 break; 144 case 's': 145 SmoothOpt = 1; 146 break; 147 case 'w': 148 datafile = optarg; 149 cmd = 'w'; 150 break; 151 case 'g': 152 cmd = 'g'; 153 break; 154 case 'x': 155 cmd = 'x'; 156 break; 157 case 't': 158 maxtime = strtol(optarg, &suffix, 0); 159 switch(*suffix) { 160 case 'd': 161 maxtime *= 24; 162 /* fall through */ 163 case 'h': 164 maxtime *= 60; 165 /* fall through */ 166 case 'm': 167 maxtime *= 60; 168 break; 169 case 0: 170 break; 171 default: 172 fprintf(stderr, 173 "Illegal suffix in -t option\n"); 174 exit(1); 175 } 176 break; 177 case 'G': 178 UseGMT = 1; 179 break; 180 case 'W': 181 OutputWidth = strtol(optarg, NULL, 0); 182 break; 183 case 'H': 184 OutputHeight = strtol(optarg, NULL, 0); 185 break; 186 default: 187 fprintf(stderr, "Unknown option %c\n", ch); 188 exit(1); 189 /* NOT REACHED */ 190 } 191 } 192 if (cmd != 'x' && ac != optind) { 193 fprintf(stderr, "Unknown argument %s\n", av[optind]); 194 exit(1); 195 /* NOT REACHED */ 196 } 197 198 total_count = 0; 199 last_ticks = 0; 200 201 if (cmd == 'x' || cmd == 'w') 202 start_gnuplot(ac - optind, av + optind, datafile); 203 204 do { 205 /* 206 * We do not allow keepalive if there is a hostname 207 * mismatch, there is no point in showing data for the 208 * current host after dumping the data from another one. 209 */ 210 if (HostnameMismatch) { 211 fprintf(stderr, 212 "Hostname mismatch, can't show live data\n"); 213 exit(1); 214 } 215 216 /* 217 * Snarf as much data as we can. If we are looping, 218 * snarf less (no point snarfing stuff we already have). 219 */ 220 bytes = 0; 221 sysctlbyname("kern.collect_data", NULL, &bytes, NULL, 0); 222 if (cmd == 'l') 223 bytes = sizeof(kcollect_t) * 2; 224 225 /* Skip to the newest entries */ 226 if (Fflag && loops == 0) 227 loops++; 228 229 if (loops) { 230 size_t loop_bytes; 231 232 loop_bytes = sizeof(kcollect_t) * 233 (4 + SLEEP_INTERVAL / KCOLLECT_INTERVAL); 234 if (bytes > loop_bytes) 235 bytes = loop_bytes; 236 } 237 238 /* 239 * If we got specified a file to load from: replace the data 240 * array and counter 241 */ 242 if (fromFile) { 243 kcollect_t *dbmAry = NULL; 244 245 load_dbm(dbmFile, &dbmAry, &count); 246 ary = ary_base = dbmAry; 247 } else { 248 kcollect_t scaleid[2]; 249 250 ary_base = malloc(bytes + 251 DATA_BASE_INDEX * sizeof(kcollect_t)); 252 ary = ary_base + DATA_BASE_INDEX; 253 sysctlbyname("kern.collect_data", ary, &bytes, NULL, 0); 254 count = bytes / sizeof(kcollect_t); 255 if (count < 2) { 256 fprintf(stderr, 257 "[ERR] kern.collect_data failed\n"); 258 exit(1); 259 } 260 scaleid[0] = ary[0]; 261 scaleid[1] = ary[1]; 262 count -= 2; 263 ary = load_kernel(scaleid, ary + 2, &count); 264 } 265 if (fields) 266 adjust_fields(&ary[1], fields); 267 268 269 /* 270 * Delete duplicate entries when looping 271 */ 272 if (loops) { 273 while (count > DATA_BASE_INDEX) { 274 if ((int)(ary[count-1].ticks - last_ticks) > 0) 275 break; 276 --count; 277 } 278 } 279 280 /* 281 * Delete any entries beyond the time limit 282 */ 283 if (maxtime) { 284 maxtime *= ary[0].hz; 285 while (count > DATA_BASE_INDEX) { 286 if ((int)(ary[0].ticks - ary[count-1].ticks) < 287 maxtime) { 288 break; 289 } 290 --count; 291 } 292 } 293 294 switch(cmd) { 295 case 't': 296 if (count > DATA_BASE_INDEX) { 297 dumpfn(ary, count, total_count, 298 (fromFile ? DISPLAY_FULL_DATE : 299 DISPLAY_TIME_ONLY)); 300 } 301 break; 302 case 'b': 303 if (HostnameMismatch) { 304 fprintf(stderr, 305 "Hostname mismatch, cannot save to DB\n"); 306 exit(1); 307 } else { 308 if (count > DATA_BASE_INDEX) 309 dump_dbm(ary, count, datafile); 310 } 311 break; 312 case 'r': 313 if (count >= DATA_BASE_INDEX) 314 restore_headers(ary, datafile); 315 break; 316 case 'l': 317 dump_fields(ary); 318 exit(0); 319 break; /* NOT REACHED */ 320 case 'g': 321 if (count > DATA_BASE_INDEX) 322 dump_gnuplot(ary, count); 323 break; 324 case 'w': 325 if (count >= DATA_BASE_INDEX) 326 dump_gnuplot(ary, count); 327 break; 328 case 'x': 329 if (count > DATA_BASE_INDEX) 330 dump_gnuplot(ary, count); 331 break; 332 } 333 if (keepalive && !fromFile) { 334 fflush(OutFP); 335 fflush(stdout); 336 switch(cmd) { 337 case 't': 338 sleep(1); 339 break; 340 case 'x': 341 case 'g': 342 case 'w': 343 sleep(60); 344 break; 345 default: 346 sleep(10); 347 break; 348 } 349 } 350 last_ticks = ary[DATA_BASE_INDEX].ticks; 351 if (count >= DATA_BASE_INDEX) 352 total_count += count - DATA_BASE_INDEX; 353 354 /* 355 * Loop for incremental aquisition. When outputting to 356 * gunplot, we have to send the whole data-set again so 357 * do not increment loops in that case. 358 */ 359 if (cmd != 'g' && cmd != 'x' && cmd != 'w') 360 ++loops; 361 362 free(ary_base); 363 } while (keepalive); 364 365 if (cmd == 'x') 366 pclose(OutFP); 367 } 368 369 static 370 void 371 format_output(uintmax_t value,char fmt,uintmax_t scale, char* ret) 372 { 373 char buf[9]; 374 375 switch(fmt) { 376 case '2': 377 /* 378 * fractional x100 379 */ 380 sprintf(ret, "%5ju.%02ju", 381 value / 100, value % 100); 382 break; 383 case 'p': 384 /* 385 * Percentage fractional x100 (100% = 10000) 386 */ 387 sprintf(ret,"%4ju.%02ju%%", 388 value / 100, value % 100); 389 break; 390 case 'm': 391 /* 392 * Megabytes 393 */ 394 humanize_number(buf, sizeof(buf), value, "", 395 2, 396 HN_FRACTIONAL | 397 HN_NOSPACE); 398 sprintf(ret,"%8.8s", buf); 399 break; 400 case 'c': 401 /* 402 * Raw count over period (this is not total) 403 */ 404 humanize_number(buf, sizeof(buf), value, "", 405 HN_AUTOSCALE, 406 HN_FRACTIONAL | 407 HN_NOSPACE | 408 HN_DIVISOR_1000); 409 sprintf(ret,"%8.8s", buf); 410 break; 411 case 'b': 412 /* 413 * Total bytes (this is a total), output 414 * in megabytes. 415 */ 416 if (scale > 100000000) { 417 humanize_number(buf, sizeof(buf), 418 value, "", 419 3, 420 HN_FRACTIONAL | 421 HN_NOSPACE); 422 } else { 423 humanize_number(buf, sizeof(buf), 424 value, "", 425 2, 426 HN_FRACTIONAL | 427 HN_NOSPACE); 428 } 429 sprintf(ret,"%8.8s", buf); 430 break; 431 default: 432 sprintf(ret,"%s"," "); 433 break; 434 } 435 } 436 437 static 438 const char * 439 get_influx_series(const char *val) 440 { 441 /* cpu values (user, idle, syst) */ 442 if ((strncmp("user", val, 8) == 0) || 443 (strncmp("idle", val, 8) == 0 ) || 444 (strncmp("syst", val, 8) == 0)) 445 return "cpu_value"; 446 447 /* load value (load) */ 448 if (strncmp("load", val, 8) == 0) 449 return "load_value"; 450 451 /* swap values (swapuse, swapano, swapcac) */ 452 if ((strncmp("swapuse", val, 8) == 0) || 453 (strncmp("swapano", val, 8) == 0 ) || 454 (strncmp("swapcac", val, 8) == 0)) 455 return "swap_value"; 456 457 /* vm values (fault, cow, zfill) */ 458 if ((strncmp("fault", val, 8) == 0) || 459 (strncmp("cow", val, 8) == 0 ) || 460 (strncmp("zfill", val, 8) == 0)) 461 return "vm_value"; 462 463 /* memory values (fault, cow, zfill) */ 464 if ((strncmp("cache", val, 8) == 0) || 465 (strncmp("inact", val, 8) == 0 ) || 466 (strncmp("act", val, 8) == 0) || 467 (strncmp("wired", val, 8) == 0) || 468 (strncmp("free", val, 8) == 0)) 469 return "memory_value"; 470 471 /* misc values (syscalls, nlookup, intr, ipi, timer) */ 472 if ((strncmp("syscalls", val, 8) == 0) || 473 (strncmp("nlookup", val, 8) == 0 ) || 474 (strncmp("intr", val, 8) == 0) || 475 (strncmp("ipi", val, 8) == 0) || 476 (strncmp("timer", val, 8) == 0)) 477 return "misc_value"; 478 479 return "misc_value"; 480 481 } 482 483 static 484 void 485 dump_influxdb(kcollect_t *ary, size_t count, size_t total_count, 486 __unused const char* display_fmt) 487 { 488 int j; 489 int i; 490 uintmax_t value; 491 size_t ts_nsec; 492 char hostname[HOST_NAME_MAX]; 493 char *colname; 494 495 if (LoadedFromDB) { 496 snprintf(hostname, HOST_NAME_MAX, "%s", (char *)ary[2].data); 497 } else { 498 if (gethostname(hostname, HOST_NAME_MAX) != 0) { 499 fprintf(stderr, "Failed to get hostname\n"); 500 exit(1); 501 } 502 } 503 504 for (i = count - 1; i >= DATA_BASE_INDEX; --i) { 505 /* 506 * Timestamp 507 */ 508 ts_nsec = (ary[i].realtime.tv_sec 509 * 1000 /* ms */ 510 * 1000 /* usec */ 511 * 1000 /* nsec */ 512 + 123 /* add a few ns since due to missing precision */); 513 ts_nsec += (ary[i].realtime.tv_usec * 1000); 514 515 for (j = 0; j < KCOLLECT_ENTRIES; ++j) { 516 if (ary[1].data[j] == 0) 517 continue; 518 519 /* 520 * NOTE: kernel does not have to provide the scale 521 * (that is, the highest likely value), nor 522 * does it make sense in all cases. 523 * 524 * But should we since we're using raw values? 525 */ 526 value = ary[i].data[j]; 527 colname = (char *)&ary[1].data[j]; 528 529 printf("%s,host=%s,type=%.8s value=%jd %jd\n", 530 get_influx_series(colname), 531 hostname, colname, value, ts_nsec); 532 } 533 printf("\n"); 534 ++total_count; 535 } 536 537 } 538 539 static 540 void 541 dump_text(kcollect_t *ary, size_t count, size_t total_count, 542 const char* display_fmt) 543 { 544 int j; 545 int i; 546 uintmax_t scale; 547 uintmax_t value; 548 char fmt; 549 char sbuf[20]; 550 struct tm *tmv; 551 time_t t; 552 553 for (i = count - 1; i >= DATA_BASE_INDEX; --i) { 554 if ((total_count & 15) == 0) { 555 if (!strcmp(display_fmt, DISPLAY_FULL_DATE)) { 556 printf("%20s", "timestamp "); 557 } else { 558 printf("%8.8s", "time"); 559 } 560 for (j = 0; j < KCOLLECT_ENTRIES; ++j) { 561 if (ary[1].data[j]) { 562 printf(" %8.8s", 563 (char *)&ary[1].data[j]); 564 } 565 } 566 printf("\n"); 567 } 568 569 /* 570 * Timestamp 571 */ 572 t = ary[i].realtime.tv_sec; 573 if (UseGMT) 574 tmv = gmtime(&t); 575 else 576 tmv = localtime(&t); 577 strftime(sbuf, sizeof(sbuf), display_fmt, tmv); 578 printf("%8s", sbuf); 579 580 for (j = 0; j < KCOLLECT_ENTRIES; ++j) { 581 if (ary[1].data[j] == 0) 582 continue; 583 584 /* 585 * NOTE: kernel does not have to provide the scale 586 * (that is, the highest likely value), nor 587 * does it make sense in all cases. 588 * 589 * Example scale - kernel provides total amount 590 * of memory available for memory related 591 * statistics in the scale field. 592 */ 593 value = ary[i].data[j]; 594 scale = KCOLLECT_GETSCALE(ary[0].data[j]); 595 fmt = KCOLLECT_GETFMT(ary[0].data[j]); 596 597 printf(" "); 598 599 format_output(value, fmt, scale, sbuf); 600 printf("%s",sbuf); 601 } 602 603 printf("\n"); 604 ++total_count; 605 } 606 } 607 608 /* restores the DBM database header records to current machine */ 609 static 610 void 611 restore_headers(kcollect_t *ary, const char *datafile) 612 { 613 DBM *db; 614 char hdr[32]; 615 datum key, value; 616 int i; 617 618 db = dbm_open(datafile, (O_RDWR), 619 (S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP)); 620 621 if (db == NULL) { 622 switch (errno) { 623 case EACCES: 624 fprintf(stderr, 625 "[ERR] database file \"%s\" is read-only, " 626 "check permissions. (%i)\n", 627 datafile, errno); 628 break; 629 default: 630 fprintf(stderr, 631 "[ERR] opening our database file \"%s\" " 632 "produced an error. (%i)\n", 633 datafile, errno); 634 } 635 exit(EXIT_FAILURE); 636 } else { 637 for (i = 0; i < DATA_BASE_INDEX; ++i) { 638 snprintf(hdr, sizeof(hdr), "%s%d", HDR_BASE, i); 639 key.dptr = hdr; 640 key.dsize = strlen(key.dptr) + 1; 641 value.dptr = &ary[i].data; 642 value.dsize = sizeof(uint64_t) * KCOLLECT_ENTRIES; 643 if (dbm_store(db,key,value,DBM_REPLACE) == -1) { 644 fprintf(stderr, 645 "[ERR] error storing the value in " 646 "the database file \"%s\" (%i)\n", 647 datafile, errno); 648 dbm_close(db); 649 exit(EXIT_FAILURE); 650 } 651 } 652 } 653 dbm_close(db); 654 } 655 656 657 /* 658 * Store the array of kcollect_t records in a dbm db database, 659 * path passed in datafile 660 */ 661 static 662 void 663 dump_dbm(kcollect_t *ary, size_t count, const char *datafile) 664 { 665 DBM * db; 666 667 db = dbm_open(datafile, (O_RDWR | O_CREAT), 668 (S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP)); 669 if (db == NULL) { 670 switch (errno) { 671 case EACCES: 672 fprintf(stderr, 673 "[ERR] database file \"%s\" is read-only, " 674 "check permissions. (%i)\n", 675 datafile, errno); 676 break; 677 default: 678 fprintf(stderr, 679 "[ERR] opening our database file \"%s\" " 680 "produced an error. (%i)\n", 681 datafile, errno); 682 } 683 exit(EXIT_FAILURE); 684 } else { 685 struct tm *tmv; 686 char buf[20]; 687 datum key; 688 datum value; 689 time_t t; 690 uint i; 691 int cmd; 692 char hdr[32]; 693 694 for (i = 0; i < count; ++i) { 695 /* 696 * The first DATA_BASE_INDEX records are special. 697 */ 698 cmd = DBM_INSERT; 699 if (i < DATA_BASE_INDEX) { 700 snprintf(hdr, sizeof(hdr), "%s%d", HDR_BASE, i); 701 key.dptr = hdr; 702 key.dsize = strlen(hdr) + 1; 703 704 value = dbm_fetch(db, key); 705 if (value.dptr == NULL || 706 bcmp(ary[i].data, value.dptr, 707 sizeof(uint64_t) * KCOLLECT_ENTRIES)) { 708 cmd = DBM_REPLACE; 709 if (value.dptr != NULL) { 710 fprintf(stderr, 711 "Header %d changed " 712 "in database, " 713 "updating\n", 714 i); 715 } 716 } 717 } else { 718 t = ary[i].realtime.tv_sec; 719 if (LoadedFromDB) 720 tmv = localtime(&t); 721 else 722 tmv = gmtime(&t); 723 strftime(buf, sizeof(buf), 724 DISPLAY_FULL_DATE, tmv); 725 key.dptr = buf; 726 key.dsize = sizeof(buf); 727 } 728 value.dptr = ary[i].data; 729 value.dsize = sizeof(uint64_t) * KCOLLECT_ENTRIES; 730 if (dbm_store(db, key, value, cmd) == -1) { 731 fprintf(stderr, 732 "[ERR] error storing the value in " 733 "the database file \"%s\" (%i)\n", 734 datafile, errno); 735 dbm_close(db); 736 exit(EXIT_FAILURE); 737 } 738 739 } 740 dbm_close(db); 741 } 742 } 743 744 /* 745 * Transform a string (str) matching a format string (fmt) into a unix 746 * timestamp and return it used by load_dbm() 747 */ 748 static 749 int 750 str2unix(const char* str, const char* fmt){ 751 struct tm tm; 752 time_t ts; 753 754 /* 755 * Reset all the fields because strptime only sets what it 756 * finds, which may lead to undefined members 757 */ 758 memset(&tm, 0, sizeof(struct tm)); 759 strptime(str, fmt, &tm); 760 ts = timegm(&tm); 761 762 return (int)ts; 763 } 764 765 /* 766 * Sorts the ckollect_t records by time, to put youngest first, 767 * so desc by timestamp used by load_dbm() 768 */ 769 static 770 int 771 rec_comparator(const void *c1, const void *c2) 772 { 773 const kcollect_t *k1 = (const kcollect_t*)c1; 774 const kcollect_t *k2 = (const kcollect_t*)c2; 775 776 if (k1->realtime.tv_sec < k2->realtime.tv_sec) 777 return -1; 778 if (k1->realtime.tv_sec > k2->realtime.tv_sec) 779 return 1; 780 return 0; 781 } 782 783 /* 784 * Normalizes kcollect records from the kernel. We reserve the first 785 * DATA_BASE_INDEX elements for specific headers. 786 */ 787 static 788 kcollect_t * 789 load_kernel(kcollect_t *scaleid, kcollect_t *ary, size_t *counter) 790 { 791 ary -= DATA_BASE_INDEX; 792 bzero(ary, sizeof(*ary) * DATA_BASE_INDEX); 793 ary[0] = scaleid[0]; 794 ary[1] = scaleid[1]; 795 796 /* 797 * Add host field 798 */ 799 gethostname((char *)ary[2].data, 800 sizeof(uint64_t) * KCOLLECT_ENTRIES - 1); 801 802 *counter += DATA_BASE_INDEX; 803 804 return ary; 805 } 806 807 /* 808 * Loads the kcollect records from a dbm DB database specified in datafile. 809 * returns the resulting array in ret_ary and the array counter in counter 810 */ 811 static 812 void 813 load_dbm(const char* datafile, kcollect_t **ret_ary, 814 size_t *counter) 815 { 816 char hostname[sizeof(uint64_t) * KCOLLECT_ENTRIES]; 817 DBM *db = dbm_open(datafile,(O_RDONLY),(S_IRUSR|S_IRGRP)); 818 datum key; 819 datum value; 820 size_t recCounter = DATA_BASE_INDEX; 821 int headersFound = 0; 822 uint c; 823 uint sc; 824 825 if (db == NULL) { 826 fprintf(stderr, 827 "[ERR] opening our database \"%s\" produced " 828 "an error! (%i)\n", 829 datafile, errno); 830 exit(EXIT_FAILURE); 831 } 832 833 /* counting loop */ 834 for (key = dbm_firstkey(db); key.dptr; key = dbm_nextkey(db)) { 835 value = dbm_fetch(db, key); 836 if (value.dptr == NULL) 837 continue; 838 if (strncmp(key.dptr, HDR_BASE, HDR_STRLEN) == 0) 839 continue; 840 recCounter++; 841 } 842 843 /* with the count allocate enough memory */ 844 if (*ret_ary) 845 free(*ret_ary); 846 847 *ret_ary = malloc(sizeof(kcollect_t) * recCounter); 848 849 if (*ret_ary == NULL) { 850 fprintf(stderr, 851 "[ERR] failed to allocate enough memory to " 852 "hold the database! Aborting.\n"); 853 dbm_close(db); 854 exit(EXIT_FAILURE); 855 } 856 bzero(*ret_ary, sizeof(kcollect_t) * recCounter); 857 858 /* 859 * Actual data retrieval but only of recCounter 860 * records 861 */ 862 c = DATA_BASE_INDEX; 863 key = dbm_firstkey(db); 864 while (key.dptr && c < recCounter) { 865 value = dbm_fetch(db, key); 866 if (value.dptr == NULL) { 867 key = dbm_nextkey(db); 868 continue; 869 } 870 if (!strncmp(key.dptr, HDR_BASE, HDR_STRLEN)) { 871 /* 872 * Ignore unsupported header indices 873 */ 874 sc = strtoul((char *)key.dptr + 875 HDR_STRLEN, NULL, 10); 876 if (sc >= DATA_BASE_INDEX) { 877 key = dbm_nextkey(db); 878 continue; 879 } 880 headersFound |= 1 << sc; 881 } else { 882 sc = c++; 883 (*ret_ary)[sc].realtime.tv_sec = 884 str2unix(key.dptr, 885 DISPLAY_FULL_DATE); 886 } 887 memcpy((*ret_ary)[sc].data, 888 value.dptr, 889 sizeof(uint64_t) * KCOLLECT_ENTRIES); 890 891 key = dbm_nextkey(db); 892 } 893 894 /* 895 * HEADER2 - hostname (must match) 896 */ 897 if ((headersFound & 4) && *(char *)(*ret_ary)[2].data == 0) 898 headersFound &= ~4; 899 900 bzero(hostname, sizeof(hostname)); 901 gethostname(hostname, sizeof(hostname) - 1); 902 903 if (headersFound & 0x0004) { 904 if (*(char *)(*ret_ary)[2].data && 905 strcmp(hostname, (char *)(*ret_ary)[2].data) != 0) { 906 HostnameMismatch = 1; /* Disable certain options */ 907 } 908 } 909 910 /* 911 * Set the counter, 912 * and sort the non-header records. 913 */ 914 *counter = recCounter; 915 qsort(&(*ret_ary)[DATA_BASE_INDEX], recCounter - DATA_BASE_INDEX, 916 sizeof(kcollect_t), rec_comparator); 917 dbm_close(db); 918 919 if ((headersFound & 3) != 3) { 920 fprintf(stderr, "We could not retrieve all necessary headers, " 921 "might be the database file is corrupted? (%i)\n", 922 headersFound); 923 exit(EXIT_FAILURE); 924 } 925 LoadedFromDB = 1; 926 } 927 928 static void 929 dump_fields(kcollect_t *ary) 930 { 931 int j; 932 933 for (j = 0; j < KCOLLECT_ENTRIES; ++j) { 934 if (ary[1].data[j] == 0) 935 continue; 936 printf("%8.8s %c\n", 937 (char *)&ary[1].data[j], 938 KCOLLECT_GETFMT(ary[0].data[j])); 939 } 940 } 941 942 static void 943 adjust_fields(kcollect_t *ent, const char *fields) 944 { 945 char *copy = strdup(fields); 946 char *word; 947 int selected[KCOLLECT_ENTRIES]; 948 int j; 949 950 bzero(selected, sizeof(selected)); 951 952 word = strtok(copy, ", \t"); 953 while (word) { 954 for (j = 0; j < KCOLLECT_ENTRIES; ++j) { 955 if (strncmp(word, (char *)&ent->data[j], 8) == 0) { 956 selected[j] = 1; 957 break; 958 } 959 } 960 word = strtok(NULL, ", \t"); 961 } 962 free(copy); 963 for (j = 0; j < KCOLLECT_ENTRIES; ++j) { 964 if (!selected[j]) 965 ent->data[j] = 0; 966 } 967 } 968