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