Initial import.
authorFlorian Forster <ff@octo.it>
Fri, 30 Apr 2010 13:16:31 +0000 (15:16 +0200)
committerFlorian Forster <octo@leeloo.lan.home.verplant.org>
Fri, 30 Apr 2010 13:16:31 +0000 (15:16 +0200)
Makefile [new file with mode: 0644]
common.c [new file with mode: 0644]
common.h [new file with mode: 0644]
fcgi_test.c [new file with mode: 0644]
graph_list.c [new file with mode: 0644]
graph_list.h [new file with mode: 0644]
utils_params.c [new file with mode: 0644]
utils_params.h [new file with mode: 0644]

diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..970d85f
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,24 @@
+CC = gcc
+CPPFLAGS = 
+CFLAGS = -Wall -Wextra -O0 -g
+LDFLAGS = 
+LDLIBS = 
+
+all: test fcgi_test
+
+clean:
+       rm -f test.cgi
+
+common.o: common.c common.h
+
+graph_list.o: graph_list.c graph_list.h
+
+utils_params.o: utils_params.c utils_params.h
+
+test: test.c utils_params.o
+
+fcgi_test: LDLIBS = -lfcgi
+fcgi_test: fcgi_test.c common.o graph_list.o utils_params.o
+
+.PHONY: clean
+
diff --git a/common.c b/common.c
new file mode 100644 (file)
index 0000000..a2bfd09
--- /dev/null
+++ b/common.c
@@ -0,0 +1,145 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <dirent.h>
+
+#include "common.h"
+
+static int foreach_rrd_file (const char *dir, /* {{{ */
+    int (*callback) (const char *, void *),
+    void *user_data)
+{
+  DIR *dh;
+  struct dirent *entry;
+  int status;
+
+  if (callback == NULL)
+    return (EINVAL);
+
+  dh = opendir (dir);
+  if (dh == NULL)
+    return (errno);
+
+  while ((entry = readdir (dh)) != NULL)
+  {
+    struct stat statbuf;
+    char abspath[PATH_MAX + 1];
+    size_t d_name_len;
+
+    if (entry->d_name[0] == '.')
+      continue;
+
+    d_name_len = strlen (entry->d_name);
+    if (d_name_len <= 4)
+      continue;
+
+    if (strcasecmp (".rrd", entry->d_name + (d_name_len - 4)) != 0)
+      continue;
+
+    snprintf (abspath, sizeof (abspath), "%s/%s", dir, entry->d_name);
+    abspath[sizeof (abspath) - 1] = 0;
+
+    memset (&statbuf, 0, sizeof (statbuf));
+
+    status = stat (abspath, &statbuf);
+    if (status != 0)
+      continue;
+
+    if (!S_ISREG (statbuf.st_mode))
+      continue;
+
+    entry->d_name[d_name_len - 4] = 0;
+
+    status = (*callback) (entry->d_name, user_data);
+    if (status != 0)
+      break;
+  } /* while (readdir) */
+
+  closedir (dh);
+  return (status);
+} /* }}} int foreach_rrd_file */
+
+static int foreach_dir (const char *dir, /* {{{ */
+    int (*callback) (const char *, void *),
+    void *user_data)
+{
+  DIR *dh;
+  struct dirent *entry;
+  int status;
+
+  if (callback == NULL)
+    return (EINVAL);
+
+  dh = opendir (dir);
+  if (dh == NULL)
+    return (errno);
+
+  while ((entry = readdir (dh)) != NULL)
+  {
+    struct stat statbuf;
+    char abspath[PATH_MAX + 1];
+
+    if (entry->d_name[0] == '.')
+      continue;
+
+    snprintf (abspath, sizeof (abspath), "%s/%s", dir, entry->d_name);
+    abspath[sizeof (abspath) - 1] = 0;
+
+    memset (&statbuf, 0, sizeof (statbuf));
+
+    status = stat (abspath, &statbuf);
+    if (status != 0)
+      continue;
+
+    if (!S_ISDIR (statbuf.st_mode))
+      continue;
+
+    status = (*callback) (entry->d_name, user_data);
+    if (status != 0)
+      break;
+  } /* while (readdir) */
+
+  closedir (dh);
+  return (status);
+} /* }}} int foreach_dir */
+
+int foreach_type (const char *host, const char *plugin, /* {{{ */
+    callback_type_t callback, void *user_data)
+{
+  char abspath[PATH_MAX + 1];
+
+  if ((host == NULL) || (plugin == NULL))
+    return (EINVAL);
+
+  snprintf (abspath, sizeof (abspath), "%s/%s/%s", DATA_DIR, host, plugin);
+  abspath[sizeof (abspath) - 1] = 0;
+
+  return (foreach_rrd_file (abspath, callback, user_data));
+} /* }}} int foreach_type */
+
+int foreach_plugin (const char *host, /* {{{ */
+    callback_plugin_t callback,
+    void *user_data)
+{
+  char abspath[PATH_MAX + 1];
+
+  if (host == NULL)
+    return (EINVAL);
+
+  snprintf (abspath, sizeof (abspath), "%s/%s", DATA_DIR, host);
+  abspath[sizeof (abspath) - 1] = 0;
+
+  return (foreach_dir (abspath, callback, user_data));
+} /* }}} int foreach_plugin */
+
+int foreach_host (callback_host_t callback, /* {{{ */
+    void *user_data)
+{
+  return (foreach_dir (DATA_DIR, callback, user_data));
+} /* }}} int foreach_host */
+
+/* vim: set sw=2 sts=2 et fdm=marker : */
diff --git a/common.h b/common.h
new file mode 100644 (file)
index 0000000..01c8710
--- /dev/null
+++ b/common.h
@@ -0,0 +1,16 @@
+#ifndef COMMON_H
+#define COMMON_H 1
+
+#define DATA_DIR "/var/lib/collectd/rrd"
+
+typedef int (*callback_type_t)   (const char *type,   void *user_data);
+typedef int (*callback_plugin_t) (const char *plugin, void *user_data);
+typedef int (*callback_host_t)   (const char *host,   void *user_data);
+
+int foreach_type (const char *host, const char *plugin,
+    callback_type_t, void *user_data);
+int foreach_plugin (const char *host, callback_plugin_t, void *user_data);
+int foreach_host (callback_host_t, void *user_data);
+
+#endif /* COMMON_H */
+/* vim: set sw=2 sts=2 et fdm=marker : */
diff --git a/fcgi_test.c b/fcgi_test.c
new file mode 100644 (file)
index 0000000..e0d3035
--- /dev/null
@@ -0,0 +1,170 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>
+#include <errno.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <dirent.h>
+
+#include <fcgiapp.h>
+#include <fcgi_stdio.h>
+
+#include "common.h"
+#include "graph_list.h"
+#include "utils_params.h"
+
+struct str_array_s
+{
+  char **ptr;
+  size_t size;
+};
+typedef struct str_array_s str_array_t;
+
+static str_array_t *array_alloc (void) /* {{{ */
+{
+  str_array_t *a;
+
+  a = malloc (sizeof (*a));
+  if (a == NULL)
+    return (NULL);
+
+  memset (a, 0, sizeof (*a));
+  a->ptr = NULL;
+  a->size = 0;
+
+  return (a);
+} /* }}} str_array_t *array_alloc */
+
+static void array_free (str_array_t *a) /* {{{ */
+{
+  if (a == NULL)
+    return;
+
+  free (a->ptr);
+  a->ptr = NULL;
+  a->size = 0;
+
+  free (a);
+} /* }}} void array_free */
+
+static int array_add (const char *entry, void *user_data) /* {{{ */
+{
+  str_array_t *a = user_data;
+  char **ptr;
+
+  if ((entry == NULL) || (a == NULL))
+    return (EINVAL);
+
+  ptr = realloc (a->ptr, sizeof (*a->ptr) * (a->size + 1));
+  if (ptr == NULL)
+    return (ENOMEM);
+  a->ptr = ptr;
+  ptr = a->ptr + a->size;
+
+  *ptr = strdup (entry);
+  if (*ptr == NULL)
+    return (ENOMEM);
+
+  a->size++;
+  return (0);
+} /* }}} int array_add */
+
+static int print_graph (const graph_list_t *gl, void *user_data)
+{
+  if (gl == NULL)
+    return (EINVAL);
+
+  printf ("host = %s; plugin = %s;", gl->host, gl->plugin);
+  if (gl->plugin_instance != NULL)
+    printf (" plugin_instance = %s;", gl->plugin_instance);
+  printf (" type = %s;", gl->type);
+  if (gl->type_instance != NULL)
+    printf (" type_instance = %s;", gl->type_instance);
+  printf ("\n");
+
+  return (0);
+} /* }}} int print_graph */
+
+static int get_graphs_list (char ***ret_graphs, /* {{{ */
+    size_t *ret_graphs_num)
+{
+  gl_update ();
+  gl_foreach (print_graph, /* user_data = */ NULL);
+
+  return (0);
+} /* }}} int get_graphs_list */
+
+static int action_hello (void) /* {{{ */
+{
+  printf ("Content-Type: text/plain\n\n");
+
+  get_graphs_list (NULL, NULL);
+
+  return (0);
+} /* }}} int action_hello */
+
+static int action_usage (void) /* {{{ */
+{
+  printf ("Content-Type: text/plain\n\n");
+
+  fputs ("Usage:\n"
+      "\n"
+      "  Available actions:\n"
+      "\n"
+      "    * hello\n"
+      "\n", stdout);
+
+  return (0);
+} /* }}} int action_usage */
+
+static int handle_request (void) /* {{{ */
+{
+  const char *action;
+
+  param_init ();
+
+  action = param ("action");
+  if (action == NULL)
+  {
+    return (action_usage ());
+  }
+  else if (strcmp ("hello", action) == 0)
+  {
+    return (action_hello ());
+  }
+  else
+  {
+    return (action_usage ());
+  }
+} /* }}} int handle_request */
+
+static int run (void) /* {{{ */
+{
+  while (FCGI_Accept() >= 0)
+  {
+    handle_request ();
+    param_finish ();
+  }
+
+  return (0);
+} /* }}} int run */
+
+int main (int argc, char **argv) /* {{{ */
+{
+  int status;
+
+  argc = 0;
+  argv = NULL;
+
+  if (FCGX_IsCGI ())
+    status = handle_request ();
+  else
+    status = run ();
+
+  exit ((status == 0) ? EXIT_SUCCESS : EXIT_FAILURE);
+} /* }}} int main */
+
+/* vim: set sw=2 sts=2 et fdm=marker : */
diff --git a/graph_list.c b/graph_list.c
new file mode 100644 (file)
index 0000000..baa8849
--- /dev/null
@@ -0,0 +1,201 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+#include <errno.h>
+
+#include "graph_list.h"
+#include "common.h"
+
+static graph_list_t *graph_list = NULL;
+static size_t graph_list_length = 0;
+static time_t gl_last_update = 0;
+
+static int gl_add_copy (graph_list_t *gl) /* {{{ */
+{
+  graph_list_t *ptr;
+  int status;
+
+  if (gl == NULL)
+    return (EINVAL);
+
+  ptr = realloc (graph_list, sizeof (*graph_list) * (graph_list_length + 1));
+  if (ptr == NULL)
+    return (ENOMEM);
+  graph_list = ptr;
+
+  ptr = graph_list + graph_list_length;
+  memset (ptr, 0, sizeof (*ptr));
+  ptr->host = NULL;
+  ptr->plugin = NULL;
+  ptr->plugin_instance = NULL;
+  ptr->type = NULL;
+  ptr->type_instance = NULL;
+
+#define DUP_OR_BREAK(member) do {                    \
+  ptr->member = NULL;                                \
+  if (gl->member != NULL)                            \
+  {                                                  \
+    ptr->member = strdup (gl->member);               \
+    if (ptr->member == NULL)                         \
+      break;                                         \
+  }                                                  \
+} while (0)
+
+  status = ENOMEM;
+  do
+  {
+    DUP_OR_BREAK(host);
+    DUP_OR_BREAK(plugin);
+    DUP_OR_BREAK(plugin_instance);
+    DUP_OR_BREAK(type);
+    DUP_OR_BREAK(type_instance);
+
+    status = 0;
+  } while (0);
+
+#undef DUP_OR_BREAK
+
+  if (status != 0)
+  {
+    free (ptr->host);
+    free (ptr->plugin);
+    free (ptr->plugin_instance);
+    free (ptr->type);
+    free (ptr->type_instance);
+    return (status);
+  }
+
+  graph_list_length++;
+  return (0);
+} /* }}} int gl_add_copy */
+
+static int callback_type (const char *type, void *user_data) /* {{{ */
+{
+  graph_list_t *gl;
+  int status;
+
+  if ((type == NULL) || (user_data == NULL))
+    return (EINVAL);
+
+  gl = user_data;
+  if ((gl->type != NULL) || (gl->type_instance != NULL))
+    return (EINVAL);
+
+  gl->type = strdup (type);
+  if (gl->type == NULL)
+    return (ENOMEM);
+
+  gl->type_instance = strchr (gl->type, '-');
+  if (gl->type_instance != NULL)
+  {
+    *gl->type_instance = 0;
+    gl->type_instance++;
+  }
+
+  status = gl_add_copy (gl);
+
+  free (gl->type);
+  gl->type = NULL;
+  gl->type_instance = NULL;
+
+  return (status);
+} /* }}} int callback_type */
+
+static int callback_plugin (const char *plugin, void *user_data) /* {{{ */
+{
+  graph_list_t *gl;
+  int status;
+
+  if ((plugin == NULL) || (user_data == NULL))
+    return (EINVAL);
+
+  gl = user_data;
+  if ((gl->plugin != NULL) || (gl->plugin_instance != NULL))
+    return (EINVAL);
+
+  gl->plugin = strdup (plugin);
+  if (gl->plugin == NULL)
+    return (ENOMEM);
+
+  gl->plugin_instance = strchr (gl->plugin, '-');
+  if (gl->plugin_instance != NULL)
+  {
+    *gl->plugin_instance = 0;
+    gl->plugin_instance++;
+  }
+
+  status = foreach_type (gl->host, plugin, callback_type, gl);
+
+  free (gl->plugin);
+  gl->plugin = NULL;
+  gl->plugin_instance = NULL;
+
+  return (status);
+} /* }}} int callback_plugin */
+
+static int callback_host (const char *host, void *user_data) /* {{{ */
+{
+  graph_list_t *gl;
+  int status;
+
+  if ((host == NULL) || (user_data == NULL))
+    return (EINVAL);
+
+  gl = user_data;
+  if (gl->host != NULL)
+    return (EINVAL);
+
+  gl->host = strdup (host);
+  if (gl->host == NULL)
+    return (ENOMEM);
+
+  status =  foreach_plugin (host, callback_plugin, gl);
+
+  free (gl->host);
+  gl->host = NULL;
+
+  return (status);
+} /* }}} int callback_host */
+
+int gl_update (void) /* {{{ */
+{
+  time_t now;
+  graph_list_t gl;
+  int status;
+
+  now = time (NULL);
+
+  if ((gl_last_update + 2) >= now)
+    return (0);
+
+  memset (&gl, 0, sizeof (gl));
+  gl.host = NULL;
+  gl.plugin = NULL;
+  gl.plugin_instance = NULL;
+  gl.type = NULL;
+  gl.type_instance = NULL;
+
+  /* TODO: Free old list */
+
+  status = foreach_host (callback_host, &gl);
+  return (status);
+} /* }}} int gl_update */
+
+int gl_foreach (gl_callback callback, void *user_data) /* {{{ */
+{
+  size_t i;
+
+  for (i = 0; i < graph_list_length; i++)
+  {
+    int status;
+
+    status = (*callback) (graph_list + i, user_data);
+    if (status != 0)
+      return (status);
+  }
+
+  return (0);
+} /* }}} int gl_foreach */
+
+/* vim: set sw=2 sts=2 et fdm=marker : */
diff --git a/graph_list.h b/graph_list.h
new file mode 100644 (file)
index 0000000..9fa1372
--- /dev/null
@@ -0,0 +1,21 @@
+#ifndef GRAPH_LIST_H
+#define GRAPH_LIST_H 1
+
+struct graph_list_s
+{
+  char *host;
+  char *plugin;
+  char *plugin_instance;
+  char *type;
+  char *type_instance;
+};
+typedef struct graph_list_s graph_list_t;
+
+typedef int (*gl_callback) (const graph_list_t *, void *);
+
+int gl_update (void);
+int gl_foreach (gl_callback callback, void *user_data);
+
+
+#endif /* GRAPH_LIST_H */
+/* vim: set sw=2 sts=2 et fdm=marker : */
diff --git a/utils_params.c b/utils_params.c
new file mode 100644 (file)
index 0000000..ec943b5
--- /dev/null
@@ -0,0 +1,218 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+
+#include "utils_params.h"
+
+struct parameter_s
+{
+  char *key;
+  char *value;
+};
+typedef struct parameter_s parameter_t;
+
+static parameter_t *parameters = NULL;
+static size_t parameters_num = 0;
+static _Bool parameters_init = 0;
+
+static int parameter_add (const char *key, const char *value) /* {{{ */
+{
+  parameter_t *ptr;
+
+  if (value == NULL)
+    return (EINVAL);
+
+  ptr = realloc (parameters, sizeof (*parameters) * (parameters_num + 1));
+  if (ptr == NULL)
+    return (ENOMEM);
+  parameters = ptr;
+
+  ptr = parameters + parameters_num;
+  if (key == NULL)
+  {
+    ptr->key = NULL;
+  }
+  else
+  {
+    ptr->key = strdup (key);
+    if (ptr->key == NULL)
+      return (ENOMEM);
+  }
+
+  ptr->value = strdup (value);
+  if (ptr->value == NULL)
+  {
+    free (ptr->key);
+    return (ENOMEM);
+  }
+
+  parameters_num++;
+  return (0);
+} /* }}} int parameter_add */
+
+static char *parameter_lookup (const char *key) /* {{{ */
+{
+  size_t i;
+
+  for (i = 0; i < parameters_num; i++)
+  {
+    if ((key == NULL) && (parameters[i].key == NULL))
+      return (parameters[i].value);
+    else if ((key != NULL) && (parameters[i].key != NULL)
+        && (strcmp (key, parameters[i].key) == 0))
+      return (parameters[i].value);
+  }
+
+  return (NULL);
+} /* }}} char *parameter_lookup */
+
+static char *uri_unescape (char *string) /* {{{ */
+{
+  char *in;
+  char *out;
+
+  if (string == NULL)
+    return (NULL);
+
+  in = string;
+  out = string;
+
+  while (*in != 0)
+  {
+    if (*in == '+')
+    {
+      *out = ' ';
+    }
+    else if ((in[0] == '%')
+        && isxdigit ((int) in[1]) && isxdigit ((int) in[2]))
+    {
+      char tmpstr[3];
+      char *endptr;
+      long value;
+
+      tmpstr[0] = in[1];
+      tmpstr[1] = in[2];
+      tmpstr[2] = 0;
+
+      errno = 0;
+      endptr = NULL;
+      value = strtol (tmpstr, &endptr, /* base = */ 16);
+      if ((endptr == tmpstr) || (errno != 0))
+      {
+        *out = '?';
+      }
+      else
+      {
+        *out = (char) value;
+      }
+
+      in += 2;
+    }
+    else
+    {
+      *out = *in;
+    }
+
+    in++;
+    out++;
+  } /* while (*in != 0) */
+
+  *out = 0;
+  return (string);
+} /* }}} char *uri_unescape */
+
+static int parse_keyval (char *keyval) /* {{{ */
+{
+  char *key;
+  char *val;
+
+  val = strchr (keyval, '=');
+  if (val == NULL)
+  {
+    key = NULL;
+    val = keyval;
+  }
+  else
+  {
+    key = keyval;
+    *val = 0;
+    val++;
+  }
+
+  parameter_add (uri_unescape (key), uri_unescape (val));
+
+  return (0);
+} /* }}} int parse_keyval */
+
+static int parse_query_string (char *query_string) /* {{{ */
+{
+  char *dummy;
+  char *keyval;
+
+  if (query_string == NULL)
+    return (EINVAL);
+
+  dummy = query_string;
+  while ((keyval = strtok (dummy, ";&")) != NULL)
+  {
+    dummy = NULL;
+    parse_keyval (keyval);
+  }
+
+  return (0);
+} /* }}} int parse_query_string */
+
+int param_init (void) /* {{{ */
+{
+  const char *query_string;
+  char *copy;
+  int status;
+
+  if (parameters_init)
+    return (0);
+
+  query_string = getenv ("QUERY_STRING");
+  if (query_string == NULL)
+    return (ENOENT);
+
+  copy = strdup (query_string);
+  if (copy == NULL)
+    return (ENOMEM);
+
+  status = parse_query_string (copy);
+  free (copy);
+
+  parameters_init = 1;
+
+  return (status);
+} /* }}} int param_init */
+
+void param_finish (void) /* {{{ */
+{
+  size_t i;
+
+  if (!parameters_init)
+    return;
+
+  for (i = 0; i < parameters_num; i++)
+  {
+    free (parameters[i].key);
+    free (parameters[i].value);
+  }
+  free (parameters);
+
+  parameters = NULL;
+  parameters_num = 0;
+  parameters_init = 0;
+} /* }}} void param_finish */
+
+const char *param (const char *key) /* {{{ */
+{
+  param_init ();
+
+  return (parameter_lookup (key));
+} /* }}} const char *param */
+
+/* vim: set sw=2 sts=2 et fdm=marker : */
diff --git a/utils_params.h b/utils_params.h
new file mode 100644 (file)
index 0000000..b68ad4d
--- /dev/null
@@ -0,0 +1,9 @@
+#ifndef UTILS_PARAMS_H
+#define UTILS_PARAMS_H 1
+
+int param_init (void);
+void param_finish (void);
+
+const char *param (const char *key);
+
+#endif /* UTILS_PARAMS_H */