auth lib: Add missing exclamation mark in auth_parse_permission()
[nagios-reports-module.git] / logutils.c
blob16bda784f2c134dff2f20ab5c2f9b2de72be07f8
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;
15 int parse_service_state(const char *str)
17 if (!strcmp(str, "OK"))
18 return SERVICE_OK;
19 if (!strcmp(str, "WARNING"))
20 return SERVICE_WARNING;
21 if (!strcmp(str, "UNKNOWN"))
22 return SERVICE_UNKNOWN;
23 if (!strcmp(str, "CRITICAL"))
24 return SERVICE_CRITICAL;
26 crash("bad value for service state: '%s'", str);
29 int parse_host_state(const char *str)
31 if (!strcmp(str, "UP"))
32 return HOST_UP;
33 if (!strcmp(str, "DOWN"))
34 return HOST_DOWN;
35 if (!strcmp(str, "UNREACHABLE"))
36 return HOST_UNREACHABLE;
38 crash("bad value for host state: '%s'", str);
41 int soft_hard(const char *str)
43 if (!strcmp(str, "HARD"))
44 return HARD_STATE;
46 if (!strcmp(str, "SOFT"))
47 return SOFT_STATE;
48 crash("wtf kind of value is '%s' to determine 'soft' or 'hard' from?", str);
51 static int print_string(void *data)
53 const char *str = data;
55 printf("%p: %s\n", str, str);
56 return 0;
60 * prints the objects we consider interesting
62 void print_interesting_objects(void)
64 if (interesting_hosts) {
65 printf("\nInteresting hosts:\n");
66 hash_walk_data(interesting_hosts, print_string);
68 if (interesting_services) {
69 printf("\nInteresting services:\n");
70 hash_walk_data(interesting_services, print_string);
72 if (interesting_hosts || interesting_services)
73 putchar('\n');
76 /* marks one object as 'interesting' */
77 int add_interesting_object(const char *orig_str)
79 char *semi_colon, *str;
81 str = strdup(orig_str);
82 semi_colon = strchr(str, ';');
83 if (!semi_colon) {
84 if (!interesting_hosts)
85 interesting_hosts = hash_init(512);
86 if (!interesting_hosts)
87 crash("Failed to initialize hash table for interesting hosts");
88 hash_add(interesting_hosts, str, strdup(orig_str));
89 } else {
90 if (!interesting_services)
91 interesting_services = hash_init(512);
92 if (!interesting_services)
93 crash("Failed to initialize hash table for interesting services");
94 *semi_colon++ = 0;
95 hash_add2(interesting_services, str, semi_colon, strdup(orig_str));
98 return 0;
101 int is_interesting_host(const char *host)
103 if (interesting_hosts)
104 return !!hash_find(interesting_hosts, host);
106 return 1;
109 int is_interesting_service(const char *host, const char *service)
111 /* fall back to checking if host is interesting */
112 if (!service || !interesting_services)
113 return is_interesting_host(host);
115 return !!hash_find2(interesting_services, host, service);
118 struct unhandled_event {
119 char *file;
120 const char *line;
121 unsigned line_no;
122 struct unhandled_event *next;
125 static struct unhandled_event *event_list;
127 * This is a fairly toothless function, since we can encounter
128 * pretty much any kind of message in the logfiles. In order to
129 * make sure we don't miss anything important though, we stash
130 * the messages and print them at the end if we're debugging.
132 void handle_unknown_event(const char *line)
134 struct unhandled_event *event;
136 num_unhandled++;
138 if (!(event = malloc(sizeof(*event))) || !(event->line = strdup(line))) {
139 crash("Failed to allocate memory for unhandled event [%s]\n", line);
140 return;
143 event->line_no = line_no;
144 event->file = cur_file->path;
146 /* add to "top" of list. we'll print in reverse order */
147 event->next = event_list;
148 event_list = event;
151 void print_unhandled_events(void)
153 struct unhandled_event *event;
154 int x = 1;
156 if (!num_unhandled)
157 return;
159 printf("\n%u Unhandled events encountered:\n" \
160 "------------------------------", num_unhandled);
162 for (x = 1; num_unhandled > (x * 10); x *= 10)
163 putchar('-');
165 putchar('\n');
166 for (event = event_list; event; event = event->next) {
167 printf("%s:%d:\n%s\n----\n", event->file, event->line_no, event->line);
171 int vectorize_string(char *str, int nvecs)
173 char *p;
174 int i = 0;
176 strv[i++] = str;
177 for (p = str; *p && i < nvecs; p++) {
178 if (*p == ';') {
179 *p = 0;
180 strv[i++] = ++p;
184 return i;
187 struct string_code *
188 get_string_code(struct string_code *codes, const char *str, uint len)
190 int i;
192 for (i = 0; codes[i].str; i++)
193 if (codes[i].len == len && !memcmp(str, codes[i].str, len))
194 return &codes[i];
196 return NULL;
199 int is_interesting(const char *ptr)
201 if (!prefixcmp(ptr, "Auto-save of retention data"))
202 return 0;
203 if (!prefixcmp(ptr, "Event broker module"))
204 return 0;
205 if (!prefixcmp(ptr, "You do not have permission"))
206 return 0;
207 if (!prefixcmp(ptr, "Local time is"))
208 return 0;
210 return 1;
213 int is_start_event(const char *ptr)
215 if (!prefixcmp(ptr, "Finished daemonizing..."))
216 return 1;
217 if (!prefixcmp(ptr, "Caught SIGHUP"))
218 return 1;
219 if (strstr(ptr, "starting..."))
220 return 1;
222 return 0;
225 int is_stop_event(const char *ptr)
227 if (!prefixcmp(ptr, "PROGRAM_RESTART"))
228 return 1;
229 if (!prefixcmp(ptr, "Caught SIGTERM"))
230 return 1;
231 if (!prefixcmp(ptr, "Successfully shutdown..."))
232 return 1;
233 if (!prefixcmp(ptr, "Bailing out"))
234 return 1;
235 if (!prefixcmp(ptr, "Lockfile"))
236 return 1;
237 if (strstr(ptr, "shutting down..."))
238 return 1;
240 return 0;
243 int strtotimet(const char *str, time_t *val)
245 char *endp;
247 *val = strtoul(str, &endp, 10);
248 if (endp == str) {
249 warn("strtotimet(): %s is not a valid time_t\n", str);
250 return -1;
253 return 0;
256 /*** general utility functions ***/
257 void __attribute__((__noreturn__)) crash(const char *fmt, ...)
259 va_list ap;
261 va_start(ap, fmt);
262 vfprintf(stderr, fmt, ap);
263 va_end(ap);
264 fputc('\n', stderr);
266 if (cur_file) {
267 fprintf(stderr, "crash() called when parsing line %u in %s\n",
268 line_no, cur_file->path);
271 exit(1);
274 void pdebug(int lvl, const char *fmt, ...)
276 va_list ap;
278 if (debug_level < lvl)
279 return;
281 va_start(ap, fmt);
282 vprintf(fmt, ap);
283 va_end(ap);
284 if (fmt[strlen(fmt) - 1] != '\n')
285 putchar('\n');
288 void warn(const char *fmt, ...)
290 va_list ap;
292 warnings++;
294 if (!debug_level)
295 return;
297 printf("WARNING: ");
298 va_start(ap, fmt);
299 vprintf(fmt, ap);
300 va_end(ap);
301 putchar('\n');
307 * Returns an increasing numeric value for a nagios logfile
308 * For a file with a name such as:
309 * nagios-12-01-2002-00.log
310 * it will return
311 * 2002120100
313 #define NUM_PARTS 4
314 uint path_cmp_number(char *path)
316 uint ret, len;
317 char *dash = NULL;
318 int i;
319 unsigned long part[NUM_PARTS];
321 dash = strrchr(path, '/');
322 if (!dash)
323 dash = path;
324 else
325 dash++;
328 * we special-case nagios.log as always being the
329 * last file to be parsed. It has to be, since it's
330 * the currently active logfile
332 if (!strcmp(dash, "nagios.log"))
333 return 1 << ((8 * sizeof(ret)) - 1);
335 len = strlen(dash);
336 if (len < 18 || strcmp(&dash[len - 4], ".log"))
337 return 0;
339 for (i = 0; i < NUM_PARTS; i++) {
340 char *endp;
342 dash = strchr(dash, '-');
343 if (!dash)
344 return 0;
346 dash++;
347 part[i] = strtoul(dash, &endp, 10);
348 if (!part[i] && dash == endp)
349 return 0;
350 if (!endp)
351 return 0;
352 dash = endp;
354 if (part[0] < 1 || part[0] > 12)
355 return 0;
356 if (part[1] < 1 || part[1] > 31)
357 return 0;
358 if (!part[2])
359 return 0;
360 ret = part[2] * 1000000;
361 ret += part[0] * 10000;
362 ret += part[1] * 100;
363 ret += part[3];
365 return ret;
368 #define min(a, b) ((a) < (b) ? (a) : (b))
369 void first_log_time(struct naglog_file *nf)
371 int fd, i = 0;
372 char buf[1024];
373 struct stat st;
375 if (!(fd = open(nf->path, O_RDONLY)))
376 crash("Failed to open %s: %s", nf->path, strerror(errno));
379 * since we're looking at every file in here anyway,
380 * we also determine the size of them so we can do an
381 * arena allocation large enough to fit the largest
382 * file + an added newline later
384 if (fstat(fd, &st) < 0)
385 crash("Failed to stat %s: %s", nf->path, strerror(errno));
387 nf->size = st.st_size;
389 if (read(fd, buf, sizeof(buf)) < min(sizeof(buf), st.st_size))
390 crash("Incomplete read of %s", nf->path);
392 buf[sizeof(buf) - 1] = 0;
393 /* skip empty lines at top of file */
394 while (i < sizeof(buf) - 12 && (buf[i] == '\n' || buf[i] == '\r'))
395 i++;
397 if (strtotimet(buf + i + 1, &nf->first))
398 crash("'%s' has no timestamp for us to parse", buf);
400 nf->cmp = path_cmp_number(nf->path);
401 close(fd);
404 static void filesort_mismatch(const struct naglog_file *a, const struct naglog_file *b)
406 printf("filesort mismatch:\n");
407 printf(" %s:\n cmp: %d\n first: %lu\n", a->path, a->cmp, a->first);
408 printf(" %s:\n cmp: %d\n first: %lu\n", b->path, b->cmp, b->first);
409 crash("%s and %s have same 'first' and 'cmp'? Bizarre...", a->path, b->path);
413 * sort function for nagios logfiles. Sorts based on
414 * first logged timestamp and then on filename, ascendingly
416 int nfile_cmp(const void *p1, const void *p2)
418 const struct naglog_file *a = p1;
419 const struct naglog_file *b = p2;
421 if (a->first > b->first)
422 return 1;
423 if (b->first > a->first)
424 return -1;
426 if (a->cmp > b->cmp)
427 return 1;
428 if (b->cmp > a->cmp)
429 return -1;
431 filesort_mismatch(a, b);
432 return 0;
435 /* same as above, but sorts in reverse order */
436 int nfile_rev_cmp(const void *p1, const void *p2)
438 const struct naglog_file *a = p1;
439 const struct naglog_file *b = p2;
441 if (a->first < b->first)
442 return 1;
443 if (b->first < a->first)
444 return -1;
446 if (a->cmp < b->cmp)
447 return 1;
448 if (b->cmp < a->cmp)
449 return -1;
451 filesort_mismatch(a, b);
452 return 0;
456 #ifndef PATH_MAX
457 # define PATH_MAX 4096
458 #endif
459 /* recurse into a log-archive path and find all logfiles there */
460 static int add_naglog_directory(const char *dir)
462 char path[PATH_MAX];
463 DIR *dirp;
464 struct dirent *de;
465 uint dlen = strlen(dir);
467 dirp = opendir(dir);
468 if (!dirp)
469 crash("Failed to opendir(%s): %s\n", dir, strerror(errno));
471 memcpy(path, dir, dlen);
472 path[dlen++] = '/';
473 while ((de = readdir(dirp))) {
474 path[dlen] = 0;
475 uint name_len;
476 if (prefixcmp(de->d_name, "nagios"))
477 continue;
478 name_len = strlen(de->d_name);
479 if (strcmp(&de->d_name[name_len - 4], ".log"))
480 continue;
482 /* build some sort of path to the file */
483 memcpy(&path[dlen], de->d_name, name_len);
484 path[dlen + name_len] = 0;
485 add_naglog_path(path);
487 closedir(dirp);
488 return 0;
491 /* Handles both files and directories */
492 int add_naglog_path(char *path)
494 struct stat st;
495 int i;
497 /* make sure we never add duplicate files */
498 for (i = 0; i < num_nfile; i++) {
499 if (!strcmp(nfile[i].path, path))
500 return -1;
503 if (stat(path, &st) < 0) {
504 crash("Failed to stat '%s': %s", path, strerror(errno));
506 if (S_ISDIR(st.st_mode)) {
507 return add_naglog_directory(path);
510 if (num_nfile >= nfile_alloc - 1) {
511 nfile_alloc += 20;
512 nfile = realloc(nfile, nfile_alloc * sizeof(*nfile));
515 nfile[num_nfile].path = strdup(path);
516 first_log_time(&nfile[num_nfile]);
517 num_nfile++;
519 return 0;