#include <assert.h>
#include <unistd.h>
#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
#include <yajl/yajl_parse.h>
#include "graph_list.h"
#include "common.h"
+#include "data_provider.h"
#include "filesystem.h"
#include "graph.h"
#include "graph_config.h"
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)
{
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 = */ " " };
+ 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++)
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
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;
|| ((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)
set_state (ctx, CTX_IDENT_TYPE_INSTANCE, CTX_IDENT_MASK);
}
- return (0);
+ return (1);
} /* }}} int gl_json_key */
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,
int fd;
int cmd;
+ struct stat statbuf;
struct flock lock;
int status;
+ time_t now;
fd = open (CACHE_FILE, O_RDONLY);
if (fd < 0)
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;
/* 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 */
/*
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++)
{
return (status);
}
+ if (!include_dynamic)
+ return (0);
+
for (i = 0; i < gl_dynamic_num; i++)
{
int status;
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++)
{
{
size_t i;
- gl_update ();
+ gl_update (/* request served = */ 0);
for (i = 0; i < gl_active_num; i++)
{
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 ();
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 */