X-Git-Url: https://git.octo.it/?p=collection4.git;a=blobdiff_plain;f=src%2Fgraph_list.c;h=b979672ec3a0cbb2abb0160bf8a458450c8af763;hp=2112c76faa88dc7b84623df2ee975ee93aaa6259;hb=49f649c73d6b8481e40158de5b76e9cbfd4350d5;hpb=99f31aafefdd00b788d9ab2ba74b1cfb70c679e3 diff --git a/src/graph_list.c b/src/graph_list.c index 2112c76..b979672 100644 --- a/src/graph_list.c +++ b/src/graph_list.c @@ -31,11 +31,14 @@ #include #include #include +#include +#include #include #include "graph_list.h" #include "common.h" +#include "data_provider.h" #include "filesystem.h" #include "graph.h" #include "graph_config.h" @@ -52,7 +55,6 @@ * Defines */ #define UPDATE_INTERVAL 900 -#define CACHE_FILE "/tmp/collection4.json" /* * Global variables @@ -207,6 +209,14 @@ static int gl_register_file (const graph_ident_t *file, /* {{{ */ return (0); } /* }}} int gl_register_file */ +static int gl_register_ident (graph_ident_t *ident, /* {{{ */ + __attribute__((unused)) void *user_data) +{ + /* TODO: Check for duplicates if multiple data providers are used. */ + + return (gl_register_file (ident, user_data)); +} /* }}} int gl_register_ident */ + static const char *get_part_from_param (const char *prim_key, /* {{{ */ const char *sec_key) { @@ -232,32 +242,95 @@ static int gl_clear_instances (void) /* {{{ */ static void gl_dump_cb (void *ctx, /* {{{ */ const char *str, unsigned int len) { - FILE *fh = ctx; + int fd = *((int *) ctx); + const char *buffer; + size_t buffer_size; + ssize_t status; + + buffer = str; + buffer_size = (size_t) len; + while (buffer_size > 0) + { + status = write (fd, buffer, buffer_size); + if (status < 0) + { + fprintf (stderr, "write(2) failed with status %i\n", errno); + return; + } - /* FIXME: Has everything been written? */ - fwrite ((void *) str, /* size = */ 1, /* nmemb = */ len, fh); + buffer += status; + buffer_size -= status; + } } /* }}} void gl_dump_cb */ -static int gl_dump (void) /* {{{ */ +static int gl_update_cache (void) /* {{{ */ { - FILE *fh; + int fd; yajl_gen handler; yajl_gen_config handler_config = { /* pretty = */ 1, /* indent = */ " " }; + const char *cache_file = graph_config_get_cache_file (); + struct flock lock; + struct stat statbuf; + int status; size_t i; - /* FIXME: Lock the file */ - fh = fopen (CACHE_FILE, "w"); - if (fh == NULL) + memset (&statbuf, 0, sizeof (statbuf)); + status = stat (cache_file, &statbuf); + if (status == 0) + { + if (statbuf.st_mtime >= gl_last_update) + /* Not writing to cache because it's at least as new as our internal data */ + return (0); + } + else + { + status = errno; + fprintf (stderr, "gl_update_cache: stat(2) failed with status %i\n", + status); + /* Continue writing the file if possible. */ + } + + fd = open (cache_file, O_WRONLY | O_TRUNC | O_CREAT, + S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH); + if (fd < 0) + { + status = errno; + fprintf (stderr, "gl_update_cache: open(2) failed with status %i\n", + status); + return (status); + } + + memset (&lock, 0, sizeof (lock)); + lock.l_type = F_WRLCK; + lock.l_whence = SEEK_SET; + lock.l_start = 0; + lock.l_len = 0; /* lock everything */ + + while (42) + { + status = fcntl (fd, F_SETLKW, &lock); + if (status == 0) + break; + + if (errno == EINTR) + continue; + + fprintf (stderr, "gl_update_cache: fcntl(2) failed with status %i\n", errno); + close (fd); return (errno); + } handler = yajl_gen_alloc2 (gl_dump_cb, &handler_config, - /* alloc funcs = */ NULL, /* ctx = */ fh); + /* alloc funcs = */ NULL, /* ctx = */ &fd); if (handler == NULL) { - fclose (fh); + close (fd); return (-1); } + fprintf (stderr, "gl_update_cache: Start writing data\n"); + fflush (stderr); + yajl_gen_array_open (handler); for (i = 0; i < gl_active_num; i++) @@ -269,10 +342,13 @@ static int gl_dump (void) /* {{{ */ yajl_gen_array_close (handler); yajl_gen_free (handler); - fclose (fh); + close (fd); + + fprintf (stderr, "gl_update_cache: Finished writing data\n"); + fflush (stderr); return (0); -} /* }}} int gl_dump */ +} /* }}} int gl_update_cache */ /* * JSON parsing functions @@ -347,6 +423,23 @@ static int gl_json_string (void *user_data, /* {{{ */ return (1); } /* }}} int gl_json_string */ +static int gl_json_start_map (void *user_data) /* {{{ */ +{ + gl_json_context_t *ctx = user_data; + + if (((ctx->state & CTX_MASK) == CTX_GRAPH_SELECT) + || ((ctx->state & CTX_MASK) == CTX_INST_SELECT) + || ((ctx->state & CTX_MASK) == CTX_INST_FILE)) + { + assert (ctx->ident == NULL); + ctx->ident = ident_create (ANY_TOKEN, + ANY_TOKEN, ANY_TOKEN, + ANY_TOKEN, ANY_TOKEN); + } + + return (1); +} /* }}} int gl_json_start_map */ + static int gl_json_end_map (void *user_data) /* {{{ */ { gl_json_context_t *ctx = user_data; @@ -482,6 +575,8 @@ static int gl_json_key (void *user_data, /* {{{ */ || ((ctx->state & CTX_MASK) == CTX_INST_SELECT) || ((ctx->state & CTX_MASK) == CTX_INST_FILE)) { + assert (ctx->ident != NULL); + if (strcasecmp ("host", buffer) == 0) set_state (ctx, CTX_IDENT_HOST, CTX_IDENT_MASK); else if (strcasecmp ("plugin", buffer) == 0) @@ -494,7 +589,7 @@ static int gl_json_key (void *user_data, /* {{{ */ set_state (ctx, CTX_IDENT_TYPE_INSTANCE, CTX_IDENT_MASK); } - return (0); + return (1); } /* }}} int gl_json_key */ yajl_callbacks gl_json_callbacks = @@ -505,7 +600,7 @@ yajl_callbacks gl_json_callbacks = /* double = */ NULL, /* number = */ NULL, /* string = */ gl_json_string, - /* start_map = */ NULL, + /* start_map = */ gl_json_start_map, /* map_key = */ gl_json_key, /* end_map = */ gl_json_end_map, /* start_array = */ NULL, @@ -520,10 +615,12 @@ static int gl_read_cache (_Bool block) /* {{{ */ int fd; int cmd; + struct stat statbuf; struct flock lock; int status; + time_t now; - fd = open (CACHE_FILE, O_RDONLY); + fd = open (graph_config_get_cache_file (), O_RDONLY); if (fd < 0) { fprintf (stderr, "gl_read_cache: open(2) failed with status %i\n", errno); @@ -556,12 +653,54 @@ static int gl_read_cache (_Bool block) /* {{{ */ if (errno == EINTR) continue; + status = errno; fprintf (stderr, "gl_read_cache: fcntl(2) failed with status %i\n", - errno); - return (errno); + status); + close (fd); + return (status); + } + + fprintf (stderr, "gl_read_cache: Opening and locking " + "cache file successful\n"); + + memset (&statbuf, 0, sizeof (statbuf)); + status = fstat (fd, &statbuf); + if (status != 0) + { + status = errno; + fprintf (stderr, "gl_read_cache: fstat(2) failed with status %i\n", + status); + close (fd); + return (status); + } + + now = time (NULL); + + if (block) + { + /* Read the file. No excuses. */ + } + else if (statbuf.st_mtime <= gl_last_update) + { + /* Our current data is at least as new as the cache. Return. */ + fprintf (stderr, "gl_read_cache: Not using cache because " + "the internal data is newer\n"); + fflush (stderr); + close (fd); + return (0); + } + else if ((statbuf.st_mtime + UPDATE_INTERVAL) < now) + { + /* We'll scan the directory anyway, so there is no need to parse the cache + * here. */ + fprintf (stderr, "gl_read_cache: Not using cache because it's too old\n"); + fflush (stderr); + close (fd); + return (0); } memset (&context, 0, sizeof (context)); + context.state = CTX_GRAPH; context.cfg = NULL; context.inst = NULL; context.ident = NULL; @@ -571,8 +710,48 @@ static int gl_read_cache (_Bool block) /* {{{ */ /* alloc funcs = */ NULL, &context); + fprintf (stderr, "gl_read_cache: Start parsing data\n"); + fflush (stderr); + + while (42) + { + ssize_t rd_status; + char buffer[1024*1024]; + + rd_status = read (fd, buffer, sizeof (buffer)); + if (rd_status < 0) + { + if ((errno == EINTR) || (errno == EAGAIN)) + continue; + + status = errno; + fprintf (stderr, "gl_read_cache: read(2) failed with status %i\n", + status); + close (fd); + return (status); + } + else if (rd_status == 0) + { + yajl_parse_complete (handle); + break; + } + else + { + yajl_parse (handle, + (unsigned char *) &buffer[0], + (unsigned int) rd_status); + } + } + + yajl_free (handle); + + gl_last_update = statbuf.st_mtime; close (fd); + fprintf (stderr, "gl_read_cache: Finished parsing data\n"); + fflush (stderr); + + return (0); } /* }}} int gl_read_cache */ /* @@ -602,15 +781,15 @@ int gl_config_submit (void) /* {{{ */ return (0); } /* }}} int graph_config_submit */ -int gl_graph_get_all (graph_callback_t callback, /* {{{ */ - void *user_data) +int gl_graph_get_all (_Bool include_dynamic, /* {{{ */ + graph_callback_t callback, void *user_data) { size_t i; if (callback == NULL) return (EINVAL); - gl_update (); + gl_update (/* request served = */ 0); for (i = 0; i < gl_active_num; i++) { @@ -621,6 +800,9 @@ int gl_graph_get_all (graph_callback_t callback, /* {{{ */ return (status); } + if (!include_dynamic) + return (0); + for (i = 0; i < gl_dynamic_num; i++) { int status; @@ -650,7 +832,7 @@ graph_config_t *gl_graph_get_selected (void) /* {{{ */ ident = ident_create (host, plugin, plugin_instance, type, type_instance); - gl_update (); + gl_update (/* request served = */ 0); for (i = 0; i < gl_active_num; i++) { @@ -711,7 +893,7 @@ int gl_instance_get_all (graph_inst_callback_t callback, /* {{{ */ { size_t i; - gl_update (); + gl_update (/* request served = */ 0); for (i = 0; i < gl_active_num; i++) { @@ -871,20 +1053,24 @@ int gl_foreach_host (int (*callback) (const char *host, void *user_data), /* {{{ return (0); } /* }}} int gl_foreach_host */ -int gl_update (void) /* {{{ */ +int gl_update (_Bool request_served) /* {{{ */ { time_t now; int status; size_t i; - /* - printf ("Content-Type: text/plain\n\n"); - */ + if (!request_served && (gl_last_update > 0)) + return (0); now = time (NULL); if ((gl_last_update + UPDATE_INTERVAL) >= now) + { + /* Write data to cache if appropriate */ + if (request_served) + gl_update_cache (); return (0); + } /* Clear state */ gl_clear_instances (); @@ -893,18 +1079,34 @@ int gl_update (void) /* {{{ */ graph_read_config (); - status = fs_scan (/* callback = */ gl_register_file, /* user data = */ NULL); + status = gl_read_cache (/* block = */ 1); + /* We have *something* to work with. Even if it's outdated, just get on with + * handling the request and take care of re-reading data later on. */ + if ((status == 0) && !request_served) + return (0); + + if ((status != 0) + || ((gl_last_update + UPDATE_INTERVAL) < now)) + { + /* Clear state */ + gl_clear_instances (); + gl_clear_hosts (); + gl_destroy (&gl_dynamic, &gl_dynamic_num); + + data_provider_get_idents (gl_register_ident, /* user data = */ NULL); + + gl_last_update = now; + } if (host_list_len > 0) qsort (host_list, host_list_len, sizeof (*host_list), gl_compare_hosts); - gl_last_update = now; - for (i = 0; i < gl_active_num; i++) graph_sort_instances (gl_active[i]); - gl_dump (); + if (request_served) + gl_update_cache (); return (status); } /* }}} int gl_update */