14 #define HASH_TABLE_SIZE 128
16 static time_t first_time
, last_time
; /* first and last timestamp to show */
17 static time_t ltime
; /* the timestamp from the current log-line */
19 static const char *image_url
= "/ninja/application/views/themes/default/icons/16x16";
20 static int reverse_parse_files
;
21 static uint skip
, limit
;
23 #define EVT_PROCESS (1 << 0)
24 #define EVT_NOTIFY (1 << 1)
25 #define EVT_ALERT (1 << 2)
26 #define EVT_COMMAND (1 << 3)
27 #define EVT_STATE (1 << 4)
28 #define EVT_FLAPPING (1 << 5)
29 #define EVT_DOWNTIME (1 << 6)
30 #define EVT_LROTATE (1 << 7)
31 #define EVT_EHANDLER (1 << 8)
32 #define EVT_START (1 << 9)
33 #define EVT_STOP (1 << 10)
35 #define EVT_HOST (1 << 20)
36 #define EVT_SERVICE (1 << 21)
37 #define EVT_INITIAL (1 << 22)
39 #define EVENT_MASK (0xffffffff & (~(EVT_HOST | EVT_SERVICE)))
40 static int event_filter
= EVENT_MASK
;
41 static int host_state_filter
= -1;
42 static int service_state_filter
= -1;
43 static int statetype_filter
= (1 << HARD_STATE
) | (1 << SOFT_STATE
);
45 #define add_event(string, eventcode) add_code(0, string, eventcode)
46 static struct string_code event_codes
[] = {
47 add_event("Error", EVT_PROCESS
),
48 add_event("Warning", EVT_PROCESS
),
49 add_event("HOST NOTIFICATION", EVT_NOTIFY
| EVT_SERVICE
),
50 add_event("HOST FLAPPING ALERT", EVT_FLAPPING
| EVT_HOST
),
51 add_event("SERVICE NOTIFICATION", EVT_NOTIFY
| EVT_SERVICE
),
52 add_event("SERVICE FLAPPING ALERT", EVT_FLAPPING
| EVT_SERVICE
),
53 add_event("LOG ROTATION", EVT_LROTATE
),
54 add_event("HOST EVENT HANDLER", EVT_EHANDLER
| EVT_HOST
),
55 add_event("SERVICE EVENT HANDLER", EVT_EHANDLER
| EVT_SERVICE
),
56 add_event("LOG VERSION", EVT_PROCESS
),
57 add_event("EXTERNAL COMMAND", EVT_COMMAND
),
59 add_code(5, "HOST ALERT", EVT_ALERT
| EVT_HOST
),
60 add_code(5, "INITIAL HOST STATE", EVT_STATE
| EVT_HOST
| EVT_INITIAL
),
61 add_code(5, "CURRENT HOST STATE", EVT_STATE
| EVT_HOST
| EVT_INITIAL
),
62 add_code(6, "SERVICE ALERT", EVT_ALERT
| EVT_SERVICE
),
63 add_code(6, "INITIAL SERVICE STATE", EVT_STATE
| EVT_SERVICE
| EVT_INITIAL
),
64 add_code(6, "CURRENT SERVICE STATE", EVT_STATE
| EVT_SERVICE
| EVT_INITIAL
),
65 add_code(3, "HOST DOWNTIME ALERT", EVT_DOWNTIME
| EVT_HOST
),
66 add_code(4, "SERVICE DOWNTIME ALERT", EVT_DOWNTIME
| EVT_SERVICE
),
70 static void print_time_iso8601(struct tm
*t
)
72 printf("[%d-%02d-%02d %02d:%02d:%02d] ",
73 t
->tm_year
+ 1900, t
->tm_mon
+ 1, t
->tm_mday
,
74 t
->tm_hour
, t
->tm_min
, t
->tm_sec
);
77 static void print_time_div_iso8601(struct tm
*t
)
79 printf("%d-%02d-%02d %02d:00 ",
80 t
->tm_year
+ 1900, t
->tm_mon
+ 1, t
->tm_mday
, t
->tm_hour
);
83 static void print_time_raw(struct tm
*t
)
85 printf("[%lu] ", ltime
);
90 void (*func
)(struct tm
*);
91 void (*func_div
)(struct tm
*);
92 } time_format_selections
[] = {
93 { "iso8601", print_time_iso8601
, print_time_div_iso8601
},
94 { "raw", print_time_raw
, NULL
},
97 static void (*print_time
)(struct tm
*) = print_time_iso8601
;
98 static void (*print_time_div
)(struct tm
*) = print_time_div_iso8601
;
100 static void parse_time_format(const char *selection
)
104 if (selection
) for (i
= 0; time_format_selections
[i
].name
; i
++) {
105 if (strcasecmp(selection
, time_format_selections
[i
].name
))
107 print_time
= time_format_selections
[i
].func
;
108 print_time_div
= time_format_selections
[i
].func_div
;
112 crash("Illegal timeformat selection: '%s'\n", selection
);
115 static inline void pre_print_mangle_line(struct tm
*t
, char *line
, uint len
)
119 for (i
= 0; i
< len
; i
++) {
130 static void print_line_ascii(int type
, struct tm
*t
, char *line
, uint len
)
137 static void print_line_ansi(int type
, struct tm
*t
, char *line
, uint len
)
139 const char *color
= NULL
;
142 case EVT_ALERT
| EVT_HOST
:
143 case EVT_STATE
| EVT_HOST
:
144 if (severity
== HOST_UP
)
150 case EVT_ALERT
| EVT_SERVICE
:
151 case EVT_STATE
| EVT_SERVICE
:
153 case SERVICE_OK
: color
= CLR_GREEN
; break;
154 case SERVICE_WARNING
: color
= CLR_YELLOW
; break;
155 case SERVICE_CRITICAL
: color
= CLR_RED
; break;
156 case SERVICE_UNKNOWN
: color
= CLR_BROWN
; break;
160 case EVT_DOWNTIME
| EVT_HOST
:
161 case EVT_DOWNTIME
| EVT_SERVICE
:
165 case EVT_FLAPPING
| EVT_HOST
:
166 case EVT_FLAPPING
| EVT_SERVICE
:
179 color
= CLR_BRIGHT_MAGENTA
;
182 case EVT_START
: case EVT_STOP
:
183 color
= CLR_BRIGHT_BLUE
;
190 printf("%s%s\n", line
, CLR_RESET
);
197 static void print_time_break(struct tm
*t
)
201 memcpy(&h
, t
, sizeof(h
));
202 h
.tm_min
= h
.tm_sec
= 0;
203 if (reverse_parse_files
) {
204 /* using mktime and gmtime_r again here means we never
205 * have to worry about changing date, month or year in
206 * case we overshoot by one */
207 time_t when
= mktime(&h
) + 3600;
217 static void print_line_html(int type
, struct tm
*t
, char *line
, uint len
)
219 const char *image
= NULL
;
220 static time_t last_time_break
= 0;
223 case EVT_ALERT
| EVT_HOST
:
224 case EVT_STATE
| EVT_HOST
:
225 if (severity
== HOST_UP
)
226 image
= "shield-ok.png";
228 image
= "shield-critical.png";
231 case EVT_ALERT
| EVT_SERVICE
:
232 case EVT_STATE
| EVT_SERVICE
:
234 case SERVICE_OK
: image
= "shield-ok.png"; break;
235 case SERVICE_WARNING
: image
= "shield-warning.png"; break;
236 case SERVICE_CRITICAL
: image
= "shield-critical.png"; break;
237 case SERVICE_UNKNOWN
: image
= "shield-unknown.png"; break;
241 case EVT_DOWNTIME
| EVT_HOST
:
242 case EVT_DOWNTIME
| EVT_SERVICE
:
243 image
= "downtime.png";
246 case EVT_FLAPPING
| EVT_HOST
:
247 case EVT_FLAPPING
| EVT_SERVICE
:
248 image
= "flapping.gif";
252 image
= "command.png";
256 image
= "logrotate.png";
259 case EVT_EHANDLER
| EVT_HOST
:
260 image
= "hostevent.gif";
263 case EVT_EHANDLER
| EVT_SERVICE
:
264 image
= "serviceevent.gif";
277 image
= "shield-info.png";
279 if (last_time_break
!= ltime
/ 3600) {
281 last_time_break
= ltime
/ 3600;
284 printf("<img src=\"%s/%s\" alt=\"%s\" /> ", image_url
, image
, image
);
286 printf("%s<br />\n", line
);
290 static void (*real_print_line
)(int type
, struct tm
*, char *, uint
) = print_line_ascii
;
291 static void print_line(int type
, char *line
, uint len
)
295 /* are we still skipping? If so, return early */
301 pre_print_mangle_line(&t
, line
, len
);
302 real_print_line(type
, &t
, line
, len
);
304 /* if we've printed all the lines we should, just exit */
312 static int parse_line(char *orig_line
, uint len
)
314 char *ptr
, *colon
, *line
;
316 struct string_code
*sc
;
320 /* ignore empty lines */
324 /* skip obviously bogus lines */
325 if (len
< 12 || *orig_line
!= '[') {
326 warn("line %d; len too short, or line doesn't start with '[' (%s)",
331 ltime
= strtoul(orig_line
+ 1, &ptr
, 10);
332 if (orig_line
+ 1 == ptr
) {
333 crash("Failed to parse log timestamp from '%s'. I can't handle malformed logdata",
338 /* only print lines in the interesting interval */
339 if ((first_time
&& ltime
< first_time
) || (last_time
&& ltime
> last_time
))
342 while (*ptr
== ']' || *ptr
== ' ')
346 len
-= line
- orig_line
;
348 if (!is_interesting(ptr
))
351 if (!(colon
= strchr(ptr
, ':'))) {
352 /* stupid heuristic, but might be good for something,
353 * somewhere, sometime. if nothing else, it should suppress
355 if (!(event_filter
& EVT_PROCESS
))
358 if (is_start_event(ptr
)) {
359 print_line(EVT_START
, line
, len
);
360 } else if (is_stop_event(ptr
)) {
361 print_line(EVT_STOP
, line
, len
);
367 if (!(sc
= get_event_type(ptr
, colon
- ptr
))) {
371 if (sc
->code
== IGNORE_LINE
)
373 if ((sc
->code
& event_filter
) != sc
->code
)
383 nvecs
= vectorize_string(ptr
, sc
->nvecs
);
385 if (nvecs
!= sc
->nvecs
) {
387 warn("Line %d in %s seems to not have all the fields it should",
388 line_no
, cur_file
->path
);
392 for (i
= 0; i
< sc
->nvecs
; i
++) {
394 /* this should never happen */
395 warn("Line %d in %s seems to be broken, or we failed to parse it into a vector",
396 line_no
, cur_file
->path
);
403 case EVT_ALERT
| EVT_HOST
:
404 case EVT_STATE
| EVT_HOST
:
405 if (!(statetype_filter
& (1 << soft_hard(strv
[2]))))
407 if (!(host_state_filter
& (1 << parse_host_state(strv
[1]))))
409 if (!is_interesting_host(strv
[0]))
412 severity
= parse_host_state(strv
[1]);
415 case EVT_ALERT
| EVT_SERVICE
:
416 case EVT_STATE
| EVT_SERVICE
:
417 if (!(statetype_filter
& (1 << soft_hard(strv
[3]))))
419 if (!(service_state_filter
& (1 << parse_service_state(strv
[2]))))
421 if (!is_interesting_service(strv
[0], strv
[1]))
424 severity
= parse_service_state(strv
[2]);
427 case EVT_FLAPPING
| EVT_HOST
:
428 case EVT_DOWNTIME
| EVT_HOST
:
429 if (!is_interesting_host(strv
[0]))
433 case EVT_FLAPPING
| EVT_SERVICE
:
434 case EVT_DOWNTIME
| EVT_SERVICE
:
435 if (!is_interesting_service(strv
[0], strv
[1]))
439 case EVT_NOTIFY
| EVT_HOST
:
440 if (!is_interesting_host(strv
[1]))
443 case EVT_NOTIFY
| EVT_SERVICE
:
444 if (!is_interesting_service(strv
[1], strv
[2]))
448 print_line(sc
->code
, line
, len
);
453 * hashes one line from an "interesting"-file. We use (void *)1
454 * to mark this as "present in hash-table" as we have no real
455 * data to lookup but still want hash_find{,2} to return non-NULL
456 * when it finds a match
458 static int hash_one_line(char *line
, uint len
)
460 return add_interesting_object(line
);
463 static int hash_interesting(const char *path
)
467 if (stat(path
, &st
) < 0)
468 crash("failed to stat %s: %s", path
, strerror(errno
));
470 lparse_path(path
, st
.st_size
, hash_one_line
);
475 static void parse_host_state_filter(char *p
)
477 host_state_filter
= 0;
481 host_state_filter
= -1;
484 host_state_filter
|= 1 << HOST_UNREACHABLE
;
487 host_state_filter
|= 1 << HOST_DOWN
;
490 host_state_filter
|= 1 << HOST_UP
;
496 static void parse_service_state_filter(char *p
)
498 service_state_filter
= 0;
502 service_state_filter
= -1;
505 service_state_filter
|= 1 << SERVICE_OK
;
508 service_state_filter
|= 1 << SERVICE_WARNING
;
511 service_state_filter
|= 1 << SERVICE_CRITICAL
;
514 service_state_filter
|= 1 << SERVICE_UNKNOWN
;
519 extern const char *__progname
;
520 static void usage(const char *fmt
, ...)
532 printf("usage: %s [options] <logfiles>\n\n", __progname
);
533 printf(" <logfiles> refers to all the nagios logfiles you want to search through\n\n");
534 printf("Options:\n");
535 printf(" --reverse parse (and print) logs in reverse\n");
536 printf(" --help this cruft\n");
537 printf(" --debug print debugging information\n");
538 printf(" --html print html output\n");
539 printf(" --ansi force-colorize the output\n");
540 printf(" --ascii don't colorize the output\n"),
541 printf(" --nagios-cfg=</path/to/nagios.cfg> path to nagios.cfg (required)\n");
542 printf(" --image-url=<image url> url to images. Implies --html\n");
543 printf(" --hide-flapping hide flapping messages\n");
544 printf(" --hide-downtime hide downtime messages\n");
545 printf(" --hide-process hide process messages\n");
546 printf(" --hide-command hide external command messages\n");
547 printf(" --hide-notifications hide notification messages\n");
548 printf(" --hide-logrotation hide log rotation messages\n");
549 printf(" --hide-initial hide INITIAL and CURRENT states\n");
550 printf(" --skip=<integer> number of filtered in messages to skip\n");
551 printf(" --limit=<integer> max number of messages to print\n");
552 printf(" --host=<host_name> show log entries for the named host\n");
553 printf(" --service=<hostname;servicedescription> show log entries for the named service\n");
554 printf(" --first=<timestamp> first log-entry to show\n");
555 printf(" --last=<timestamp> last log-entry to show\n");
556 printf(" --state-type=[hard|soft] state-types to show. default is all\n");
557 printf(" --host-states=[*ardu] host-states to show. can be mixed\n");
558 printf(" 'a' and '*' shows 'all'\n");
559 printf(" 'r' shows 'recovery'\n");
560 printf(" 'd' shows 'down'\n");
561 printf(" 'u' shows 'unreachable'\n");
562 printf(" --service-states=[*arwcu] service-states to show. can be mixed\n");
563 printf(" 'a' and '*' shows 'all'\n");
564 printf(" 'r' shows 'recovery'\n");
565 printf(" 'w' shows 'warning'\n");
566 printf(" 'c' shows 'critical'\n");
567 printf(" 'u' shows 'unknown'\n");
568 printf(" --time-format=[");
569 for (i
= 0; time_format_selections
[i
].name
; i
++) {
570 printf("%s", time_format_selections
[i
].name
);
571 if (time_format_selections
[i
+ 1].name
)
574 printf("] set timeformat for log-entries\n");
584 int main(int argc
, char **argv
)
587 const char *nagios_cfg
= NULL
;
589 strv
= calloc(sizeof(char *), MAX_NVECS
);
591 crash("Failed to alloc initial structs");
593 if (isatty(fileno(stdout
))) {
594 real_print_line
= print_line_ansi
;
595 event_filter
&= ~(EVT_LROTATE
| EVT_PROCESS
);
598 for (i
= 1; i
< argc
; i
++) {
599 char *opt
, *arg
= argv
[i
];
602 if ((opt
= strchr(arg
, '='))) {
606 else if (i
< argc
- 1) {
610 if (!strcmp(arg
, "--reverse")) {
611 reverse_parse_files
= 1;
614 if (!strcmp(arg
, "--html")) {
615 real_print_line
= print_line_html
;
618 if (!strcmp(arg
, "--ansi")) {
619 real_print_line
= print_line_ansi
;
622 if (!strcmp(arg
, "--ascii")) {
623 real_print_line
= print_line_ascii
;
626 if (!strcmp(arg
, "--debug") || !strcmp(arg
, "-d")) {
630 if (!strcmp(arg
, "--help")) {
634 if (!prefixcmp(arg
, "--hide-flapping")) {
635 event_filter
&= ~EVT_FLAPPING
;
638 if (!prefixcmp(arg
, "--hide-downtime")) {
639 event_filter
&= ~EVT_DOWNTIME
;
642 if (!prefixcmp(arg
, "--hide-process")) {
643 event_filter
&= ~EVT_PROCESS
;
646 if (!prefixcmp(arg
, "--hide-command")) {
647 event_filter
&= ~EVT_COMMAND
;
650 if (!prefixcmp(arg
, "--hide-notification")) {
651 event_filter
&= ~EVT_NOTIFY
;
654 if (!prefixcmp(arg
, "--hide-logrotat")) {
655 event_filter
&= ~EVT_LROTATE
;
658 if (!prefixcmp(arg
, "--hide-initial")) {
659 event_filter
&= ~EVT_INITIAL
;
663 if (!prefixcmp(arg
, "--")) {
665 usage("Option '%s' requires an argument\n", arg
);
670 /* options parsed below require arguments */
671 if (!strcmp(arg
, "--nagios-cfg")) {
675 if (!strcmp(arg
, "--skip")) {
676 skip
= strtoul(opt
, NULL
, 0);
679 if (!strcmp(arg
, "--limit")) {
680 limit
= strtoul(opt
, NULL
, 0);
683 if (!strcmp(arg
, "--host")) {
684 event_filter
|= EVT_HOST
;
685 add_interesting_object(opt
);
688 if (!strcmp(arg
, "--service")) {
689 event_filter
|= EVT_SERVICE
;
690 add_interesting_object(opt
);
693 if (!strcmp(arg
, "--image-url")) {
694 real_print_line
= print_line_html
;
698 if (!strcmp(arg
, "--interesting") || !strcmp(arg
, "-i")) {
700 usage("%s requires a filename as argument", arg
);
701 hash_interesting(opt
);
704 if (!strcmp(arg
, "--first") || !strcmp(arg
, "--last")) {
708 crash("%s requires a timestamp as argument", arg
);
709 when
= strtoul(opt
, NULL
, 0);
712 if (!strcmp(arg
, "--first"))
718 if (!strcmp(arg
, "--state-type")) {
719 if (!strcasecmp(opt
, "hard"))
720 statetype_filter
= (1 << HARD_STATE
);
721 if (!strcasecmp(opt
, "soft"))
722 statetype_filter
= (1 << SOFT_STATE
);
725 if (!strcmp(arg
, "--host-states")) {
726 event_filter
|= EVT_HOST
;
727 parse_host_state_filter(opt
);
730 if (!strcmp(arg
, "--service-states")) {
731 event_filter
|= EVT_SERVICE
;
732 parse_service_state_filter(opt
);
735 if (!strcmp(arg
, "--time-format")) {
736 parse_time_format(opt
);
740 /* non-argument, so treat as either nagios.cfg or a logfile */
741 if (!strcmp(&arg
[strlen(arg
) - 11], "/nagios.cfg")) {
744 add_naglog_path(arg
);
749 print_interesting_objects();
751 /* fallback for op5 systems */
752 if (!nagios_cfg
&& !num_nfile
) {
753 nagios_cfg
= "/opt/monitor/etc/nagios.cfg";
756 struct cfg_comp
*conf
;
758 conf
= cfg_parse_file(nagios_cfg
);
760 usage("Failed to parse nagios' main config file '%s'\n", nagios_cfg
);
761 for (i
= 0; i
< conf
->vars
; i
++) {
762 struct cfg_var
*v
= conf
->vlist
[i
];
763 if (!strcmp(v
->key
, "log_file")) {
764 add_naglog_path(v
->value
);
766 if (!strcmp(v
->key
, "log_archive_path")) {
767 add_naglog_path(v
->value
);
775 if (reverse_parse_files
)
776 qsort(nfile
, num_nfile
, sizeof(*nfile
), nfile_rev_cmp
);
778 qsort(nfile
, num_nfile
, sizeof(*nfile
), nfile_cmp
);
780 for (i
= 0; i
< num_nfile
; i
++) {
781 struct naglog_file
*nf
= &nfile
[i
];
782 if (last_time
&& nf
->first
> last_time
) {
783 debug("ignoring %s\n", nf
->path
);
786 if (first_time
&& i
< num_nfile
- 1 && nfile
[i
+ 1].first
< first_time
) {
787 debug("ignoring %s\n", nf
->path
);
792 debug("importing from %s (%lu : %u)\n", nf
->path
, nf
->first
, nf
->cmp
);
794 lparse_path_real(reverse_parse_files
, nf
->path
, nf
->size
, parse_line
);
797 if (warnings
&& debug_level
)
798 fprintf(stderr
, "Total warnings: %d\n", warnings
);
800 print_unhandled_events();