15 #define HASH_TABLE_SIZE 128
17 static time_t first_time
, last_time
; /* first and last timestamp to show */
18 static time_t ltime
; /* the timestamp from the current log-line */
20 static const char *image_url
= "/ninja/application/views/themes/default/icons/16x16";
21 static int reverse_parse_files
;
22 static uint skip
, limit
;
23 static int hide_state_dupes
; /* if set, we hide duplicate state messages */
25 #define EVT_PROCESS (1 << 0)
26 #define EVT_NOTIFY (1 << 1)
27 #define EVT_ALERT (1 << 2)
28 #define EVT_COMMAND (1 << 3)
29 #define EVT_STATE (1 << 4)
30 #define EVT_FLAPPING (1 << 5)
31 #define EVT_DOWNTIME (1 << 6)
32 #define EVT_LROTATE (1 << 7)
33 #define EVT_EHANDLER (1 << 8)
34 #define EVT_START (1 << 9)
35 #define EVT_STOP (1 << 10)
37 #define EVT_HOST (1 << 20)
38 #define EVT_SERVICE (1 << 21)
39 #define EVT_INITIAL (1 << 22)
41 #define EVENT_MASK (0xffffffff & (~(EVT_HOST | EVT_SERVICE)))
42 static int event_filter
= EVENT_MASK
;
43 static int host_state_filter
= -1;
44 static int service_state_filter
= -1;
45 static int statetype_filter
= (1 << HARD_STATE
) | (1 << SOFT_STATE
);
47 #define add_event(string, eventcode) add_code(0, string, eventcode)
48 static struct string_code event_codes
[] = {
49 add_event("Error", EVT_PROCESS
),
50 add_event("Warning", EVT_PROCESS
),
51 add_event("HOST NOTIFICATION", EVT_NOTIFY
| EVT_SERVICE
),
52 add_event("HOST FLAPPING ALERT", EVT_FLAPPING
| EVT_HOST
),
53 add_event("SERVICE NOTIFICATION", EVT_NOTIFY
| EVT_SERVICE
),
54 add_event("SERVICE FLAPPING ALERT", EVT_FLAPPING
| EVT_SERVICE
),
55 add_event("LOG ROTATION", EVT_LROTATE
),
56 add_event("HOST EVENT HANDLER", EVT_EHANDLER
| EVT_HOST
),
57 add_event("SERVICE EVENT HANDLER", EVT_EHANDLER
| EVT_SERVICE
),
58 add_event("LOG VERSION", EVT_PROCESS
),
59 add_event("EXTERNAL COMMAND", EVT_COMMAND
),
61 add_code(5, "HOST ALERT", EVT_ALERT
| EVT_HOST
),
62 add_code(5, "INITIAL HOST STATE", EVT_STATE
| EVT_HOST
| EVT_INITIAL
),
63 add_code(5, "CURRENT HOST STATE", EVT_STATE
| EVT_HOST
| EVT_INITIAL
),
64 add_code(6, "SERVICE ALERT", EVT_ALERT
| EVT_SERVICE
),
65 add_code(6, "INITIAL SERVICE STATE", EVT_STATE
| EVT_SERVICE
| EVT_INITIAL
),
66 add_code(6, "CURRENT SERVICE STATE", EVT_STATE
| EVT_SERVICE
| EVT_INITIAL
),
67 add_code(3, "HOST DOWNTIME ALERT", EVT_DOWNTIME
| EVT_HOST
),
68 add_code(4, "SERVICE DOWNTIME ALERT", EVT_DOWNTIME
| EVT_SERVICE
),
72 static void print_time_iso8601(struct tm
*t
)
74 printf("[%d-%02d-%02d %02d:%02d:%02d] ",
75 t
->tm_year
+ 1900, t
->tm_mon
+ 1, t
->tm_mday
,
76 t
->tm_hour
, t
->tm_min
, t
->tm_sec
);
79 static void print_time_div_iso8601(struct tm
*t
)
81 printf("%d-%02d-%02d %02d:00 ",
82 t
->tm_year
+ 1900, t
->tm_mon
+ 1, t
->tm_mday
, t
->tm_hour
);
85 static void print_time_raw(struct tm
*t
)
87 printf("[%lu] ", ltime
);
92 void (*func
)(struct tm
*);
93 void (*func_div
)(struct tm
*);
94 } time_format_selections
[] = {
95 { "iso8601", print_time_iso8601
, print_time_div_iso8601
},
96 { "raw", print_time_raw
, NULL
},
99 static void (*print_time
)(struct tm
*) = print_time_iso8601
;
100 static void (*print_time_div
)(struct tm
*) = print_time_div_iso8601
;
102 static void parse_time_format(const char *selection
)
106 if (selection
) for (i
= 0; time_format_selections
[i
].name
; i
++) {
107 if (strcasecmp(selection
, time_format_selections
[i
].name
))
109 print_time
= time_format_selections
[i
].func
;
110 print_time_div
= time_format_selections
[i
].func_div
;
114 crash("Illegal timeformat selection: '%s'\n", selection
);
117 static inline void pre_print_mangle_line(struct tm
*t
, char *line
, uint len
)
121 for (i
= 0; i
< len
; i
++) {
132 static void print_line_ascii(int type
, struct tm
*t
, char *line
, uint len
)
139 static void print_line_ansi(int type
, struct tm
*t
, char *line
, uint len
)
141 const char *color
= NULL
;
143 switch (type
& ~EVT_INITIAL
) {
144 case EVT_ALERT
| EVT_HOST
:
145 case EVT_STATE
| EVT_HOST
:
146 if (severity
== HOST_UP
)
152 case EVT_ALERT
| EVT_SERVICE
:
153 case EVT_STATE
| EVT_SERVICE
:
155 case SERVICE_OK
: color
= CLR_GREEN
; break;
156 case SERVICE_WARNING
: color
= CLR_YELLOW
; break;
157 case SERVICE_CRITICAL
: color
= CLR_RED
; break;
158 case SERVICE_UNKNOWN
: color
= CLR_BROWN
; break;
162 case EVT_DOWNTIME
| EVT_HOST
:
163 case EVT_DOWNTIME
| EVT_SERVICE
:
167 case EVT_FLAPPING
| EVT_HOST
:
168 case EVT_FLAPPING
| EVT_SERVICE
:
181 color
= CLR_BRIGHT_MAGENTA
;
184 case EVT_START
: case EVT_STOP
:
185 color
= CLR_BRIGHT_BLUE
;
192 printf("%s%s\n", line
, CLR_RESET
);
200 static void print_time_break(struct tm
*t
)
204 memcpy(&h
, t
, sizeof(h
));
205 h
.tm_min
= h
.tm_sec
= 0;
206 if (reverse_parse_files
) {
207 /* using mktime and gmtime_r again here means we never
208 * have to worry about changing date, month or year in
209 * case we overshoot by one */
210 time_t when
= mktime(&h
) + 3600;
220 static void print_line_html(int type
, struct tm
*t
, char *line
, uint len
)
222 const char *image
= NULL
;
223 static time_t last_time_break
= 0;
226 case EVT_ALERT
| EVT_HOST
:
227 case EVT_STATE
| EVT_HOST
:
228 if (severity
== HOST_UP
)
229 image
= "shield-ok.png";
231 image
= "shield-critical.png";
234 case EVT_ALERT
| EVT_SERVICE
:
235 case EVT_STATE
| EVT_SERVICE
:
237 case SERVICE_OK
: image
= "shield-ok.png"; break;
238 case SERVICE_WARNING
: image
= "shield-warning.png"; break;
239 case SERVICE_CRITICAL
: image
= "shield-critical.png"; break;
240 case SERVICE_UNKNOWN
: image
= "shield-unknown.png"; break;
244 case EVT_DOWNTIME
| EVT_HOST
:
245 case EVT_DOWNTIME
| EVT_SERVICE
:
246 image
= "scheduled-downtime.png";
249 case EVT_FLAPPING
| EVT_HOST
:
250 case EVT_FLAPPING
| EVT_SERVICE
:
251 image
= "flapping.gif";
255 image
= "command.png";
259 image
= "logrotate.png";
262 case EVT_EHANDLER
| EVT_HOST
:
263 image
= "hostevent.gif";
266 case EVT_EHANDLER
| EVT_SERVICE
:
267 image
= "serviceevent.gif";
280 image
= "shield-info.png";
282 if (last_time_break
!= ltime
/ 3600) {
284 last_time_break
= ltime
/ 3600;
287 printf("<img src=\"%s/%s\" alt=\"%s\" /> ", image_url
, image
, image
);
289 printf("%s<br />\n", line
);
293 static void (*real_print_line
)(int type
, struct tm
*, char *, uint
) = print_line_ascii
;
294 static void print_line(int type
, char *line
, uint len
)
298 /* are we still skipping? If so, return early */
304 pre_print_mangle_line(&t
, line
, len
);
305 real_print_line(type
, &t
, line
, len
);
307 /* if we've printed all the lines we should, just exit */
315 static int parse_line(char *orig_line
, uint len
)
317 char *ptr
, *colon
, *line
;
319 struct string_code
*sc
;
324 /* ignore empty lines */
328 /* skip obviously bogus lines */
329 if (len
< 12 || *orig_line
!= '[') {
330 warn("line %d; len too short, or line doesn't start with '[' (%s)",
335 ltime
= strtoul(orig_line
+ 1, &ptr
, 10);
336 if (orig_line
+ 1 == ptr
) {
337 crash("Failed to parse log timestamp from '%s'. I can't handle malformed logdata",
342 /* only print lines in the interesting interval */
343 if (ltime
< first_time
|| ltime
> last_time
)
346 while (*ptr
== ']' || *ptr
== ' ')
350 len
-= line
- orig_line
;
352 if (!is_interesting(ptr
))
355 if (!(colon
= strchr(ptr
, ':'))) {
356 /* stupid heuristic, but might be good for something,
357 * somewhere, sometime. if nothing else, it should suppress
359 if (!(event_filter
& EVT_PROCESS
))
362 if (is_start_event(ptr
)) {
363 print_line(EVT_START
, line
, len
);
364 } else if (is_stop_event(ptr
)) {
365 print_line(EVT_STOP
, line
, len
);
371 if (!(sc
= get_event_type(ptr
, colon
- ptr
))) {
375 if (sc
->code
== IGNORE_LINE
)
377 if ((sc
->code
& event_filter
) != sc
->code
)
387 nvecs
= vectorize_string(ptr
, sc
->nvecs
);
389 if (nvecs
!= sc
->nvecs
) {
391 warn("Line %d in %s seems to not have all the fields it should",
392 line_no
, cur_file
->path
);
396 for (i
= 0; i
< sc
->nvecs
; i
++) {
398 /* this should never happen */
399 warn("Line %d in %s seems to be broken, or we failed to parse it into a vector",
400 line_no
, cur_file
->path
);
406 switch (sc
->code
& ~EVT_INITIAL
) {
407 case EVT_ALERT
| EVT_HOST
:
408 case EVT_STATE
| EVT_HOST
:
409 hard
= soft_hard(strv
[2]);
410 if (!(statetype_filter
& (1 << hard
)))
412 severity
= parse_host_state(strv
[1]);
413 if (!(host_state_filter
& (1 << severity
)))
415 if (!auth_host_ok(strv
[0]))
417 if (!is_interesting_host(strv
[0]))
419 if (hide_state_dupes
&& !host_has_new_state(strv
[0], severity
, hard
))
424 case EVT_ALERT
| EVT_SERVICE
:
425 case EVT_STATE
| EVT_SERVICE
:
426 hard
= soft_hard(strv
[3]);
427 if (!(statetype_filter
& (1 << hard
)))
429 severity
= parse_service_state(strv
[2]);
430 if (!(service_state_filter
& (1 << severity
)))
432 if (!auth_service_ok(strv
[0], strv
[1]))
434 if (!is_interesting_service(strv
[0], strv
[1]))
436 if (hide_state_dupes
&& !service_has_new_state(strv
[0], strv
[1], severity
, hard
))
441 case EVT_FLAPPING
| EVT_HOST
:
442 case EVT_DOWNTIME
| EVT_HOST
:
443 if (!auth_host_ok(strv
[0]))
445 if (!is_interesting_host(strv
[0]))
449 case EVT_FLAPPING
| EVT_SERVICE
:
450 case EVT_DOWNTIME
| EVT_SERVICE
:
451 if (!auth_service_ok(strv
[0], strv
[1]))
453 if (!is_interesting_service(strv
[0], strv
[1]))
457 case EVT_NOTIFY
| EVT_HOST
:
458 if (!auth_host_ok(strv
[1]))
460 if (!is_interesting_host(strv
[1]))
463 case EVT_NOTIFY
| EVT_SERVICE
:
464 if (!auth_service_ok(strv
[1], strv
[2]))
466 if (!is_interesting_service(strv
[1], strv
[2]))
470 print_line(sc
->code
, line
, len
);
475 * hashes one line from an "interesting"-file. We use (void *)1
476 * to mark this as "present in hash-table" as we have no real
477 * data to lookup but still want hash_find{,2} to return non-NULL
478 * when it finds a match
480 static int hash_one_line(char *line
, uint len
)
482 return add_interesting_object(line
);
485 static int hash_interesting(const char *path
)
489 if (stat(path
, &st
) < 0)
490 crash("failed to stat %s: %s", path
, strerror(errno
));
492 lparse_path(path
, st
.st_size
, hash_one_line
);
497 static void parse_host_state_filter(char *p
)
499 host_state_filter
= 0;
503 host_state_filter
= -1;
506 host_state_filter
|= 1 << HOST_UNREACHABLE
;
509 host_state_filter
|= 1 << HOST_DOWN
;
512 host_state_filter
|= 1 << HOST_UP
;
518 static void parse_service_state_filter(char *p
)
520 service_state_filter
= 0;
524 service_state_filter
= -1;
527 service_state_filter
|= 1 << SERVICE_OK
;
530 service_state_filter
|= 1 << SERVICE_WARNING
;
533 service_state_filter
|= 1 << SERVICE_CRITICAL
;
536 service_state_filter
|= 1 << SERVICE_UNKNOWN
;
541 extern const char *__progname
;
542 static void usage(const char *fmt
, ...)
554 printf("usage: %s [options] [logfiles]\n\n", __progname
);
555 printf(" <logfiles> refers to all the nagios logfiles you want to search through\n");
556 printf(" If --nagios-cfg is given or can be inferred no logfiles need to be supplied\n");
557 printf("Options:\n");
558 printf(" --reverse parse (and print) logs in reverse\n");
559 printf(" --help this cruft\n");
560 printf(" --debug print debugging information\n");
561 printf(" --html print html output\n");
562 printf(" --ansi force-colorize the output\n");
563 printf(" --ascii don't colorize the output\n"),
564 printf(" --user=<username> show only logs this user can see\n");
565 printf(" --cgi-cfg=</path/to/cgi.cfg> path to cgi.cfg\n");
566 printf(" --nagios-cfg=</path/to/nagios.cfg> path to nagios.cfg\n");
567 printf(" --object-cache=</path/to/objects.cache> path to objects.cache\n");
568 printf(" --image-url=<image url> url to images. Implies --html\n");
569 printf(" --hide-state-dupes hide duplicate status messages\n");
570 printf(" --hide-flapping hide flapping messages\n");
571 printf(" --hide-downtime hide downtime messages\n");
572 printf(" --hide-process hide process messages\n");
573 printf(" --hide-command hide external command messages\n");
574 printf(" --hide-notifications hide notification messages\n");
575 printf(" --hide-logrotation hide log rotation messages\n");
576 printf(" --hide-initial hide INITIAL and CURRENT states\n");
577 printf(" --skip=<integer> number of filtered in messages to skip\n");
578 printf(" --limit=<integer> max number of messages to print\n");
579 printf(" --host=<host_name> show log entries for the named host\n");
580 printf(" --service=<hostname;servicedescription> show log entries for the named service\n");
581 printf(" --first=<timestamp> first log-entry to show\n");
582 printf(" --last=<timestamp> last log-entry to show\n");
583 printf(" --state-type=[hard|soft] state-types to show. default is all\n");
584 printf(" --host-states=[*ardu] host-states to show. can be mixed\n");
585 printf(" 'a' and '*' shows 'all'\n");
586 printf(" 'r' shows 'recovery'\n");
587 printf(" 'd' shows 'down'\n");
588 printf(" 'u' shows 'unreachable'\n");
589 printf(" --service-states=[*arwcu] service-states to show. can be mixed\n");
590 printf(" 'a' and '*' shows 'all'\n");
591 printf(" 'r' shows 'recovery'\n");
592 printf(" 'w' shows 'warning'\n");
593 printf(" 'c' shows 'critical'\n");
594 printf(" 'u' shows 'unknown'\n");
595 printf(" --time-format=[");
596 for (i
= 0; time_format_selections
[i
].name
; i
++) {
597 printf("%s", time_format_selections
[i
].name
);
598 if (time_format_selections
[i
+ 1].name
)
601 printf("] set timeformat for log-entries\n");
611 int main(int argc
, char **argv
)
614 const char *nagios_cfg
= NULL
, *cgi_cfg
= NULL
, *object_cache
= NULL
;
616 strv
= calloc(sizeof(char *), MAX_NVECS
);
618 crash("Failed to alloc initial structs");
620 if (isatty(fileno(stdout
))) {
621 real_print_line
= print_line_ansi
;
622 event_filter
&= ~(EVT_LROTATE
| EVT_PROCESS
);
625 for (i
= 1; i
< argc
; i
++) {
626 char *opt
, *arg
= argv
[i
];
627 int arg_len
, eq_opt
= 0;
629 if ((opt
= strchr(arg
, '='))) {
633 else if (i
< argc
- 1) {
637 if (!strcmp(arg
, "--reverse")) {
638 reverse_parse_files
= 1;
641 if (!strcmp(arg
, "--html")) {
642 real_print_line
= print_line_html
;
645 if (!strcmp(arg
, "--ansi")) {
646 real_print_line
= print_line_ansi
;
649 if (!strcmp(arg
, "--ascii")) {
650 real_print_line
= print_line_ascii
;
653 if (!strcmp(arg
, "--debug") || !strcmp(arg
, "-d")) {
657 if (!strcmp(arg
, "--help")) {
661 if (!prefixcmp(arg
, "--hide-state-dupes")) {
662 hide_state_dupes
= 1;
665 if (!prefixcmp(arg
, "--hide-flapping")) {
666 event_filter
&= ~EVT_FLAPPING
;
669 if (!prefixcmp(arg
, "--hide-downtime")) {
670 event_filter
&= ~EVT_DOWNTIME
;
673 if (!prefixcmp(arg
, "--hide-process")) {
674 event_filter
&= ~EVT_PROCESS
;
677 if (!prefixcmp(arg
, "--hide-command")) {
678 event_filter
&= ~EVT_COMMAND
;
681 if (!prefixcmp(arg
, "--hide-notification")) {
682 event_filter
&= ~EVT_NOTIFY
;
685 if (!prefixcmp(arg
, "--hide-logrotat")) {
686 event_filter
&= ~EVT_LROTATE
;
689 if (!prefixcmp(arg
, "--hide-initial")) {
690 event_filter
&= ~EVT_INITIAL
;
694 if (!prefixcmp(arg
, "--")) {
696 usage("Option '%s' requires an argument\n", arg
);
701 /* options parsed below require arguments */
702 if (!strcmp(arg
, "--user")) {
706 if (!prefixcmp(arg
, "--object-cache")) {
710 if (!strcmp(arg
, "--nagios-cfg")) {
714 if (!strcmp(arg
, "--cgi-cfg")) {
718 if (!strcmp(arg
, "--skip")) {
719 skip
= strtoul(opt
, NULL
, 0);
722 if (!strcmp(arg
, "--limit")) {
723 limit
= strtoul(opt
, NULL
, 0);
726 if (!strcmp(arg
, "--host")) {
727 event_filter
|= EVT_HOST
;
728 add_interesting_object(opt
);
731 if (!strcmp(arg
, "--service")) {
732 event_filter
|= EVT_SERVICE
;
733 add_interesting_object(opt
);
736 if (!strcmp(arg
, "--image-url")) {
737 real_print_line
= print_line_html
;
741 if (!strcmp(arg
, "--interesting") || !strcmp(arg
, "-i")) {
743 usage("%s requires a filename as argument", arg
);
744 hash_interesting(opt
);
747 if (!strcmp(arg
, "--first") || !strcmp(arg
, "--last")) {
751 crash("%s requires a timestamp as argument", arg
);
752 when
= strtoul(opt
, NULL
, 0);
755 if (!strcmp(arg
, "--first"))
761 if (!strcmp(arg
, "--state-type")) {
762 if (!strcasecmp(opt
, "hard"))
763 statetype_filter
= (1 << HARD_STATE
);
764 if (!strcasecmp(opt
, "soft"))
765 statetype_filter
= (1 << SOFT_STATE
);
768 if (!strcmp(arg
, "--host-states")) {
769 event_filter
|= EVT_HOST
;
770 parse_host_state_filter(opt
);
773 if (!strcmp(arg
, "--service-states")) {
774 event_filter
|= EVT_SERVICE
;
775 parse_service_state_filter(opt
);
778 if (!strcmp(arg
, "--time-format")) {
779 parse_time_format(opt
);
783 /* non-argument, so treat as config- or log-file */
784 arg_len
= strlen(arg
);
785 if (arg_len
> 7 && !strcmp(&arg
[arg_len
- 7], "cgi.cfg")) {
787 } else if (!strcmp(&arg
[strlen(arg
) - 10], "nagios.cfg")) {
790 add_naglog_path(arg
);
795 print_interesting_objects();
797 /* fallback for op5 systems */
798 if (auth_get_user() || (!nagios_cfg
&& !num_nfile
)) {
799 struct cfg_comp
*conf
;
801 conf
= cfg_parse_file(cgi_cfg
? cgi_cfg
: "/opt/monitor/etc/cgi.cfg");
804 for (i
= 0; i
< conf
->vars
; i
++) {
805 struct cfg_var
*v
= conf
->vlist
[i
];
806 if (!nagios_cfg
&& !strcmp(v
->key
, "main_config_file")) {
807 nagios_cfg
= strdup(v
->value
);
809 if (!prefixcmp(v
->key
, "authorized_for_")) {
810 auth_parse_permission(v
->key
, v
->value
);
813 cfg_destroy_compound(conf
);
816 crash("Failed to parse cgi.cfg file '%s'\n", cgi_cfg
);
818 if (auth_get_user()) {
819 crash("--user given, but no suitable cgi.cfg file found\n");
824 if (!nagios_cfg
&& !num_nfile
) {
825 nagios_cfg
= "/opt/monitor/etc/nagios.cfg";
828 struct cfg_comp
*conf
;
830 conf
= cfg_parse_file(nagios_cfg
);
832 usage("Failed to parse nagios' main config file '%s'\n", nagios_cfg
);
833 for (i
= 0; i
< conf
->vars
; i
++) {
834 struct cfg_var
*v
= conf
->vlist
[i
];
835 if (!strcmp(v
->key
, "log_file")) {
836 add_naglog_path(v
->value
);
838 if (!strcmp(v
->key
, "log_archive_path")) {
839 add_naglog_path(v
->value
);
841 if (!object_cache
&& !strcmp(v
->key
, "object_cache_file")) {
842 object_cache
= v
->value
;
850 if (auth_get_user() && object_cache
) {
851 auth_init(object_cache
);
854 if (hide_state_dupes
)
857 /* make sure first_time and last_time are set */
858 last_time
= last_time
? last_time
: time(NULL
);
859 first_time
= first_time
? first_time
: 1;
861 /* flip them if the user made an error (common when reverse-importing) */
862 if (last_time
< first_time
) {
863 int temp
= last_time
;
864 last_time
= first_time
;
868 if (reverse_parse_files
)
869 qsort(nfile
, num_nfile
, sizeof(*nfile
), nfile_rev_cmp
);
871 qsort(nfile
, num_nfile
, sizeof(*nfile
), nfile_cmp
);
873 for (i
= 0; i
< num_nfile
; i
++) {
874 struct naglog_file
*nf
= &nfile
[i
];
875 time_t last
; /* last possible timestamp in current file */
877 if (reverse_parse_files
)
878 last
= i
? nfile
[i
- 1].first
: time(NULL
);
880 last
= i
+ 1 < num_nfile
? nfile
[i
+ 1].first
: time(NULL
);
882 if (first_time
> last
|| last_time
< nf
->first
) {
887 debug("importing from %s (%lu : %u)\n", nf
->path
, nf
->first
, nf
->cmp
);
889 lparse_path_real(reverse_parse_files
, nf
->path
, nf
->size
, parse_line
);
892 if (warnings
&& debug_level
)
893 fprintf(stderr
, "Total warnings: %d\n", warnings
);
895 print_unhandled_events();