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
;
141 switch (type
& ~EVT_INITIAL
) {
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
);
198 static void print_time_break(struct tm
*t
)
202 memcpy(&h
, t
, sizeof(h
));
203 h
.tm_min
= h
.tm_sec
= 0;
204 if (reverse_parse_files
) {
205 /* using mktime and gmtime_r again here means we never
206 * have to worry about changing date, month or year in
207 * case we overshoot by one */
208 time_t when
= mktime(&h
) + 3600;
218 static void print_line_html(int type
, struct tm
*t
, char *line
, uint len
)
220 const char *image
= NULL
;
221 static time_t last_time_break
= 0;
224 case EVT_ALERT
| EVT_HOST
:
225 case EVT_STATE
| EVT_HOST
:
226 if (severity
== HOST_UP
)
227 image
= "shield-ok.png";
229 image
= "shield-critical.png";
232 case EVT_ALERT
| EVT_SERVICE
:
233 case EVT_STATE
| EVT_SERVICE
:
235 case SERVICE_OK
: image
= "shield-ok.png"; break;
236 case SERVICE_WARNING
: image
= "shield-warning.png"; break;
237 case SERVICE_CRITICAL
: image
= "shield-critical.png"; break;
238 case SERVICE_UNKNOWN
: image
= "shield-unknown.png"; break;
242 case EVT_DOWNTIME
| EVT_HOST
:
243 case EVT_DOWNTIME
| EVT_SERVICE
:
244 image
= "downtime.png";
247 case EVT_FLAPPING
| EVT_HOST
:
248 case EVT_FLAPPING
| EVT_SERVICE
:
249 image
= "flapping.gif";
253 image
= "command.png";
257 image
= "logrotate.png";
260 case EVT_EHANDLER
| EVT_HOST
:
261 image
= "hostevent.gif";
264 case EVT_EHANDLER
| EVT_SERVICE
:
265 image
= "serviceevent.gif";
278 image
= "shield-info.png";
280 if (last_time_break
!= ltime
/ 3600) {
282 last_time_break
= ltime
/ 3600;
285 printf("<img src=\"%s/%s\" alt=\"%s\" /> ", image_url
, image
, image
);
287 printf("%s<br />\n", line
);
291 static void (*real_print_line
)(int type
, struct tm
*, char *, uint
) = print_line_ascii
;
292 static void print_line(int type
, char *line
, uint len
)
296 /* are we still skipping? If so, return early */
302 pre_print_mangle_line(&t
, line
, len
);
303 real_print_line(type
, &t
, line
, len
);
305 /* if we've printed all the lines we should, just exit */
313 static int parse_line(char *orig_line
, uint len
)
315 char *ptr
, *colon
, *line
;
317 struct string_code
*sc
;
321 /* ignore empty lines */
325 /* skip obviously bogus lines */
326 if (len
< 12 || *orig_line
!= '[') {
327 warn("line %d; len too short, or line doesn't start with '[' (%s)",
332 ltime
= strtoul(orig_line
+ 1, &ptr
, 10);
333 if (orig_line
+ 1 == ptr
) {
334 crash("Failed to parse log timestamp from '%s'. I can't handle malformed logdata",
339 /* only print lines in the interesting interval */
340 if ((first_time
&& ltime
< first_time
) || (last_time
&& ltime
> last_time
))
343 while (*ptr
== ']' || *ptr
== ' ')
347 len
-= line
- orig_line
;
349 if (!is_interesting(ptr
))
352 if (!(colon
= strchr(ptr
, ':'))) {
353 /* stupid heuristic, but might be good for something,
354 * somewhere, sometime. if nothing else, it should suppress
356 if (!(event_filter
& EVT_PROCESS
))
359 if (is_start_event(ptr
)) {
360 print_line(EVT_START
, line
, len
);
361 } else if (is_stop_event(ptr
)) {
362 print_line(EVT_STOP
, line
, len
);
368 if (!(sc
= get_event_type(ptr
, colon
- ptr
))) {
372 if (sc
->code
== IGNORE_LINE
)
374 if ((sc
->code
& event_filter
) != sc
->code
)
384 nvecs
= vectorize_string(ptr
, sc
->nvecs
);
386 if (nvecs
!= sc
->nvecs
) {
388 warn("Line %d in %s seems to not have all the fields it should",
389 line_no
, cur_file
->path
);
393 for (i
= 0; i
< sc
->nvecs
; i
++) {
395 /* this should never happen */
396 warn("Line %d in %s seems to be broken, or we failed to parse it into a vector",
397 line_no
, cur_file
->path
);
404 case EVT_ALERT
| EVT_HOST
:
405 case EVT_STATE
| EVT_HOST
:
406 if (!(statetype_filter
& (1 << soft_hard(strv
[2]))))
408 if (!(host_state_filter
& (1 << parse_host_state(strv
[1]))))
410 if (!auth_host_ok(strv
[0]))
412 if (!is_interesting_host(strv
[0]))
415 severity
= parse_host_state(strv
[1]);
418 case EVT_ALERT
| EVT_SERVICE
:
419 case EVT_STATE
| EVT_SERVICE
:
420 if (!(statetype_filter
& (1 << soft_hard(strv
[3]))))
422 if (!(service_state_filter
& (1 << parse_service_state(strv
[2]))))
424 if (!auth_service_ok(strv
[0], strv
[1]))
426 if (!is_interesting_service(strv
[0], strv
[1]))
429 severity
= parse_service_state(strv
[2]);
432 case EVT_FLAPPING
| EVT_HOST
:
433 case EVT_DOWNTIME
| EVT_HOST
:
434 if (!auth_host_ok(strv
[0]))
436 if (!is_interesting_host(strv
[0]))
440 case EVT_FLAPPING
| EVT_SERVICE
:
441 case EVT_DOWNTIME
| EVT_SERVICE
:
442 if (!auth_service_ok(strv
[0], strv
[1]))
444 if (!is_interesting_service(strv
[0], strv
[1]))
448 case EVT_NOTIFY
| EVT_HOST
:
449 if (!auth_host_ok(strv
[1]))
451 if (!is_interesting_host(strv
[1]))
454 case EVT_NOTIFY
| EVT_SERVICE
:
455 if (!auth_service_ok(strv
[1], strv
[2]))
457 if (!is_interesting_service(strv
[1], strv
[2]))
461 print_line(sc
->code
, line
, len
);
466 * hashes one line from an "interesting"-file. We use (void *)1
467 * to mark this as "present in hash-table" as we have no real
468 * data to lookup but still want hash_find{,2} to return non-NULL
469 * when it finds a match
471 static int hash_one_line(char *line
, uint len
)
473 return add_interesting_object(line
);
476 static int hash_interesting(const char *path
)
480 if (stat(path
, &st
) < 0)
481 crash("failed to stat %s: %s", path
, strerror(errno
));
483 lparse_path(path
, st
.st_size
, hash_one_line
);
488 static void parse_host_state_filter(char *p
)
490 host_state_filter
= 0;
494 host_state_filter
= -1;
497 host_state_filter
|= 1 << HOST_UNREACHABLE
;
500 host_state_filter
|= 1 << HOST_DOWN
;
503 host_state_filter
|= 1 << HOST_UP
;
509 static void parse_service_state_filter(char *p
)
511 service_state_filter
= 0;
515 service_state_filter
= -1;
518 service_state_filter
|= 1 << SERVICE_OK
;
521 service_state_filter
|= 1 << SERVICE_WARNING
;
524 service_state_filter
|= 1 << SERVICE_CRITICAL
;
527 service_state_filter
|= 1 << SERVICE_UNKNOWN
;
532 extern const char *__progname
;
533 static void usage(const char *fmt
, ...)
545 printf("usage: %s [options] [logfiles]\n\n", __progname
);
546 printf(" <logfiles> refers to all the nagios logfiles you want to search through\n");
547 printf(" If --nagios-cfg is given or can be inferred no logfiles need to be supplied\n");
548 printf("Options:\n");
549 printf(" --reverse parse (and print) logs in reverse\n");
550 printf(" --help this cruft\n");
551 printf(" --debug print debugging information\n");
552 printf(" --html print html output\n");
553 printf(" --ansi force-colorize the output\n");
554 printf(" --ascii don't colorize the output\n"),
555 printf(" --user=<username> show only logs this user can see\n");
556 printf(" --cgi-cfg=</path/to/cgi.cfg> path to cgi.cfg\n");
557 printf(" --nagios-cfg=</path/to/nagios.cfg> path to nagios.cfg\n");
558 printf(" --object-cache=</path/to/objects.cache> path to objects.cache\n");
559 printf(" --image-url=<image url> url to images. Implies --html\n");
560 printf(" --hide-flapping hide flapping messages\n");
561 printf(" --hide-downtime hide downtime messages\n");
562 printf(" --hide-process hide process messages\n");
563 printf(" --hide-command hide external command messages\n");
564 printf(" --hide-notifications hide notification messages\n");
565 printf(" --hide-logrotation hide log rotation messages\n");
566 printf(" --hide-initial hide INITIAL and CURRENT states\n");
567 printf(" --skip=<integer> number of filtered in messages to skip\n");
568 printf(" --limit=<integer> max number of messages to print\n");
569 printf(" --host=<host_name> show log entries for the named host\n");
570 printf(" --service=<hostname;servicedescription> show log entries for the named service\n");
571 printf(" --first=<timestamp> first log-entry to show\n");
572 printf(" --last=<timestamp> last log-entry to show\n");
573 printf(" --state-type=[hard|soft] state-types to show. default is all\n");
574 printf(" --host-states=[*ardu] host-states to show. can be mixed\n");
575 printf(" 'a' and '*' shows 'all'\n");
576 printf(" 'r' shows 'recovery'\n");
577 printf(" 'd' shows 'down'\n");
578 printf(" 'u' shows 'unreachable'\n");
579 printf(" --service-states=[*arwcu] service-states to show. can be mixed\n");
580 printf(" 'a' and '*' shows 'all'\n");
581 printf(" 'r' shows 'recovery'\n");
582 printf(" 'w' shows 'warning'\n");
583 printf(" 'c' shows 'critical'\n");
584 printf(" 'u' shows 'unknown'\n");
585 printf(" --time-format=[");
586 for (i
= 0; time_format_selections
[i
].name
; i
++) {
587 printf("%s", time_format_selections
[i
].name
);
588 if (time_format_selections
[i
+ 1].name
)
591 printf("] set timeformat for log-entries\n");
601 int main(int argc
, char **argv
)
604 const char *nagios_cfg
= NULL
, *cgi_cfg
= NULL
, *object_cache
= NULL
;
606 strv
= calloc(sizeof(char *), MAX_NVECS
);
608 crash("Failed to alloc initial structs");
610 if (isatty(fileno(stdout
))) {
611 real_print_line
= print_line_ansi
;
612 event_filter
&= ~(EVT_LROTATE
| EVT_PROCESS
);
615 for (i
= 1; i
< argc
; i
++) {
616 char *opt
, *arg
= argv
[i
];
617 int arg_len
, eq_opt
= 0;
619 if ((opt
= strchr(arg
, '='))) {
623 else if (i
< argc
- 1) {
627 if (!strcmp(arg
, "--reverse")) {
628 reverse_parse_files
= 1;
631 if (!strcmp(arg
, "--html")) {
632 real_print_line
= print_line_html
;
635 if (!strcmp(arg
, "--ansi")) {
636 real_print_line
= print_line_ansi
;
639 if (!strcmp(arg
, "--ascii")) {
640 real_print_line
= print_line_ascii
;
643 if (!strcmp(arg
, "--debug") || !strcmp(arg
, "-d")) {
647 if (!strcmp(arg
, "--help")) {
651 if (!prefixcmp(arg
, "--hide-flapping")) {
652 event_filter
&= ~EVT_FLAPPING
;
655 if (!prefixcmp(arg
, "--hide-downtime")) {
656 event_filter
&= ~EVT_DOWNTIME
;
659 if (!prefixcmp(arg
, "--hide-process")) {
660 event_filter
&= ~EVT_PROCESS
;
663 if (!prefixcmp(arg
, "--hide-command")) {
664 event_filter
&= ~EVT_COMMAND
;
667 if (!prefixcmp(arg
, "--hide-notification")) {
668 event_filter
&= ~EVT_NOTIFY
;
671 if (!prefixcmp(arg
, "--hide-logrotat")) {
672 event_filter
&= ~EVT_LROTATE
;
675 if (!prefixcmp(arg
, "--hide-initial")) {
676 event_filter
&= ~EVT_INITIAL
;
680 if (!prefixcmp(arg
, "--")) {
682 usage("Option '%s' requires an argument\n", arg
);
687 /* options parsed below require arguments */
688 if (!strcmp(arg
, "--user")) {
692 if (!prefixcmp(arg
, "--object-cache")) {
696 if (!strcmp(arg
, "--nagios-cfg")) {
700 if (!strcmp(arg
, "--cgi-cfg")) {
704 if (!strcmp(arg
, "--skip")) {
705 skip
= strtoul(opt
, NULL
, 0);
708 if (!strcmp(arg
, "--limit")) {
709 limit
= strtoul(opt
, NULL
, 0);
712 if (!strcmp(arg
, "--host")) {
713 event_filter
|= EVT_HOST
;
714 add_interesting_object(opt
);
717 if (!strcmp(arg
, "--service")) {
718 event_filter
|= EVT_SERVICE
;
719 add_interesting_object(opt
);
722 if (!strcmp(arg
, "--image-url")) {
723 real_print_line
= print_line_html
;
727 if (!strcmp(arg
, "--interesting") || !strcmp(arg
, "-i")) {
729 usage("%s requires a filename as argument", arg
);
730 hash_interesting(opt
);
733 if (!strcmp(arg
, "--first") || !strcmp(arg
, "--last")) {
737 crash("%s requires a timestamp as argument", arg
);
738 when
= strtoul(opt
, NULL
, 0);
741 if (!strcmp(arg
, "--first"))
747 if (!strcmp(arg
, "--state-type")) {
748 if (!strcasecmp(opt
, "hard"))
749 statetype_filter
= (1 << HARD_STATE
);
750 if (!strcasecmp(opt
, "soft"))
751 statetype_filter
= (1 << SOFT_STATE
);
754 if (!strcmp(arg
, "--host-states")) {
755 event_filter
|= EVT_HOST
;
756 parse_host_state_filter(opt
);
759 if (!strcmp(arg
, "--service-states")) {
760 event_filter
|= EVT_SERVICE
;
761 parse_service_state_filter(opt
);
764 if (!strcmp(arg
, "--time-format")) {
765 parse_time_format(opt
);
769 /* non-argument, so treat as config- or log-file */
770 arg_len
= strlen(arg
);
771 if (arg_len
> 7 && !strcmp(&arg
[arg_len
- 7], "cgi.cfg")) {
773 } else if (!strcmp(&arg
[strlen(arg
) - 10], "nagios.cfg")) {
776 add_naglog_path(arg
);
781 print_interesting_objects();
783 /* fallback for op5 systems */
784 if (auth_get_user() || (!nagios_cfg
&& !num_nfile
)) {
785 struct cfg_comp
*conf
;
787 conf
= cfg_parse_file(cgi_cfg
? cgi_cfg
: "/opt/monitor/etc/cgi.cfg");
790 for (i
= 0; i
< conf
->vars
; i
++) {
791 struct cfg_var
*v
= conf
->vlist
[i
];
792 if (!nagios_cfg
&& !strcmp(v
->key
, "main_config_file")) {
793 nagios_cfg
= strdup(v
->value
);
795 if (!prefixcmp(v
->key
, "authorized_for_")) {
796 auth_parse_permission(v
->key
, v
->value
);
799 cfg_destroy_compound(conf
);
802 crash("Failed to parse cgi.cfg file '%s'\n", cgi_cfg
);
804 if (auth_get_user()) {
805 crash("--user given, but no suitable cgi.cfg file found\n");
810 if (!nagios_cfg
&& !num_nfile
) {
811 nagios_cfg
= "/opt/monitor/etc/nagios.cfg";
814 struct cfg_comp
*conf
;
816 conf
= cfg_parse_file(nagios_cfg
);
818 usage("Failed to parse nagios' main config file '%s'\n", nagios_cfg
);
819 for (i
= 0; i
< conf
->vars
; i
++) {
820 struct cfg_var
*v
= conf
->vlist
[i
];
821 if (!strcmp(v
->key
, "log_file")) {
822 add_naglog_path(v
->value
);
824 if (!strcmp(v
->key
, "log_archive_path")) {
825 add_naglog_path(v
->value
);
827 if (!object_cache
&& !strcmp(v
->key
, "object_cache_file")) {
828 object_cache
= v
->value
;
836 if (auth_get_user() && object_cache
) {
837 auth_init(object_cache
);
840 if (reverse_parse_files
)
841 qsort(nfile
, num_nfile
, sizeof(*nfile
), nfile_rev_cmp
);
843 qsort(nfile
, num_nfile
, sizeof(*nfile
), nfile_cmp
);
845 for (i
= 0; i
< num_nfile
; i
++) {
846 struct naglog_file
*nf
= &nfile
[i
];
847 if (last_time
&& nf
->first
> last_time
) {
848 debug("last_time ignoring %s\n", nf
->path
);
851 if (first_time
&& i
< num_nfile
- 1 && nfile
[i
+ 1].first
< first_time
) {
852 debug("first_time ignoring %s\n", nf
->path
);
857 debug("importing from %s (%lu : %u)\n", nf
->path
, nf
->first
, nf
->cmp
);
859 lparse_path_real(reverse_parse_files
, nf
->path
, nf
->size
, parse_line
);
862 if (warnings
&& debug_level
)
863 fprintf(stderr
, "Total warnings: %d\n", warnings
);
865 print_unhandled_events();