nrelease: Clean up a bit the 'clean' target
[dragonfly.git] / usr.bin / kcollect / kcollect.c
blob1e448cab2de75620d7ae78e91b469e1338700d1a
1 /*
2 * Copyright (c) 2017-2019 The DragonFly Project. All rights reserved.
4 * This code is derived from software contributed to The DragonFly Project
5 * by Matthew Dillon <dillon@backplane.com>
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
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.
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.
32 #include "kcollect.h"
34 #include <ndbm.h>
35 #include <fcntl.h>
36 #include <errno.h>
38 #define SLEEP_INTERVAL 60 /* minimum is KCOLLECT_INTERVAL */
39 #define DATA_BASE_INDEX 8 /* up to 8 headers */
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
46 #define HDR_FMT_INDEX 0
47 #define HDR_FMT_TITLE 1
48 #define HDR_FMT_HOST 2
50 #define HOST_NAME_MAX sysconf(_SC_HOST_NAME_MAX)
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 *);
57 static void (*dumpfn)(kcollect_t *, size_t, size_t, const char *);
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 *);
68 FILE *OutFP;
69 int UseGMT;
70 int OutputWidth = 1024;
71 int OutputHeight = 1024;
72 int SmoothOpt;
73 static int LoadedFromDB;
74 static int HostnameMismatch;
75 static int Fflag;
77 int
78 main(int ac, char **av)
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;
96 OutFP = stdout;
97 dumpfn = dump_text;
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);
105 while ((ch = getopt(ac, av, "o:O:b:d:r:fFlsgt:xw:GW:H:")) != -1) {
106 char *suffix;
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);
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);
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 */
192 if (cmd != 'x' && ac != optind) {
193 fprintf(stderr, "Unknown argument %s\n", av[optind]);
194 exit(1);
195 /* NOT REACHED */
198 total_count = 0;
199 last_ticks = 0;
201 if (cmd == 'x' || cmd == 'w')
202 start_gnuplot(ac - optind, av + optind, datafile);
204 do {
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.
210 if (HostnameMismatch) {
211 fprintf(stderr,
212 "Hostname mismatch, can't show live data\n");
213 exit(1);
217 * Snarf as much data as we can. If we are looping,
218 * snarf less (no point snarfing stuff we already have).
220 bytes = 0;
221 sysctlbyname("kern.collect_data", NULL, &bytes, NULL, 0);
222 if (cmd == 'l')
223 bytes = sizeof(kcollect_t) * 2;
225 /* Skip to the newest entries */
226 if (Fflag && loops == 0)
227 loops++;
229 if (loops) {
230 size_t loop_bytes;
232 loop_bytes = sizeof(kcollect_t) *
233 (4 + SLEEP_INTERVAL / KCOLLECT_INTERVAL);
234 if (bytes > loop_bytes)
235 bytes = loop_bytes;
239 * If we got specified a file to load from: replace the data
240 * array and counter
242 if (fromFile) {
243 kcollect_t *dbmAry = NULL;
245 load_dbm(dbmFile, &dbmAry, &count);
246 ary = ary_base = dbmAry;
247 } else {
248 kcollect_t scaleid[2];
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);
260 scaleid[0] = ary[0];
261 scaleid[1] = ary[1];
262 count -= 2;
263 ary = load_kernel(scaleid, ary + 2, &count);
265 if (fields)
266 adjust_fields(&ary[1], fields);
270 * Delete duplicate entries when looping
272 if (loops) {
273 while (count > DATA_BASE_INDEX) {
274 if ((int)(ary[count-1].ticks - last_ticks) > 0)
275 break;
276 --count;
281 * Delete any entries beyond the time limit
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;
290 --count;
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));
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);
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;
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;
350 last_ticks = ary[DATA_BASE_INDEX].ticks;
351 if (count >= DATA_BASE_INDEX)
352 total_count += count - DATA_BASE_INDEX;
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.
359 if (cmd != 'g' && cmd != 'x' && cmd != 'w')
360 ++loops;
362 free(ary_base);
363 } while (keepalive);
365 if (cmd == 'x')
366 pclose(OutFP);
369 static
370 void
371 format_output(uintmax_t value,char fmt,uintmax_t scale, char* ret)
373 char buf[9];
375 switch(fmt) {
376 case '2':
378 * fractional x100
380 sprintf(ret, "%5ju.%02ju",
381 value / 100, value % 100);
382 break;
383 case 'p':
385 * Percentage fractional x100 (100% = 10000)
387 sprintf(ret,"%4ju.%02ju%%",
388 value / 100, value % 100);
389 break;
390 case 'm':
392 * Megabytes
394 humanize_number(buf, sizeof(buf), value, "",
396 HN_FRACTIONAL |
397 HN_NOSPACE);
398 sprintf(ret,"%8.8s", buf);
399 break;
400 case 'c':
402 * Raw count over period (this is not total)
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':
413 * Total bytes (this is a total), output
414 * in megabytes.
416 if (scale > 100000000) {
417 humanize_number(buf, sizeof(buf),
418 value, "",
420 HN_FRACTIONAL |
421 HN_NOSPACE);
422 } else {
423 humanize_number(buf, sizeof(buf),
424 value, "",
426 HN_FRACTIONAL |
427 HN_NOSPACE);
429 sprintf(ret,"%8.8s", buf);
430 break;
431 default:
432 sprintf(ret,"%s"," ");
433 break;
437 static
438 const char *
439 get_influx_series(const char *val)
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";
447 /* load value (load) */
448 if (strncmp("load", val, 8) == 0)
449 return "load_value";
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";
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";
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";
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";
479 return "misc_value";
483 static
484 void
485 dump_influxdb(kcollect_t *ary, size_t count, size_t total_count,
486 __unused const char* display_fmt)
488 int j;
489 int i;
490 uintmax_t value;
491 size_t ts_nsec;
492 char hostname[HOST_NAME_MAX];
493 char *colname;
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);
504 for (i = count - 1; i >= DATA_BASE_INDEX; --i) {
506 * Timestamp
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);
515 for (j = 0; j < KCOLLECT_ENTRIES; ++j) {
516 if (ary[1].data[j] == 0)
517 continue;
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.
524 * But should we since we're using raw values?
526 value = ary[i].data[j];
527 colname = (char *)&ary[1].data[j];
529 printf("%s,host=%s,type=%.8s value=%jd %jd\n",
530 get_influx_series(colname),
531 hostname, colname, value, ts_nsec);
533 printf("\n");
534 ++total_count;
539 static
540 void
541 dump_text(kcollect_t *ary, size_t count, size_t total_count,
542 const char* display_fmt)
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;
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");
560 for (j = 0; j < KCOLLECT_ENTRIES; ++j) {
561 if (ary[1].data[j]) {
562 printf(" %8.8s",
563 (char *)&ary[1].data[j]);
566 printf("\n");
570 * Timestamp
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);
580 for (j = 0; j < KCOLLECT_ENTRIES; ++j) {
581 if (ary[1].data[j] == 0)
582 continue;
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.
589 * Example scale - kernel provides total amount
590 * of memory available for memory related
591 * statistics in the scale field.
593 value = ary[i].data[j];
594 scale = KCOLLECT_GETSCALE(ary[0].data[j]);
595 fmt = KCOLLECT_GETFMT(ary[0].data[j]);
597 printf(" ");
599 format_output(value, fmt, scale, sbuf);
600 printf("%s",sbuf);
603 printf("\n");
604 ++total_count;
608 /* restores the DBM database header records to current machine */
609 static
610 void
611 restore_headers(kcollect_t *ary, const char *datafile)
613 DBM *db;
614 char hdr[32];
615 datum key, value;
616 int i;
618 db = dbm_open(datafile, (O_RDWR),
619 (S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP));
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);
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);
653 dbm_close(db);
658 * Store the array of kcollect_t records in a dbm db database,
659 * path passed in datafile
661 static
662 void
663 dump_dbm(kcollect_t *ary, size_t count, const char *datafile)
665 DBM * db;
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);
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];
694 for (i = 0; i < count; ++i) {
696 * The first DATA_BASE_INDEX records are special.
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;
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",
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);
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);
740 dbm_close(db);
745 * Transform a string (str) matching a format string (fmt) into a unix
746 * timestamp and return it used by load_dbm()
748 static
750 str2unix(const char* str, const char* fmt){
751 struct tm tm;
752 time_t ts;
755 * Reset all the fields because strptime only sets what it
756 * finds, which may lead to undefined members
758 memset(&tm, 0, sizeof(struct tm));
759 strptime(str, fmt, &tm);
760 ts = timegm(&tm);
762 return (int)ts;
766 * Sorts the ckollect_t records by time, to put youngest first,
767 * so desc by timestamp used by load_dbm()
769 static
771 rec_comparator(const void *c1, const void *c2)
773 const kcollect_t *k1 = (const kcollect_t*)c1;
774 const kcollect_t *k2 = (const kcollect_t*)c2;
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;
784 * Normalizes kcollect records from the kernel. We reserve the first
785 * DATA_BASE_INDEX elements for specific headers.
787 static
788 kcollect_t *
789 load_kernel(kcollect_t *scaleid, kcollect_t *ary, size_t *counter)
791 ary -= DATA_BASE_INDEX;
792 bzero(ary, sizeof(*ary) * DATA_BASE_INDEX);
793 ary[0] = scaleid[0];
794 ary[1] = scaleid[1];
797 * Add host field
799 gethostname((char *)ary[2].data,
800 sizeof(uint64_t) * KCOLLECT_ENTRIES - 1);
802 *counter += DATA_BASE_INDEX;
804 return ary;
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
811 static
812 void
813 load_dbm(const char* datafile, kcollect_t **ret_ary,
814 size_t *counter)
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;
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);
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++;
843 /* with the count allocate enough memory */
844 if (*ret_ary)
845 free(*ret_ary);
847 *ret_ary = malloc(sizeof(kcollect_t) * recCounter);
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);
856 bzero(*ret_ary, sizeof(kcollect_t) * recCounter);
859 * Actual data retrieval but only of recCounter
860 * records
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;
870 if (!strncmp(key.dptr, HDR_BASE, HDR_STRLEN)) {
872 * Ignore unsupported header indices
874 sc = strtoul((char *)key.dptr +
875 HDR_STRLEN, NULL, 10);
876 if (sc >= DATA_BASE_INDEX) {
877 key = dbm_nextkey(db);
878 continue;
880 headersFound |= 1 << sc;
881 } else {
882 sc = c++;
883 (*ret_ary)[sc].realtime.tv_sec =
884 str2unix(key.dptr,
885 DISPLAY_FULL_DATE);
887 memcpy((*ret_ary)[sc].data,
888 value.dptr,
889 sizeof(uint64_t) * KCOLLECT_ENTRIES);
891 key = dbm_nextkey(db);
895 * HEADER2 - hostname (must match)
897 if ((headersFound & 4) && *(char *)(*ret_ary)[2].data == 0)
898 headersFound &= ~4;
900 bzero(hostname, sizeof(hostname));
901 gethostname(hostname, sizeof(hostname) - 1);
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 */
911 * Set the counter,
912 * and sort the non-header records.
914 *counter = recCounter;
915 qsort(&(*ret_ary)[DATA_BASE_INDEX], recCounter - DATA_BASE_INDEX,
916 sizeof(kcollect_t), rec_comparator);
917 dbm_close(db);
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);
925 LoadedFromDB = 1;
928 static void
929 dump_fields(kcollect_t *ary)
931 int j;
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]));
942 static void
943 adjust_fields(kcollect_t *ent, const char *fields)
945 char *copy = strdup(fields);
946 char *word;
947 int selected[KCOLLECT_ENTRIES];
948 int j;
950 bzero(selected, sizeof(selected));
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;
960 word = strtok(NULL, ", \t");
962 free(copy);
963 for (j = 0; j < KCOLLECT_ENTRIES; ++j) {
964 if (!selected[j])
965 ent->data[j] = 0;