import: Don't enable indexes unless we've disabled them
[nagios-reports-module.git] / logutils.c
blob8c774850106a4d9d24609533ca4b52bb1eed2eb0
1 #include "logutils.h"
3 char **strv = NULL;
4 int num_nfile = 0;
5 int nfile_alloc = 0;
6 struct naglog_file *nfile;
7 int debug_level = 0;
8 struct naglog_file *cur_file; /* the file we're currently importing */
9 uint line_no = 0;
10 uint num_unhandled = 0;
11 uint warnings = 0;
12 static hash_table *interesting_hosts, *interesting_services;
14 #define host_code(S) { 0, #S, sizeof(#S) - 1, HOST_##S }
15 static struct string_code host_state[] = {
16 host_code(UP),
17 host_code(DOWN),
18 host_code(UNREACHABLE),
21 #define service_code(S) { 0, #S, sizeof(#S) - 1, SERVICE_##S }
22 static struct string_code service_state[] = {
23 service_code(OK),
24 service_code(WARNING),
25 service_code(CRITICAL),
26 service_code(UNKNOWN),
29 #define notification_code(S) { 0, #S, sizeof(#S) - 1, NOTIFICATION_##S }
30 static struct string_code notification_reason[] = {
31 notification_code(ACKNOWLEDGEMENT),
32 notification_code(FLAPPINGSTART),
33 notification_code(DOWNTIMESTART),
34 notification_code(FLAPPINGSTOP),
35 notification_code(FLAPPINGDISABLED),
36 notification_code(DOWNTIMESTART),
37 notification_code(DOWNTIMEEND),
38 notification_code(DOWNTIMECANCELLED),
39 notification_code(CUSTOM),
42 int parse_service_state_gently(const char *str)
44 int i;
46 for (i = 0; i < ARRAY_SIZE(service_state); i++) {
47 if (!strcmp(str, service_state[i].str))
48 return service_state[i].code;
51 return -1;
54 int parse_service_state(const char *str)
56 int ret = parse_service_state_gently(str);
58 if (ret < 0)
59 crash("bad value for service state: '%s'", str);
61 return ret;
64 int parse_host_state_gently(const char *str)
66 int i;
68 for (i = 0; i < ARRAY_SIZE(host_state); i++) {
69 if (!strcmp(str, host_state[i].str))
70 return host_state[i].code;
73 return -1;
76 int parse_host_state(const char *str)
78 int ret = parse_host_state_gently(str);
80 if (ret < 0)
81 crash("bad value for host state: '%s'", str);
83 return ret;
86 int parse_notification_reason(const char *str)
88 int i;
90 for (i = 0; i < ARRAY_SIZE(notification_reason); i++) {
91 if (!prefixcmp(str, notification_reason[i].str))
92 return notification_reason[i].code;
95 return NOTIFICATION_NORMAL;
98 int soft_hard(const char *str)
100 if (!strcmp(str, "HARD"))
101 return HARD_STATE;
103 if (!strcmp(str, "SOFT"))
104 return SOFT_STATE;
105 crash("wtf kind of value is '%s' to determine 'soft' or 'hard' from?", str);
108 static int print_string(void *data)
110 const char *str = data;
112 printf("%p: %s\n", str, str);
113 return 0;
117 * prints the objects we consider interesting
119 void print_interesting_objects(void)
121 if (interesting_hosts) {
122 printf("\nInteresting hosts:\n");
123 hash_walk_data(interesting_hosts, print_string);
125 if (interesting_services) {
126 printf("\nInteresting services:\n");
127 hash_walk_data(interesting_services, print_string);
129 if (interesting_hosts || interesting_services)
130 putchar('\n');
133 /* marks one object as 'interesting' */
134 int add_interesting_object(const char *orig_str)
136 char *semi_colon, *str;
138 str = strdup(orig_str);
139 semi_colon = strchr(str, ';');
140 if (!semi_colon) {
141 if (!interesting_hosts)
142 interesting_hosts = hash_init(512);
143 if (!interesting_hosts)
144 crash("Failed to initialize hash table for interesting hosts");
145 hash_add(interesting_hosts, str, strdup(orig_str));
146 } else {
147 if (!interesting_services)
148 interesting_services = hash_init(512);
149 if (!interesting_services)
150 crash("Failed to initialize hash table for interesting services");
151 *semi_colon++ = 0;
152 hash_add2(interesting_services, str, semi_colon, strdup(orig_str));
155 return 0;
158 int is_interesting_host(const char *host)
160 if (interesting_hosts)
161 return !!hash_find(interesting_hosts, host);
163 return 1;
166 int is_interesting_service(const char *host, const char *service)
168 /* fall back to checking if host is interesting */
169 if (!service || !interesting_services)
170 return is_interesting_host(host);
172 return !!hash_find2(interesting_services, host, service);
175 struct unhandled_event {
176 char *file;
177 const char *line;
178 unsigned line_no;
179 struct unhandled_event *next;
182 static struct unhandled_event *event_list;
184 * This is a fairly toothless function, since we can encounter
185 * pretty much any kind of message in the logfiles. In order to
186 * make sure we don't miss anything important though, we stash
187 * the messages and print them at the end if we're debugging.
189 void handle_unknown_event(const char *line)
191 struct unhandled_event *event;
193 num_unhandled++;
195 if (!(event = malloc(sizeof(*event))) || !(event->line = strdup(line))) {
196 crash("Failed to allocate memory for unhandled event [%s]\n", line);
197 return;
200 event->line_no = line_no;
201 event->file = cur_file->path;
203 /* add to "top" of list. we'll print in reverse order */
204 event->next = event_list;
205 event_list = event;
208 void print_unhandled_events(void)
210 struct unhandled_event *event;
211 int x = 1;
213 if (!num_unhandled)
214 return;
216 printf("\n%u Unhandled events encountered:\n" \
217 "------------------------------", num_unhandled);
219 for (x = 1; num_unhandled > (x * 10); x *= 10)
220 putchar('-');
222 putchar('\n');
223 for (event = event_list; event; event = event->next) {
224 printf("%s:%d:\n%s\n----\n", event->file, event->line_no, event->line);
228 int vectorize_string(char *str, int nvecs)
230 char *p;
231 int i = 0;
233 strv[i++] = str;
234 for (p = str; *p && i < nvecs; p++) {
235 if (*p == ';') {
236 *p = 0;
237 strv[i++] = ++p;
241 return i;
244 struct string_code *
245 get_string_code(struct string_code *codes, const char *str, uint len)
247 int i;
249 for (i = 0; codes[i].str; i++)
250 if (codes[i].len == len && !memcmp(str, codes[i].str, len))
251 return &codes[i];
253 return NULL;
256 int is_interesting(const char *ptr)
258 if (!prefixcmp(ptr, "Auto-save of retention data"))
259 return 0;
260 if (!prefixcmp(ptr, "Event broker module"))
261 return 0;
262 if (!prefixcmp(ptr, "You do not have permission"))
263 return 0;
264 if (!prefixcmp(ptr, "Local time is"))
265 return 0;
267 return 1;
270 int is_start_event(const char *ptr)
272 if (!prefixcmp(ptr, "Finished daemonizing..."))
273 return 1;
274 if (!prefixcmp(ptr, "Caught SIGHUP"))
275 return 1;
276 if (strstr(ptr, "starting..."))
277 return 1;
279 return 0;
282 int is_stop_event(const char *ptr)
284 if (!prefixcmp(ptr, "PROGRAM_RESTART"))
285 return 1;
286 if (!prefixcmp(ptr, "Caught SIGTERM"))
287 return 1;
288 if (!prefixcmp(ptr, "Successfully shutdown..."))
289 return 1;
290 if (!prefixcmp(ptr, "Bailing out"))
291 return 1;
292 if (!prefixcmp(ptr, "Lockfile"))
293 return 1;
294 if (strstr(ptr, "shutting down..."))
295 return 1;
297 return 0;
300 int strtotimet(const char *str, time_t *val)
302 char *endp;
304 *val = strtoul(str, &endp, 10);
305 if (endp == str) {
306 warn("strtotimet(): %s is not a valid time_t\n", str);
307 return -1;
310 return 0;
313 /*** general utility functions ***/
314 void __attribute__((__noreturn__)) crash(const char *fmt, ...)
316 va_list ap;
318 va_start(ap, fmt);
319 vfprintf(stderr, fmt, ap);
320 va_end(ap);
321 fputc('\n', stderr);
323 if (cur_file) {
324 fprintf(stderr, "crash() called when parsing line %u in %s\n",
325 line_no, cur_file->path);
328 exit(1);
331 void pdebug(int lvl, const char *fmt, ...)
333 va_list ap;
335 if (debug_level < lvl)
336 return;
338 va_start(ap, fmt);
339 vprintf(fmt, ap);
340 va_end(ap);
341 if (fmt[strlen(fmt) - 1] != '\n')
342 putchar('\n');
345 void warn(const char *fmt, ...)
347 va_list ap;
349 warnings++;
351 if (!debug_level)
352 return;
354 printf("WARNING: ");
355 va_start(ap, fmt);
356 vprintf(fmt, ap);
357 va_end(ap);
358 putchar('\n');
364 * Returns an increasing numeric value for a nagios logfile
365 * For a file with a name such as:
366 * nagios-12-01-2002-00.log
367 * it will return
368 * 2002120100
370 #define NUM_PARTS 4
371 uint path_cmp_number(char *path)
373 uint ret, len;
374 char *dash = NULL;
375 int i;
376 unsigned long part[NUM_PARTS];
378 dash = strrchr(path, '/');
379 if (!dash)
380 dash = path;
381 else
382 dash++;
385 * we special-case nagios.log as always being the
386 * last file to be parsed. It has to be, since it's
387 * the currently active logfile
389 if (!strcmp(dash, "nagios.log"))
390 return 1 << ((8 * sizeof(ret)) - 1);
392 len = strlen(dash);
393 if (len < 18 || strcmp(&dash[len - 4], ".log"))
394 return 0;
396 for (i = 0; i < NUM_PARTS; i++) {
397 char *endp;
399 dash = strchr(dash, '-');
400 if (!dash)
401 return 0;
403 dash++;
404 part[i] = strtoul(dash, &endp, 10);
405 if (!part[i] && dash == endp)
406 return 0;
407 if (!endp)
408 return 0;
409 dash = endp;
411 if (part[0] < 1 || part[0] > 12)
412 return 0;
413 if (part[1] < 1 || part[1] > 31)
414 return 0;
415 if (!part[2])
416 return 0;
417 ret = part[2] * 1000000;
418 ret += part[0] * 10000;
419 ret += part[1] * 100;
420 ret += part[3];
422 return ret;
425 #define min(a, b) ((a) < (b) ? (a) : (b))
426 void first_log_time(struct naglog_file *nf)
428 int fd, i = 0;
429 char buf[1024];
430 struct stat st;
432 if (!(fd = open(nf->path, O_RDONLY)))
433 crash("Failed to open %s: %s", nf->path, strerror(errno));
436 * since we're looking at every file in here anyway,
437 * we also determine the size of them so we can do an
438 * arena allocation large enough to fit the largest
439 * file + an added newline later
441 if (fstat(fd, &st) < 0)
442 crash("Failed to stat %s: %s", nf->path, strerror(errno));
444 nf->size = st.st_size;
446 if (read(fd, buf, sizeof(buf)) < min(sizeof(buf), st.st_size))
447 crash("Incomplete read of %s", nf->path);
449 buf[sizeof(buf) - 1] = 0;
450 /* skip empty lines at top of file */
451 while (i < sizeof(buf) - 12 && (buf[i] == '\n' || buf[i] == '\r'))
452 i++;
454 if (strtotimet(buf + i + 1, &nf->first))
455 crash("'%s' has no timestamp for us to parse", buf);
457 nf->cmp = path_cmp_number(nf->path);
458 close(fd);
461 static void filesort_mismatch(const struct naglog_file *a, const struct naglog_file *b)
463 printf("filesort mismatch:\n");
464 printf(" %s:\n cmp: %d\n first: %lu\n", a->path, a->cmp, a->first);
465 printf(" %s:\n cmp: %d\n first: %lu\n", b->path, b->cmp, b->first);
466 crash("%s and %s have same 'first' and 'cmp'? Bizarre...", a->path, b->path);
470 * sort function for nagios logfiles. Sorts based on
471 * first logged timestamp and then on filename, ascendingly
473 int nfile_cmp(const void *p1, const void *p2)
475 const struct naglog_file *a = p1;
476 const struct naglog_file *b = p2;
478 if (a->first > b->first)
479 return 1;
480 if (b->first > a->first)
481 return -1;
483 if (a->cmp > b->cmp)
484 return 1;
485 if (b->cmp > a->cmp)
486 return -1;
488 filesort_mismatch(a, b);
489 return 0;
492 /* same as above, but sorts in reverse order */
493 int nfile_rev_cmp(const void *p1, const void *p2)
495 const struct naglog_file *a = p1;
496 const struct naglog_file *b = p2;
498 if (a->first < b->first)
499 return 1;
500 if (b->first < a->first)
501 return -1;
503 if (a->cmp < b->cmp)
504 return 1;
505 if (b->cmp < a->cmp)
506 return -1;
508 filesort_mismatch(a, b);
509 return 0;
513 #ifndef PATH_MAX
514 # define PATH_MAX 4096
515 #endif
516 /* recurse into a log-archive path and find all logfiles there */
517 static int add_naglog_directory(const char *dir)
519 char path[PATH_MAX];
520 DIR *dirp;
521 struct dirent *de;
522 uint dlen = strlen(dir);
524 dirp = opendir(dir);
525 if (!dirp)
526 crash("Failed to opendir(%s): %s\n", dir, strerror(errno));
528 memcpy(path, dir, dlen);
529 path[dlen++] = '/';
530 while ((de = readdir(dirp))) {
531 path[dlen] = 0;
532 uint name_len;
533 if (prefixcmp(de->d_name, "nagios"))
534 continue;
535 name_len = strlen(de->d_name);
536 if (strcmp(&de->d_name[name_len - 4], ".log"))
537 continue;
539 /* build some sort of path to the file */
540 memcpy(&path[dlen], de->d_name, name_len);
541 path[dlen + name_len] = 0;
542 add_naglog_path(path);
544 closedir(dirp);
545 return 0;
548 /* Handles both files and directories */
549 int add_naglog_path(char *path)
551 struct stat st;
552 int i;
554 /* make sure we never add duplicate files */
555 for (i = 0; i < num_nfile; i++) {
556 if (!strcmp(nfile[i].path, path))
557 return -1;
560 if (stat(path, &st) < 0) {
561 crash("Failed to stat '%s': %s", path, strerror(errno));
563 if (S_ISDIR(st.st_mode)) {
564 return add_naglog_directory(path);
567 if (num_nfile >= nfile_alloc - 1) {
568 nfile_alloc += 20;
569 nfile = realloc(nfile, nfile_alloc * sizeof(*nfile));
572 nfile[num_nfile].path = strdup(path);
573 first_log_time(&nfile[num_nfile]);
574 num_nfile++;
576 return 0;