CORE_LIBS="$LIBS"
+EX_CHECK_ALL(glib-2.0, glib_check_version, glib.h, glib-2.0, 2.28.7, ftp://ftp.gtk.org/pub/glib/2.28/, "")
+GLIB_LIBS=$THIS_LIB
+AC_SUBST(GLIB_LIBS)
+
+
if test $enable_rrd_graph != no; then
dnl EX_CHECK_ALL(z, zlibVersion, zlib.h, zlib, 1.2.5, http://zlib.net/, "")
dnl EX_CHECK_ALL(png, png_access_version_number, png.h, libpng, 1.4.8, ftp://ftp.simplesystems.org/pub/libpng/png/src/, "")
EX_CHECK_ALL(pangocairo-1.0, pango_cairo_context_set_font_options, pango/pango.h, pangocairo, 1.28.4, http://ftp.gnome.org/pub/GNOME/sources/pango/1.28, "")
fi
-EX_CHECK_ALL(glib-2.0, glib_check_version, glib.h, glib-2.0, 2.28.7, ftp://ftp.gtk.org/pub/glib/2.28/, "")
EX_CHECK_ALL(xml2, xmlParseFile, libxml/parser.h, libxml-2.0, 2.7.8, http://xmlsoft.org/downloads.html, /usr/include/libxml2)
if test "$EX_CHECK_ALL_ERR" = "YES"; then
LDFLAGS=${LDFLAGS}" "`$PKGCONFIG --libs-only-L $4`
LDFLAGS=${LDFLAGS}" "`$PKGCONFIG --libs-only-other $4`
LIBS=${LIBS}" "`$PKGCONFIG --libs-only-l $4`
+ THIS_LIB=`$PKGCONFIG --libs-only-l $4`
dnl remove the cached value and test again
unset ac_cv_lib_`echo $1 | sed ['s/[^_a-zA-Z0-9]/_/g;s/^[0-9]/_/']`_$2
AC_CHECK_LIB($1,$2,[
rrd_nan_inf.c \
rrd_rpncalc.c \
rrd_utils.c \
+ mutex.c \
rrd_update.c
RRD_C_FILES = \
if BUILD_RRDGRAPH
RRD_C_FILES += rrd_graph.c \
rrd_graph_helper.c \
- rrd_xport.c \
+ rrd_xport.c optparse.c \
rrd_gfx.c \
pngsize.c
endif
noinst_HEADERS = \
unused.h \
gettext.h \
+ mutex.h \
rrd_getopt.h rrd_parsetime.h \
rrd_config_bottom.h rrd_i18n.h \
- rrd_format.h rrd_tool.h rrd_xport.h rrd.h rrd_rpncalc.h \
+ rrd_format.h rrd_tool.h rrd_xport.h optparse.h rrd.h rrd_rpncalc.h \
rrd_hw.h rrd_hw_math.h rrd_hw_update.h \
fnv.h rrd_graph.h \
rrd_is_thread_safe.h
endif
rrdcgi_SOURCES = rrd_cgi.c
-rrdcgi_LDADD = librrd.la
+rrdcgi_LDADD = librrd.la
rrdupdate_SOURCES = rrdupdate.c
rrdupdate_LDADD = librrdupd.la
rrdcached_SOURCES = rrd_daemon.c
rrdcached_DEPENDENCIES = librrd_th.la
rrdcached_CPPFLAGS = -DVERSION='"$(VERSION)"' -DLOCALSTATEDIR='"$(localstatedir)"'
-rrdcached_LDADD = librrd_th.la
+rrdcached_LDADD = librrd_th.la $(GLIB_LIBS)
pkgconfigdir = $(libdir)/pkgconfig
pkgconfig_DATA = librrd.pc
--- /dev/null
+/*
+ *
+ * mutex.c
+ *
+ * Cross platform mutex
+ *
+ */
+
+#include "mutex.h"
+
+int mutex_init(mutex_t *mutex)
+{
+#ifdef WIN32
+ *mutex = CreateMutex(NULL, FALSE, NULL);
+ return (*mutex == NULL);
+#else
+ return pthread_mutex_init(mutex, NULL);;
+#endif
+}
+
+int mutex_lock(mutex_t *mutex)
+{
+#ifdef WIN32
+ if (*mutex == NULL) { /* static initializer? */
+ HANDLE p = CreateMutex(NULL, FALSE, NULL);
+ if (InterlockedCompareExchangePointer((PVOID*)mutex, (PVOID)p, NULL) != NULL)
+ CloseHandle(p);
+ }
+ return (WaitForSingleObject(*mutex, INFINITE) == WAIT_FAILED);
+#else
+ return pthread_mutex_lock(mutex);
+#endif
+}
+
+int mutex_unlock(mutex_t *mutex)
+{
+#ifdef WIN32
+ return (ReleaseMutex(*mutex) == 0);
+#else
+ return pthread_mutex_unlock(mutex);
+#endif
+}
+
+int mutex_cleanup(mutex_t *mutex)
+{
+#ifdef WIN32
+ return (CloseHandle(mutex) == 0);
+#else
+ return pthread_mutex_destroy(mutex);
+#endif
+}
+
+/*
+ * vim: set sw=2 sts=2 ts=8 et fdm=marker :
+ */
+
--- /dev/null
+/*
+ * mutex.h - Cross platform mutex
+ */
+
+#ifndef MUTEX_H_B13C67AB432C4C39AF823A339537CA40
+#define MUTEX_H_B13C67AB432C4C39AF823A339537CA40
+
+#ifdef WIN32
+#include <windows.h>
+#include <process.h>
+#else
+#include <pthread.h>
+#endif
+
+#ifndef WIN32
+#define mutex_t pthread_mutex_t
+#define MUTEX_INITIALIZER PTHREAD_MUTEX_INITIALIZER
+#else
+#define mutex_t HANDLE
+#define MUTEX_INITIALIZER NULL
+#endif
+
+int mutex_init(mutex_t *mutex);
+int mutex_lock(mutex_t *mutex);
+int mutex_unlock(mutex_t *mutex);
+int mutex_cleanup(mutex_t *mutex);
+
+#endif /* MUTEX__H */
+
+/*
+ * vim: set sw=2 sts=2 ts=8 et fdm=marker :
+ */
+
--- /dev/null
+#include <stdio.h>
+#include "optparse.h"
+
+#define opterror(options, format, args...) \
+ snprintf(options->errmsg, sizeof(options->errmsg), format, args);
+
+void optparse_init(struct optparse *options, char **argv)
+{
+ options->argv = argv;
+ options->permute = 1;
+ options->optind = 1;
+ options->subopt = 0;
+ options->optarg = NULL;
+ options->errmsg[0] = '\0';
+}
+
+static inline int
+is_dashdash(const char *arg)
+{
+ return arg != NULL && arg[0] == '-' && arg[1] == '-' && arg[2] == '\0';
+}
+
+static inline int
+is_shortopt(const char *arg)
+{
+ return arg != NULL && arg[0] == '-' && arg[1] != '-' && arg[1] != '\0';
+}
+
+static inline int
+is_longopt(const char *arg)
+{
+ return arg != NULL && arg[0] == '-' && arg[1] == '-' && arg[2] != '\0';
+}
+
+static void
+permute(struct optparse *options, int index)
+{
+ char *nonoption = options->argv[index];
+ for (int i = index; i < options->optind - 1; i++)
+ options->argv[i] = options->argv[i + 1];
+ options->argv[options->optind - 1] = nonoption;
+}
+
+static enum optparse_argtype
+argtype(const char *optstring, char c)
+{
+ if (c == ':')
+ return -1;
+ for (; *optstring && c != *optstring; optstring++);
+ if (!*optstring)
+ return -1;
+ enum optparse_argtype count = OPTPARSE_NONE;
+ if (optstring[1] == ':')
+ count += optstring[2] == ':' ? 2 : 1;
+ return count;
+}
+
+int optparse(struct optparse *options, const char *optstring)
+{
+ options->errmsg[0] = '\0';
+ options->optopt = 0;
+ options->optarg = NULL;
+ char *option = options->argv[options->optind];
+ if (option == NULL) {
+ return -1;
+ } else if (is_dashdash(option)) {
+ options->optind++; // consume "--"
+ return -1;
+ } else if (!is_shortopt(option)) {
+ if (options->permute) {
+ int index = options->optind;
+ options->optind++;
+ int r = optparse(options, optstring);
+ permute(options, index);
+ options->optind--;
+ return r;
+ } else {
+ return -1;
+ }
+ }
+ option += options->subopt + 1;
+ options->optopt = option[0];
+ int type = argtype(optstring, option[0]);
+ char *next = options->argv[options->optind + 1];
+ switch (type) {
+ case -1:
+ opterror(options, "invalid option -- '%c'", option[0]);
+ options->optind++;
+ return '?';
+ case OPTPARSE_NONE:
+ if (option[1]) {
+ options->subopt++;
+ } else {
+ options->subopt = 0;
+ options->optind++;
+ }
+ return option[0];
+ case OPTPARSE_REQUIRED:
+ options->subopt = 0;
+ options->optind++;
+ if (option[1]) {
+ options->optarg = option + 1;
+ } else if (next != NULL) {
+ options->optarg = next;
+ options->optind++;
+ } else {
+ opterror(options, "option requires an argument -- '%c'", option[0]);
+ options->optarg = NULL;
+ return '?';
+ }
+ return option[0];
+ case OPTPARSE_OPTIONAL:
+ options->subopt = 0;
+ options->optind++;
+ if (option[1])
+ options->optarg = option + 1;
+ else
+ options->optarg = NULL;
+ return option[0];
+ }
+ return 0;
+}
+
+char *optparse_arg(struct optparse *options)
+{
+ options->subopt = 0;
+ char *option = options->argv[options->optind];
+ if (option != NULL)
+ options->optind++;
+ return option;
+}
+
+static inline int
+longopts_end(const struct optparse_long *longopts, int i)
+{
+ return !longopts[i].longname && !longopts[i].shortname;
+}
+
+static size_t
+optstring_length(const struct optparse_long *longopts)
+{
+ int length = 0;
+ for (int i = 0; !longopts_end(longopts, i); i++, length++)
+ length += longopts[i].argtype;
+ return length + 1;
+}
+
+static void
+optstring_from_long(const struct optparse_long *longopts, char *optstring)
+{
+ char *p = optstring;
+ for (int i = 0; !longopts_end(longopts, i); i++) {
+ if (longopts[i].shortname) {
+ *p++ = longopts[i].shortname;
+ for (int a = 0; a < longopts[i].argtype; a++)
+ *p++ = ':';
+ }
+ }
+ *p = '\0';
+}
+
+/* Unlike strcmp(), handles options containing "=". */
+static int
+longopts_match(const char *longname, const char *option)
+{
+ if (longname == NULL)
+ return 0;
+ const char *a = option, *n = longname;
+ for (; *a && *n && *a != '='; a++, n++)
+ if (*a != *n)
+ return 0;
+ return *n == '\0' && (*a == '\0' || *a == '=');
+}
+
+/* Return the part after "=", or NULL. */
+static const char *
+longopts_arg(const char *option)
+{
+ for (; *option && *option != '='; option++);
+ if (*option == '=')
+ return option + 1;
+ else
+ return NULL;
+}
+
+static int
+long_fallback(struct optparse *options,
+ const struct optparse_long *longopts,
+ int *longindex)
+{
+ char optstring[optstring_length(longopts)];
+ optstring_from_long(longopts, optstring);
+ int result = optparse(options, optstring);
+ if (longindex != NULL) {
+ *longindex = -1;
+ if (result != -1)
+ for (int i = 0; !longopts_end(longopts, i); i++)
+ if (longopts[i].shortname == options->optopt)
+ *longindex = i;
+ }
+ return result;
+}
+
+int
+optparse_long(struct optparse *options,
+ const struct optparse_long *longopts,
+ int *longindex)
+{
+ char *option = options->argv[options->optind];
+ if (option == NULL) {
+ return -1;
+ } else if (is_shortopt(option)) {
+ return long_fallback(options, longopts, longindex);
+ } else if (!is_longopt(option)) {
+ if (options->permute) {
+ int index = options->optind;
+ options->optind++;
+ int r = optparse_long(options, longopts, longindex);
+ permute(options, index);
+ options->optind--;
+ return r;
+ } else {
+ return -1;
+ }
+ }
+
+ /* Parse as long option. */
+ options->errmsg[0] = '\0';
+ options->optopt = 0;
+ options->optarg = NULL;
+ option += 2; // skip "--"
+ options->optind++;
+ for (int i = 0; !longopts_end(longopts, i); i++) {
+ const char *name = longopts[i].longname;
+ if (longopts_match(name, option)) {
+ if (longindex)
+ *longindex = i;
+ options->optopt = longopts[i].shortname;
+ const char *arg = longopts_arg(option);
+ if (longopts[i].argtype == OPTPARSE_NONE && arg != NULL) {
+ opterror(options, "option takes no arguments -- '%s'", name);
+ return '?';
+ } if (arg != NULL) {
+ options->optarg = arg;
+ } else if (longopts[i].argtype == OPTPARSE_REQUIRED) {
+ options->optarg = options->argv[options->optind++];
+ if (options->optarg == NULL) {
+ opterror(options, "option requires argument -- '%s'", name);
+ return '?';
+ }
+ }
+ return options->optopt;
+ }
+ }
+ opterror(options, "invalid option -- '%s'", option);
+ return '?';
+}
--- /dev/null
+#ifndef OPTPARSE_H
+#define OPTPARSE_H
+
+/**
+ * Optparse -- portable, reentrant, embeddable, getopt-like option parser
+ *
+ * The POSIX getopt() option parser has three fatal flaws. These flaws
+ * are solved by Optparse.
+ *
+ * 1) Parser state is stored entirely in global variables, some of
+ * which are static and inaccessible. This means only one thread can
+ * use getopt(). It also means it's not possible to recursively parse
+ * nested sub-arguments while in the middle of argument parsing.
+ * Optparse fixes this by storing all state on a local struct.
+ *
+ * 2) The POSIX standard provides no way to properly reset the parser.
+ * This means for portable code that getopt() is only good for one
+ * run, over one argv with one optstring. It also means subcommand
+ * options cannot be processed with getopt(). Most implementations
+ * provide a method to reset the parser, but it's not portable.
+ * Optparse provides an optparse_arg() function for stepping over
+ * subcommands and continuing parsing of options with another
+ * optstring. The Optparse struct itself can be passed around to
+ * subcommand handlers for additional subcommand option parsing. A
+ * full reset can be achieved by with an additional optparse_init().
+ *
+ * 3) Error messages are printed to stderr. This can be disabled with
+ * opterr, but the messages themselves are still inaccessible.
+ * Optparse solves this by writing an error message in its errmsg
+ * field. The downside to Optparse is that this error message will
+ * always be in English rather than the current locale.
+ *
+ * Optparse should be familiar with anyone accustomed to getopt(), and
+ * it could be a nearly drop-in replacement. The optstring is the same
+ * and the fields have the same names as the getopt() global variables
+ * (optarg, optind, optopt).
+ *
+ * Optparse also supports GNU-style long options with optparse_long().
+ * The interface is slightly different and simpler than getopt_long().
+ *
+ * By default, argv is permuted as it is parsed, moving non-option
+ * arguments to the end. This can be disabled by setting the `permute`
+ * field to 0 after initialization.
+ */
+
+struct optparse {
+ char **argv;
+ int permute;
+ int optind;
+ int optopt;
+ const char *optarg;
+ char errmsg[48];
+ int subopt;
+};
+
+enum optparse_argtype { OPTPARSE_NONE, OPTPARSE_REQUIRED, OPTPARSE_OPTIONAL };
+
+struct optparse_long {
+ const char *longname;
+ int shortname;
+ enum optparse_argtype argtype;
+};
+
+/**
+ * Initializes the parser state.
+ */
+void optparse_init(struct optparse *options, char **argv);
+
+/**
+ * Read the next option in the argv array.
+ * @param optstring a getopt()-formatted option string.
+ * @return the next option character, -1 for done, or '?' for error
+ *
+ * Just like getopt(), a character followed by a colon means no
+ * argument. One colon means the option has a required argument. Two
+ * colons means the option takes an optional argument.
+ */
+int optparse(struct optparse *options, const char *optstring);
+
+/**
+ * Handles GNU-style long options in addition to getopt() options.
+ * This works a lot like GNU's getopt_long(). The last option in
+ * longopts must be all zeros, marking the end of the array. The
+ * longindex argument may be NULL.
+ */
+int
+optparse_long(struct optparse *options,
+ const struct optparse_long *longopts,
+ int *longindex);
+
+/**
+ * Used for stepping over non-option arguments.
+ * @return the next non-option argument, or -1 for no more arguments
+ *
+ * Argument parsing can continue with optparse() after using this
+ * function. That would be used to parse the options for the
+ * subcommand returned by optparse_arg(). This function allows you to
+ * ignore the value of optind.
+ */
+char *optparse_arg(struct optparse *options);
+
+#endif
im.graphfile[0] = '\0';
}
- rrd_graph_script(argc, argv, &im, 1);
+ rrd_graph_script(argc, argv, &im, optind+1);
setlocale(LC_NUMERIC, old_locale); /* reenable locale for rendering the graph */
if (rrd_test_error()) {
enum gf_en last_gf = GF_PRINT;
float last_linewidth = 0.0;
- for (i = optind + optno; i < argc; i++) {
+ for (i = optno; i < argc; i++) {
graph_desc_t *gdp;
unsigned int eaten = 0;
/* System Headers */
/* Local headers */
-
+#include "mutex.h"
#include <stdarg.h>
#include <stdlib.h>
#include <ctype.h>
const char *tspec,
rrd_time_value_t * ptv)
{
+ static mutex_t parsetime_mutex = MUTEX_INITIALIZER;
time_t now = time(NULL);
int hr = 0;
+ /* yes this code is non re-entrant ... so lets make sure we do not run
+ in twice */
+ mutex_lock(&parsetime_mutex);
+
/* this MUST be initialized to zero for midnight/noon/teatime */
Specials = VariousWords; /* initialize special words context */
panic(e("the specified time is incorrect (out of range?)"));
}
EnsureMemFree();
+ /* ok done ... drop the mutex lock */
+ mutex_unlock(&parsetime_mutex);
+
return TIME_OK;
} /* rrd_parsetime */
#include "rrd_xport.h"
#include "unused.h"
#include "rrd_client.h"
+#include "optparse.h"
#if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__CYGWIN32__)
#include <io.h>
time_t start_tmp = 0, end_tmp = 0;
rrd_time_value_t start_tv, end_tv;
char *parsetime_error = NULL;
-
- struct option long_options[] = {
- {"start", required_argument, 0, 's'},
- {"end", required_argument, 0, 'e'},
- {"maxrows", required_argument, 0, 'm'},
- {"step", required_argument, 0, 261},
- {"enumds", no_argument, 0, 262}, /* these are handled in the frontend ... */
- {"json", no_argument, 0, 263}, /* these are handled in the frontend ... */
- {"daemon", required_argument, 0, 'd'},
- {0, 0, 0, 0}
+ struct optparse options;
+ optparse_init(&options, argv);
+
+ struct optparse_long longopts[] = {
+ {"start", 's', OPTPARSE_REQUIRED},
+ {"end", 'e', OPTPARSE_REQUIRED},
+ {"maxrows",'m', OPTPARSE_REQUIRED},
+ {"step", 261, OPTPARSE_REQUIRED},
+ {"enumds", 262, OPTPARSE_NONE}, /* these are handled in the frontend ... */
+ {"json", 263, OPTPARSE_NONE}, /* these are handled in the frontend ... */
+ {"daemon", 'd', OPTPARSE_REQUIRED},
+ {0}
};
- optind = 0;
- opterr = 0; /* initialize getopt */
-
rrd_graph_init(&im);
rrd_parsetime("end-24h", &start_tv);
rrd_parsetime("now", &end_tv);
- while (1) {
- int option_index = 0;
- int opt;
- opt = getopt_long(argc, argv, "s:e:m:d:", long_options, &option_index);
- if (opt == EOF)
- break;
+
+ int opt;
+ while ((opt = optparse_long(&options,longopts,NULL)) != -1){
switch (opt) {
case 261:
- im.step = atoi(optarg);
- break;
- case 262:
+ im.step = atoi(options.optarg);
break;
case 's':
- if ((parsetime_error = rrd_parsetime(optarg, &start_tv))) {
+ if ((parsetime_error = rrd_parsetime(options.optarg, &start_tv))) {
rrd_set_error("start time: %s", parsetime_error);
return -1;
}
break;
case 'e':
- if ((parsetime_error = rrd_parsetime(optarg, &end_tv))) {
+ if ((parsetime_error = rrd_parsetime(options.optarg, &end_tv))) {
rrd_set_error("end time: %s", parsetime_error);
return -1;
}
break;
case 'm':
- im.xsize = atol(optarg);
+ im.xsize = atol(options.optarg);
if (im.xsize < 10) {
rrd_set_error("maxrows below 10 rows");
return -1;
return (-1);
}
- im.daemon_addr = strdup(optarg);
+ im.daemon_addr = strdup(options.optarg);
if (im.daemon_addr == NULL)
{
rrd_set_error("strdup error");
}
case '?':
- rrd_set_error("unknown option '%s'", argv[optind - 1]);
+ rrd_set_error("%s: %s", argv[0], options.errmsg);
return -1;
}
}
im.end = end_tmp;
im.step = max((long) im.step, (im.end - im.start) / im.xsize);
- rrd_graph_script(argc, argv, &im, 0);
+ rrd_graph_script(argc, argv, &im, options.optind);
if (rrd_test_error()) {
im_free(&im);
return -1;