From 9e5770370bb085ccfb19f3dd14707542bf1475f1 Mon Sep 17 00:00:00 2001 From: Florian Forster Date: Fri, 30 Apr 2010 15:16:31 +0200 Subject: [PATCH 1/1] Initial import. --- Makefile | 24 +++++++ common.c | 145 ++++++++++++++++++++++++++++++++++++++ common.h | 16 +++++ fcgi_test.c | 170 ++++++++++++++++++++++++++++++++++++++++++++ graph_list.c | 201 ++++++++++++++++++++++++++++++++++++++++++++++++++++ graph_list.h | 21 ++++++ utils_params.c | 218 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ utils_params.h | 9 +++ 8 files changed, 804 insertions(+) create mode 100644 Makefile create mode 100644 common.c create mode 100644 common.h create mode 100644 fcgi_test.c create mode 100644 graph_list.c create mode 100644 graph_list.h create mode 100644 utils_params.c create mode 100644 utils_params.h diff --git a/Makefile b/Makefile new file mode 100644 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 index 0000000..a2bfd09 --- /dev/null +++ b/common.c @@ -0,0 +1,145 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#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 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 index 0000000..e0d3035 --- /dev/null +++ b/fcgi_test.c @@ -0,0 +1,170 @@ +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#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 index 0000000..baa8849 --- /dev/null +++ b/graph_list.c @@ -0,0 +1,201 @@ +#include +#include +#include +#include +#include + +#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 index 0000000..9fa1372 --- /dev/null +++ b/graph_list.h @@ -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 index 0000000..ec943b5 --- /dev/null +++ b/utils_params.c @@ -0,0 +1,218 @@ +#include +#include +#include +#include +#include + +#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 index 0000000..b68ad4d --- /dev/null +++ b/utils_params.h @@ -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 */ -- 2.11.0