4 /* stubs required for linking */
5 int ipc_grok_var(char *var
, char *val
) { return 0; }
7 /* global variables used in all log-handling apps */
10 static int nfile_alloc
= 0;
11 struct naglog_file
*nfile
;
13 struct naglog_file
*cur_file
; /* the file we're currently importing */
15 uint num_unhandled
= 0;
17 static dkhash_table
*interesting_hosts
, *interesting_services
;
19 #define host_code(S) { 0, #S, sizeof(#S) - 1, HOST_##S }
20 static struct string_code host_state
[] = {
23 host_code(UNREACHABLE
),
26 #define service_code(S) { 0, #S, sizeof(#S) - 1, SERVICE_##S }
27 static struct string_code service_state
[] = {
29 service_code(WARNING
),
30 service_code(CRITICAL
),
31 service_code(UNKNOWN
),
34 #define notification_code(S) { 0, #S, sizeof(#S) - 1, NOTIFICATION_##S }
35 static struct string_code notification_reason
[] = {
36 notification_code(ACKNOWLEDGEMENT
),
37 notification_code(FLAPPINGSTART
),
38 notification_code(DOWNTIMESTART
),
39 notification_code(FLAPPINGSTOP
),
40 notification_code(FLAPPINGDISABLED
),
41 notification_code(DOWNTIMESTART
),
42 notification_code(DOWNTIMEEND
),
43 notification_code(DOWNTIMECANCELLED
),
44 notification_code(CUSTOM
),
47 /*** general utility functions ***/
48 void __attribute__((__noreturn__
)) lp_crash(const char *fmt
, ...)
53 vfprintf(stderr
, fmt
, ap
);
58 fprintf(stderr
, "crash() called when parsing line %u in %s\n",
59 line_no
, cur_file
->path
);
65 void pdebug(int lvl
, const char *fmt
, ...)
69 if (debug_level
< lvl
)
75 if (fmt
[strlen(fmt
) - 1] != '\n')
79 void warn(const char *fmt
, ...)
96 int parse_service_state_gently(const char *str
)
100 for (i
= 0; i
< ARRAY_SIZE(service_state
); i
++) {
101 if (!strcmp(str
, service_state
[i
].str
))
102 return service_state
[i
].code
;
108 int parse_service_state(const char *str
)
110 int ret
= parse_service_state_gently(str
);
113 lp_crash("bad value for service state: '%s'", str
);
118 int parse_host_state_gently(const char *str
)
122 for (i
= 0; i
< ARRAY_SIZE(host_state
); i
++) {
123 if (!strcmp(str
, host_state
[i
].str
))
124 return host_state
[i
].code
;
130 int parse_host_state(const char *str
)
132 int ret
= parse_host_state_gently(str
);
135 lp_crash("bad value for host state: '%s'", str
);
140 int parse_notification_reason(const char *str
)
144 for (i
= 0; i
< ARRAY_SIZE(notification_reason
); i
++) {
145 if (!prefixcmp(str
, notification_reason
[i
].str
))
146 return notification_reason
[i
].code
;
149 return NOTIFICATION_NORMAL
;
152 int soft_hard(const char *str
)
154 if (!strcmp(str
, "HARD"))
157 if (!strcmp(str
, "SOFT"))
159 lp_crash("wtf kind of value is '%s' to determine 'soft' or 'hard' from?", str
);
162 static int print_string(void *data
)
164 const char *str
= data
;
166 printf("%p: %s\n", str
, str
);
171 * prints the objects we consider interesting
173 void print_interesting_objects(void)
175 if (interesting_hosts
) {
176 printf("\nInteresting hosts:\n");
177 dkhash_walk_data(interesting_hosts
, print_string
);
179 if (interesting_services
) {
180 printf("\nInteresting services:\n");
181 dkhash_walk_data(interesting_services
, print_string
);
183 if (interesting_hosts
|| interesting_services
)
187 /* marks one object as 'interesting' */
188 int add_interesting_object(const char *orig_str
)
190 char *semi_colon
, *str
;
192 str
= strdup(orig_str
);
193 semi_colon
= strchr(str
, ';');
195 if (!interesting_hosts
)
196 interesting_hosts
= dkhash_create(512);
197 if (!interesting_hosts
)
198 crash("Failed to initialize hash table for interesting hosts");
199 dkhash_insert(interesting_hosts
, str
, NULL
, strdup(orig_str
));
201 if (!interesting_services
)
202 interesting_services
= dkhash_create(512);
203 if (!interesting_services
)
204 lp_crash("Failed to initialize hash table for interesting services");
206 dkhash_insert(interesting_services
, str
, semi_colon
, strdup(orig_str
));
212 int is_interesting_host(const char *host
)
214 if (interesting_hosts
)
215 return !!dkhash_get(interesting_hosts
, host
, NULL
);
220 int is_interesting_service(const char *host
, const char *service
)
222 /* fall back to checking if host is interesting */
223 if (!service
|| !interesting_services
)
224 return is_interesting_host(host
);
226 return !!dkhash_get(interesting_services
, host
, service
);
229 struct unhandled_event
{
233 unsigned long repeated
;
234 struct unhandled_event
*next
;
237 static struct unhandled_event
*event_list
;
239 * This is a fairly toothless function, since we can encounter
240 * pretty much any kind of message in the logfiles. In order to
241 * make sure we don't miss anything important though, we stash
242 * the messages and print them at the end if we're debugging.
244 void handle_unknown_event(const char *line
)
246 struct unhandled_event
*event
;
247 static struct unhandled_event
*last
= NULL
;
251 if (last
&& !strncmp(&line
[14], &last
->line
[14], 20)) {
256 /* add to top of list. we'll print in reverse order */
258 /* add to "top" of list. we'll print in reverse order */
259 last
->next
= event_list
;
264 if (!(event
= calloc(1, sizeof(*event
))) || !(event
->line
= strdup(line
))) {
265 lp_crash("Failed to allocate memory for unhandled event [%s]\n", line
);
269 event
->line_no
= line_no
;
270 event
->file
= cur_file
->path
;
274 void print_unhandled_events(void)
276 struct unhandled_event
*event
;
283 * add the fake closing event so we get the last
284 * real event added to the list. The fake one won't
285 * get added though, so we needn't bother with it.
287 handle_unknown_event("Fake unhandled event");
290 * fake message bumps counter, so we decrease it here
291 * to get an accurate count
295 printf("\n%u Unhandled events encountered:\n" \
296 "------------------------------", num_unhandled
);
298 for (x
= 1; num_unhandled
> (x
* 10); x
*= 10)
302 for (event
= event_list
; event
; event
= event
->next
) {
303 printf("%s:%d:\n%s\n", event
->file
, event
->line_no
, event
->line
);
304 if (event
->repeated
) {
305 printf(" #### Similar events repeated %lu times\n", event
->repeated
);
311 int vectorize_string(char *str
, int nvecs
)
317 for (p
= str
; *p
&& i
< nvecs
; p
++) {
328 * This takes care of lines that have been field-separated at
329 * semi-colons and passes it to the function above.
331 char *devectorize_string(char **ary
, int nvecs
)
335 for (i
= 1; i
< nvecs
; i
++) {
336 /* the char before the first char in the string
337 * in our array is the one where we replaced a
338 * semi-colon with a nul char, so the math here
339 * is actually correct.
348 get_string_code(struct string_code
*codes
, const char *str
, uint len
)
352 for (i
= 0; codes
[i
].str
; i
++)
353 if (codes
[i
].len
== len
&& !memcmp(str
, codes
[i
].str
, len
))
359 int is_interesting(const char *ptr
)
361 if (!prefixcmp(ptr
, "Auto-save of retention data"))
363 if (!prefixcmp(ptr
, "Event broker module"))
365 if (!prefixcmp(ptr
, "You do not have permission"))
367 if (!prefixcmp(ptr
, "Local time is"))
373 int is_start_event(const char *ptr
)
375 if (!prefixcmp(ptr
, "Finished daemonizing..."))
377 if (!prefixcmp(ptr
, "Caught SIGHUP"))
379 if (strstr(ptr
, "starting..."))
385 int is_stop_event(const char *ptr
)
387 if (!prefixcmp(ptr
, "PROGRAM_RESTART"))
389 if (!prefixcmp(ptr
, "Caught SIGTERM"))
391 if (!prefixcmp(ptr
, "Successfully shutdown..."))
393 if (!prefixcmp(ptr
, "Bailing out"))
395 if (!prefixcmp(ptr
, "Lockfile"))
397 if (strstr(ptr
, "shutting down..."))
403 int strtotimet(const char *str
, time_t *val
)
407 *val
= strtoul(str
, &endp
, 10);
409 warn("strtotimet(): %s is not a valid time_t\n", str
);
417 * Returns an increasing numeric value for a nagios logfile
418 * For a file with a name such as:
419 * nagios-12-01-2002-00.log
424 uint
path_cmp_number(char *path
)
429 unsigned long part
[NUM_PARTS
];
431 dash
= strrchr(path
, '/');
438 * we special-case nagios.log as always being the
439 * last file to be parsed. It has to be, since it's
440 * the currently active logfile
442 if (!strcmp(dash
, "nagios.log"))
443 return 1 << ((8 * sizeof(ret
)) - 1);
446 if (len
< 18 || strcmp(&dash
[len
- 4], ".log"))
449 for (i
= 0; i
< NUM_PARTS
; i
++) {
452 dash
= strchr(dash
, '-');
457 part
[i
] = strtoul(dash
, &endp
, 10);
458 if (!part
[i
] && dash
== endp
)
464 if (part
[0] < 1 || part
[0] > 12)
466 if (part
[1] < 1 || part
[1] > 31)
470 ret
= part
[2] * 1000000;
471 ret
+= part
[0] * 10000;
472 ret
+= part
[1] * 100;
478 void first_log_time(struct naglog_file
*nf
)
485 if (!(fd
= open(nf
->path
, O_RDONLY
)))
486 lp_crash("Failed to open %s: %s", nf
->path
, strerror(errno
));
489 * since we're looking at every file in here anyway,
490 * we also determine the size of them so we can do an
491 * arena allocation large enough to fit the largest
492 * file + an added newline later
494 if (fstat(fd
, &st
) < 0)
495 lp_crash("Failed to stat %s: %s", nf
->path
, strerror(errno
));
497 nf
->size
= st
.st_size
;
499 if (read(fd
, buf
, sizeof(buf
)) < min((int)sizeof(buf
), st
.st_size
))
500 lp_crash("Incomplete read of %s", nf
->path
);
502 buf
[sizeof(buf
) - 1] = 0;
503 /* skip empty lines at top of file */
504 while (i
< sizeof(buf
) - 12 && (buf
[i
] == '\n' || buf
[i
] == '\r'))
507 if (strtotimet(buf
+ i
+ 1, &nf
->first
))
508 lp_crash("'%s' has no timestamp for us to parse", buf
);
510 nf
->cmp
= path_cmp_number(nf
->path
);
514 static void filesort_mismatch(const struct naglog_file
*a
, const struct naglog_file
*b
)
516 printf("filesort mismatch:\n");
517 printf(" %s:\n cmp: %d\n first: %lu\n", a
->path
, a
->cmp
, a
->first
);
518 printf(" %s:\n cmp: %d\n first: %lu\n", b
->path
, b
->cmp
, b
->first
);
519 lp_crash("%s and %s have same 'first' and 'cmp'? Bizarre...", a
->path
, b
->path
);
523 * sort function for nagios logfiles. Sorts based on
524 * first logged timestamp and then on filename, ascendingly
526 int nfile_cmp(const void *p1
, const void *p2
)
528 const struct naglog_file
*a
= p1
;
529 const struct naglog_file
*b
= p2
;
531 if (a
->first
> b
->first
)
533 if (b
->first
> a
->first
)
541 filesort_mismatch(a
, b
);
545 /* same as above, but sorts in reverse order */
546 int nfile_rev_cmp(const void *p1
, const void *p2
)
548 const struct naglog_file
*a
= p1
;
549 const struct naglog_file
*b
= p2
;
551 if (a
->first
< b
->first
)
553 if (b
->first
< a
->first
)
561 filesort_mismatch(a
, b
);
567 # define PATH_MAX 4096
569 /* recurse into a log-archive path and find all logfiles there */
570 static int add_naglog_directory(const char *dir
)
575 uint dlen
= strlen(dir
);
579 crash("Failed to opendir(%s): %s\n", dir
, strerror(errno
));
581 memcpy(path
, dir
, dlen
);
583 while ((de
= readdir(dirp
))) {
584 unsigned int name_len
;
586 if (prefixcmp(de
->d_name
, "nagios"))
588 name_len
= strlen(de
->d_name
);
589 if (strcmp(&de
->d_name
[name_len
- 4], ".log"))
592 /* build some sort of path to the file */
593 memcpy(&path
[dlen
], de
->d_name
, name_len
);
594 path
[dlen
+ name_len
] = 0;
595 add_naglog_path(path
);
601 /* Handles both files and directories */
602 int add_naglog_path(char *path
)
607 /* make sure we never add duplicate files */
608 for (i
= 0; i
< num_nfile
; i
++) {
609 if (!strcmp(nfile
[i
].path
, path
))
613 if (stat(path
, &st
) < 0) {
614 lp_crash("Failed to stat '%s': %s", path
, strerror(errno
));
616 if (S_ISDIR(st
.st_mode
)) {
617 return add_naglog_directory(path
);
620 if (num_nfile
>= nfile_alloc
- 1) {
622 nfile
= realloc(nfile
, nfile_alloc
* sizeof(*nfile
));
625 nfile
[num_nfile
].path
= strdup(path
);
626 first_log_time(&nfile
[num_nfile
]);