]> git.ipfire.org Git - thirdparty/rrdtool-1.x.git/commitdiff
trheadsafe extension
authorTobias Oetiker <tobi@oetiker.ch>
Tue, 30 Jun 2015 08:46:15 +0000 (10:46 +0200)
committerTobias Oetiker <tobi@oetiker.ch>
Tue, 30 Jun 2015 08:46:15 +0000 (10:46 +0200)
configure.ac
m4/acinclude.m4
src/Makefile.am
src/mutex.c [new file with mode: 0644]
src/mutex.h [new file with mode: 0644]
src/optparse.c [new file with mode: 0644]
src/optparse.h [new file with mode: 0644]
src/rrd_graph.c
src/rrd_graph_helper.c
src/rrd_parsetime.c
src/rrd_xport.c

index 059ca8b19e6e4c02c5b24a5b1324090e4c3e44ee..8f97436511fee5a2790669f485b11d1668f9bfbc 100644 (file)
@@ -525,6 +525,11 @@ AM_CONDITIONAL(BUILD_RRDGRAPH,[test $enable_rrd_graph != no])
 
 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/, "")
@@ -537,7 +542,6 @@ EX_CHECK_ALL(cairo,      cairo_ps_surface_create,       cairo-ps.h,
 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
index 6aed223893471dd35fb1a7d0d1d53075807e590f..5eff6de64c2ce8a137e2a20ed2bfc402a5804916 100644 (file)
@@ -35,6 +35,7 @@ AC_DEFUN([EX_CHECK_ALL],
              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,[
index f0a544853135b1c207513caff39a309e537f32c4..da65ab76ae95207120b6c5232ea22e65b3aabbca 100644 (file)
@@ -31,6 +31,7 @@ UPD_C_FILES =         \
        rrd_nan_inf.c   \
        rrd_rpncalc.c   \
        rrd_utils.c     \
+       mutex.c         \
        rrd_update.c
 
 RRD_C_FILES =          \
@@ -48,7 +49,7 @@ 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
@@ -56,9 +57,10 @@ 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
@@ -104,7 +106,7 @@ bin_PROGRAMS += rrdcgi
 endif
 
 rrdcgi_SOURCES = rrd_cgi.c
-rrdcgi_LDADD   = librrd.la
+rrdcgi_LDADD   = librrd.la 
 
 rrdupdate_SOURCES = rrdupdate.c
 rrdupdate_LDADD          = librrdupd.la
@@ -116,7 +118,7 @@ rrdtool_LDADD       = librrd.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
diff --git a/src/mutex.c b/src/mutex.c
new file mode 100644 (file)
index 0000000..c359b6f
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ *
+ * 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 :
+ */
+
diff --git a/src/mutex.h b/src/mutex.h
new file mode 100644 (file)
index 0000000..17664db
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ *  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 :
+ */
+
diff --git a/src/optparse.c b/src/optparse.c
new file mode 100644 (file)
index 0000000..8889b94
--- /dev/null
@@ -0,0 +1,257 @@
+#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 '?';
+}
diff --git a/src/optparse.h b/src/optparse.h
new file mode 100644 (file)
index 0000000..3e70dc0
--- /dev/null
@@ -0,0 +1,102 @@
+#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
index d783a358609d68465555b4e31ff6a10485a9ab21..4a0f246c6862e26e12554d797485b3c012df3fa6 100644 (file)
@@ -3995,7 +3995,7 @@ rrd_info_t *rrd_graph_v(
         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()) {
index e7ec902292ac95ebc30aa1e7f5211caccb0cf464..8ea141a56f5736d603258f7a7cd9e1d0c643854b 100644 (file)
@@ -1090,7 +1090,7 @@ void rrd_graph_script(
     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;
 
index a5f1db8910bdc4755869de1cbcbf01c11fa34122..9162884fa215dea2aa8bd8623227fa19cae6de4c 100644 (file)
 /* System Headers */
 
 /* Local headers */
-
+#include "mutex.h"
 #include <stdarg.h>
 #include <stdlib.h>
 #include <ctype.h>
@@ -837,9 +837,14 @@ char     *rrd_parsetime(
     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 */
@@ -990,6 +995,9 @@ char     *rrd_parsetime(
             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 */
 
index 7708be6e084baad327d3fc245068c42f234503c8..2c15325815d85ecd0b5fde9203a9f09b5c600449 100644 (file)
@@ -11,6 +11,7 @@
 #include "rrd_xport.h"
 #include "unused.h"
 #include "rrd_client.h"
+#include "optparse.h"
 
 #if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__CYGWIN32__)
 #include <io.h>
@@ -58,55 +59,49 @@ int rrd_xport(
     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;
@@ -121,7 +116,7 @@ int rrd_xport(
                 return (-1);
             }
 
-            im.daemon_addr = strdup(optarg);
+            im.daemon_addr = strdup(options.optarg);
             if (im.daemon_addr == NULL)
             {
                 rrd_set_error("strdup error");
@@ -131,7 +126,7 @@ int rrd_xport(
         }
 
         case '?':
-            rrd_set_error("unknown option '%s'", argv[optind - 1]);
+            rrd_set_error("%s: %s", argv[0], options.errmsg);
             return -1;
         }
     }
@@ -156,7 +151,7 @@ int rrd_xport(
     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;