summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/CMakeLists.txt1
-rw-r--r--src/config.c315
-rw-r--r--src/fastd.h1
-rw-r--r--src/options.c347
4 files changed, 350 insertions, 314 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index d527006..cd0b5b5 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -21,6 +21,7 @@ add_executable(fastd
crypto.c
crypto_linux.c
handshake.c
+ options.c
peer.c
printf.c
queue.c
diff --git a/src/config.c b/src/config.c
index 6296209..0decec8 100644
--- a/src/config.c
+++ b/src/config.c
@@ -36,8 +36,6 @@
#include <stdarg.h>
#include <strings.h>
-#include <arpa/inet.h>
-
#include <sys/stat.h>
#include <sys/types.h>
@@ -126,25 +124,6 @@ static void default_config(fastd_config_t *conf) {
conf->peer_group->max_connections = -1;
}
-static bool config_match(const char *opt, ...) {
- va_list ap;
- bool match = false;
- const char *str;
-
- va_start(ap, opt);
-
- while((str = va_arg(ap, const char*)) != NULL) {
- if (strcmp(opt, str) == 0) {
- match = true;
- break;
- }
- }
-
- va_end(ap);
-
- return match;
-}
-
bool fastd_config_protocol(fastd_context_t *ctx, fastd_config_t *conf, const char *name) {
if (!strcmp(name, "ec25519-fhmqvc"))
conf->protocol = &fastd_protocol_ec25519_fhmqvc;
@@ -522,271 +501,6 @@ static void assess_peers(fastd_context_t *ctx, fastd_config_t *conf) {
}
}
-static void print_usage(const char *options, const char *message) {
- /* 28 spaces */
- static const char spaces[] = " ";
-
- int len = strlen(options);
-
- printf("%s", options);
-
- if (len < 28)
- printf("%s", spaces+len);
- else
- printf("\n%s", spaces);
-
- puts(message);
-}
-
-static void usage(fastd_context_t *ctx, fastd_config_t *conf) {
- puts("fastd (Fast and Secure Tunnelling Daemon) " FASTD_VERSION " usage:\n");
-
-#define OR ", "
-#define OPTION(func, options, message) print_usage(" " options, message)
-#define OPTION_ARG(func, options, arg, message) print_usage(" " options " " arg, message)
-#include "options.def.h"
-#undef OR
-#undef OPTION
-#undef OPTION_ARG
-
- exit(0);
-}
-
-static void version(fastd_context_t *ctx, fastd_config_t *conf) {
- puts("fastd " FASTD_VERSION);
- exit(0);
-}
-
-static int parse_log_level(fastd_context_t *ctx, const char *arg) {
- if (!strcmp(arg, "fatal"))
- return LOG_CRIT;
- else if (!strcmp(arg, "error"))
- return LOG_ERR;
- else if (!strcmp(arg, "warn"))
- return LOG_WARNING;
- else if (!strcmp(arg, "info"))
- return LOG_NOTICE;
- else if (!strcmp(arg, "verbose"))
- return LOG_INFO;
- else if (!strcmp(arg, "debug"))
- return LOG_DEBUG;
- else
- exit_error(ctx, "invalid log level `%s'", arg);
-}
-
-
-
-static void option_user(fastd_context_t *ctx, fastd_config_t *conf, const char *arg) {
- free(conf->user);
- conf->user = strdup(arg);
-}
-
-static void option_group(fastd_context_t *ctx, fastd_config_t *conf, const char *arg) {
- free(conf->group);
- conf->group = strdup(arg);
-}
-
-static void option_log_level(fastd_context_t *ctx, fastd_config_t *conf, const char *arg) {
- conf->log_stderr_level = parse_log_level(ctx, arg);
-}
-
-static void option_syslog_level(fastd_context_t *ctx, fastd_config_t *conf, const char *arg) {
- conf->log_syslog_level = parse_log_level(ctx, arg);
-}
-
-static void option_syslog_ident(fastd_context_t *ctx, fastd_config_t *conf, const char *arg) {
- free(conf->log_syslog_ident);
- conf->log_syslog_ident = strdup(arg);
-}
-
-static void option_hide_ip_addresses(fastd_context_t *ctx, fastd_config_t *conf) {
- conf->hide_ip_addresses = true;
-}
-
-static void option_hide_mac_addresses(fastd_context_t *ctx, fastd_config_t *conf) {
- conf->hide_mac_addresses = true;
-}
-
-static void option_config(fastd_context_t *ctx, fastd_config_t *conf, const char *arg) {
- if (!strcmp(arg, "-"))
- arg = NULL;
-
- if (!fastd_read_config(ctx, conf, arg, false, 0))
- exit(1);
-}
-
-static void option_config_peer(fastd_context_t *ctx, fastd_config_t *conf, const char *arg) {
- fastd_peer_config_new(ctx, conf);
-
- if(!fastd_read_config(ctx, conf, arg, true, 0))
- exit(1);
-}
-
-static void option_config_peer_dir(fastd_context_t *ctx, fastd_config_t *conf, const char *arg) {
- fastd_add_peer_dir(ctx, conf, arg);
-}
-
-static void option_mode(fastd_context_t *ctx, fastd_config_t *conf, const char *arg) {
- if (!strcmp(arg, "tap"))
- conf->mode = MODE_TAP;
- else if (!strcmp(arg, "tun"))
- conf->mode = MODE_TUN;
- else
- exit_error(ctx, "invalid mode `%s'", arg);
-}
-
-static void option_interface(fastd_context_t *ctx, fastd_config_t *conf, const char *arg) {
- free(conf->ifname);
- conf->ifname = strdup(arg);
-}
-
-static void option_mtu(fastd_context_t *ctx, fastd_config_t *conf, const char *arg) {
- char *endptr;
-
- conf->mtu = strtol(arg, &endptr, 10);
- if (*endptr || conf->mtu < 576)
- exit_error(ctx, "invalid mtu `%s'", arg);
-}
-
-static void option_bind(fastd_context_t *ctx, fastd_config_t *conf, const char *arg) {
- long l;
- char *charptr;
- char *endptr;
- char *addrstr;
-
- if (arg[0] == '[') {
- charptr = strchr(arg, ']');
- if (!charptr || (charptr[1] != ':' && charptr[1] != '\0'))
- exit_error(ctx, "invalid bind address `%s'", arg);
-
- addrstr = strndup(arg+1, charptr-arg-1);
-
- if (charptr[1] == ':')
- charptr++;
- else
- charptr = NULL;
- }
- else {
- charptr = strchr(arg, ':');
- if (charptr) {
- addrstr = strndup(arg, charptr-arg);
- }
- else {
- addrstr = strdup(arg);
- }
- }
-
- if (charptr) {
- l = strtol(charptr+1, &endptr, 10);
- if (*endptr || l < 0 || l > 65535)
- exit_error(ctx, "invalid bind port `%s'", charptr+1);
- }
- else {
- l = 0;
- }
-
- fastd_peer_address_t addr = {};
-
- if (strcmp(addrstr, "any") == 0) {
- /* nothing to do */
- }
- else if (arg[0] == '[') {
- addr.in6.sin6_family = AF_INET6;
- addr.in6.sin6_port = htons(l);
-
- if (inet_pton(AF_INET6, addrstr, &addr.in6.sin6_addr) != 1)
- exit_error(ctx, "invalid bind address `%s'", addrstr);
- }
- else {
- addr.in.sin_family = AF_INET;
- addr.in.sin_port = htons(l);
-
- if (inet_pton(AF_INET, addrstr, &addr.in.sin_addr) != 1)
- exit_error(ctx, "invalid bind address `%s'", addrstr);
- }
-
- free(addrstr);
-
- fastd_config_bind_address(ctx, conf, &addr, NULL, false, false);
-}
-
-static void option_protocol(fastd_context_t *ctx, fastd_config_t *conf, const char *arg) {
- if (!fastd_config_protocol(ctx, conf, arg))
- exit_error(ctx, "invalid protocol `%s'", arg);
-}
-
-static void option_method(fastd_context_t *ctx, fastd_config_t *conf, const char *arg) {
- if (!fastd_config_method(ctx, conf, arg))
- exit_error(ctx, "invalid method `%s'", arg);
-}
-
-static void option_forward(fastd_context_t *ctx, fastd_config_t *conf) {
- conf->forward = true;
-}
-
-static void option_on_up(fastd_context_t *ctx, fastd_config_t *conf, const char *arg) {
- free(conf->on_up);
- free(conf->on_up_dir);
-
- conf->on_up = strdup(arg);
- conf->on_up_dir = get_current_dir_name();
-}
-
-static void option_on_down(fastd_context_t *ctx, fastd_config_t *conf, const char *arg) {
- free(conf->on_down);
- free(conf->on_down_dir);
-
- conf->on_down = strdup(arg);
- conf->on_down_dir = get_current_dir_name();
-}
-
-static void option_on_establish(fastd_context_t *ctx, fastd_config_t *conf, const char *arg) {
- free(conf->on_establish);
- free(conf->on_establish_dir);
-
- conf->on_establish = strdup(arg);
- conf->on_establish_dir = get_current_dir_name();
-}
-
-static void option_on_disestablish(fastd_context_t *ctx, fastd_config_t *conf, const char *arg) {
- free(conf->on_disestablish);
- free(conf->on_disestablish_dir);
-
- conf->on_disestablish = strdup(arg);
- conf->on_disestablish_dir = get_current_dir_name();
-}
-
-static void option_on_verify(fastd_context_t *ctx, fastd_config_t *conf, const char *arg) {
- free(conf->on_verify);
- free(conf->on_verify_dir);
-
- conf->on_verify = strdup(arg);
- conf->on_verify_dir = get_current_dir_name();
-}
-
-static void option_daemon(fastd_context_t *ctx, fastd_config_t *conf) {
- conf->daemon = true;
-}
-
-static void option_pid_file(fastd_context_t *ctx, fastd_config_t *conf, const char *arg) {
- free(conf->pid_file);
- conf->pid_file = strdup(arg);
-}
-
-static void option_generate_key(fastd_context_t *ctx, fastd_config_t *conf) {
- conf->generate_key = true;
- conf->show_key = false;
-}
-
-static void option_show_key(fastd_context_t *ctx, fastd_config_t *conf) {
- conf->generate_key = false;
- conf->show_key = true;
-}
-
-static void option_machine_readable(fastd_context_t *ctx, fastd_config_t *conf) {
- conf->machine_readable = true;
-}
-
static void configure_user(fastd_context_t *ctx, fastd_config_t *conf) {
conf->uid = getuid();
@@ -875,34 +589,7 @@ static void configure_method_parameters(fastd_context_t *ctx, fastd_config_t *co
void fastd_configure(fastd_context_t *ctx, fastd_config_t *conf, int argc, char *const argv[]) {
default_config(conf);
- int i = 1;
- while (i < argc) {
-#define OR ,
-#define OPTION(func, options, message) \
- ({ \
- if(config_match(argv[i], options, NULL)) { \
- i++; \
- func(ctx, conf); \
- continue; \
- } \
- })
-#define OPTION_ARG(func, options, arg, message) \
- ({ \
- if(config_match(argv[i], options, NULL)) { \
- i+=2; \
- if (i > argc) \
- exit_error(ctx, "config error: option `%s' needs an argument; see --help for usage", argv[i-2]); \
- func(ctx, conf, argv[i-1]); \
- continue; \
- } \
- })
-#include "options.def.h"
-#undef OR
-#undef OPTION
-#undef OPTION_ARG
-
- exit_error(ctx, "config error: unknown option `%s'; see --help for usage", argv[i]);
- }
+ fastd_config_handle_options(ctx, conf, argc, argv);
if (conf->log_stderr_level < 0 && conf->log_syslog_level < 0 && !conf->log_files)
conf->log_stderr_level = FASTD_DEFAULT_LOG_LEVEL;
diff --git a/src/fastd.h b/src/fastd.h
index ba74330..75335e9 100644
--- a/src/fastd.h
+++ b/src/fastd.h
@@ -341,6 +341,7 @@ void fastd_config_peer_group_pop(fastd_context_t *ctx, fastd_config_t *conf);
void fastd_config_release(fastd_context_t *ctx, fastd_config_t *conf);
void fastd_configure(fastd_context_t *ctx, fastd_config_t *conf, int argc, char *const argv[]);
void fastd_config_load_peer_dirs(fastd_context_t *ctx, fastd_config_t *conf);
+void fastd_config_handle_options(fastd_context_t *ctx, fastd_config_t *conf, int argc, char *const argv[]);
void fastd_cap_init(fastd_context_t *ctx);
void fastd_cap_drop(fastd_context_t *ctx);
diff --git a/src/options.c b/src/options.c
new file mode 100644
index 0000000..aba6bd6
--- /dev/null
+++ b/src/options.c
@@ -0,0 +1,347 @@
+/*
+ Copyright (c) 2012-2013, Matthias Schiffer <mschiffer@universe-factory.net>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#include "fastd.h"
+#include "peer.h"
+
+#include <arpa/inet.h>
+
+
+static int parse_log_level(fastd_context_t *ctx, const char *arg) {
+ if (!strcmp(arg, "fatal"))
+ return LOG_CRIT;
+ else if (!strcmp(arg, "error"))
+ return LOG_ERR;
+ else if (!strcmp(arg, "warn"))
+ return LOG_WARNING;
+ else if (!strcmp(arg, "info"))
+ return LOG_NOTICE;
+ else if (!strcmp(arg, "verbose"))
+ return LOG_INFO;
+ else if (!strcmp(arg, "debug"))
+ return LOG_DEBUG;
+ else
+ exit_error(ctx, "invalid log level `%s'", arg);
+}
+
+
+static void print_usage(const char *options, const char *message) {
+ /* 28 spaces */
+ static const char spaces[] = " ";
+
+ int len = strlen(options);
+
+ printf("%s", options);
+
+ if (len < 28)
+ printf("%s", spaces+len);
+ else
+ printf("\n%s", spaces);
+
+ puts(message);
+}
+
+static void usage(fastd_context_t *ctx, fastd_config_t *conf) {
+ puts("fastd (Fast and Secure Tunnelling Daemon) " FASTD_VERSION " usage:\n");
+
+#define OR ", "
+#define OPTION(func, options, message) print_usage(" " options, message)
+#define OPTION_ARG(func, options, arg, message) print_usage(" " options " " arg, message)
+#include "options.def.h"
+#undef OR
+#undef OPTION
+#undef OPTION_ARG
+
+ exit(0);
+}
+
+static void version(fastd_context_t *ctx, fastd_config_t *conf) {
+ puts("fastd " FASTD_VERSION);
+ exit(0);
+}
+
+static void option_user(fastd_context_t *ctx, fastd_config_t *conf, const char *arg) {
+ free(conf->user);
+ conf->user = strdup(arg);
+}
+
+static void option_group(fastd_context_t *ctx, fastd_config_t *conf, const char *arg) {
+ free(conf->group);
+ conf->group = strdup(arg);
+}
+
+static void option_log_level(fastd_context_t *ctx, fastd_config_t *conf, const char *arg) {
+ conf->log_stderr_level = parse_log_level(ctx, arg);
+}
+
+static void option_syslog_level(fastd_context_t *ctx, fastd_config_t *conf, const char *arg) {
+ conf->log_syslog_level = parse_log_level(ctx, arg);
+}
+
+static void option_syslog_ident(fastd_context_t *ctx, fastd_config_t *conf, const char *arg) {
+ free(conf->log_syslog_ident);
+ conf->log_syslog_ident = strdup(arg);
+}
+
+static void option_hide_ip_addresses(fastd_context_t *ctx, fastd_config_t *conf) {
+ conf->hide_ip_addresses = true;
+}
+
+static void option_hide_mac_addresses(fastd_context_t *ctx, fastd_config_t *conf) {
+ conf->hide_mac_addresses = true;
+}
+
+static void option_config(fastd_context_t *ctx, fastd_config_t *conf, const char *arg) {
+ if (!strcmp(arg, "-"))
+ arg = NULL;
+
+ if (!fastd_read_config(ctx, conf, arg, false, 0))
+ exit(1);
+}
+
+static void option_config_peer(fastd_context_t *ctx, fastd_config_t *conf, const char *arg) {
+ fastd_peer_config_new(ctx, conf);
+
+ if(!fastd_read_config(ctx, conf, arg, true, 0))
+ exit(1);
+}
+
+static void option_config_peer_dir(fastd_context_t *ctx, fastd_config_t *conf, const char *arg) {
+ fastd_add_peer_dir(ctx, conf, arg);
+}
+
+static void option_mode(fastd_context_t *ctx, fastd_config_t *conf, const char *arg) {
+ if (!strcmp(arg, "tap"))
+ conf->mode = MODE_TAP;
+ else if (!strcmp(arg, "tun"))
+ conf->mode = MODE_TUN;
+ else
+ exit_error(ctx, "invalid mode `%s'", arg);
+}
+
+static void option_interface(fastd_context_t *ctx, fastd_config_t *conf, const char *arg) {
+ free(conf->ifname);
+ conf->ifname = strdup(arg);
+}
+
+static void option_mtu(fastd_context_t *ctx, fastd_config_t *conf, const char *arg) {
+ char *endptr;
+
+ conf->mtu = strtol(arg, &endptr, 10);
+ if (*endptr || conf->mtu < 576)
+ exit_error(ctx, "invalid mtu `%s'", arg);
+}
+
+static void option_bind(fastd_context_t *ctx, fastd_config_t *conf, const char *arg) {
+ long l;
+ char *charptr;
+ char *endptr;
+ char *addrstr;
+
+ if (arg[0] == '[') {
+ charptr = strchr(arg, ']');
+ if (!charptr || (charptr[1] != ':' && charptr[1] != '\0'))
+ exit_error(ctx, "invalid bind address `%s'", arg);
+
+ addrstr = strndup(arg+1, charptr-arg-1);
+
+ if (charptr[1] == ':')
+ charptr++;
+ else
+ charptr = NULL;
+ }
+ else {
+ charptr = strchr(arg, ':');
+ if (charptr) {
+ addrstr = strndup(arg, charptr-arg);
+ }
+ else {
+ addrstr = strdup(arg);
+ }
+ }
+
+ if (charptr) {
+ l = strtol(charptr+1, &endptr, 10);
+ if (*endptr || l < 0 || l > 65535)
+ exit_error(ctx, "invalid bind port `%s'", charptr+1);
+ }
+ else {
+ l = 0;
+ }
+
+ fastd_peer_address_t addr = {};
+
+ if (strcmp(addrstr, "any") == 0) {
+ /* nothing to do */
+ }
+ else if (arg[0] == '[') {
+ addr.in6.sin6_family = AF_INET6;
+ addr.in6.sin6_port = htons(l);
+
+ if (inet_pton(AF_INET6, addrstr, &addr.in6.sin6_addr) != 1)
+ exit_error(ctx, "invalid bind address `%s'", addrstr);
+ }
+ else {
+ addr.in.sin_family = AF_INET;
+ addr.in.sin_port = htons(l);
+
+ if (inet_pton(AF_INET, addrstr, &addr.in.sin_addr) != 1)
+ exit_error(ctx, "invalid bind address `%s'", addrstr);
+ }
+
+ free(addrstr);
+
+ fastd_config_bind_address(ctx, conf, &addr, NULL, false, false);
+}
+
+static void option_protocol(fastd_context_t *ctx, fastd_config_t *conf, const char *arg) {
+ if (!fastd_config_protocol(ctx, conf, arg))
+ exit_error(ctx, "invalid protocol `%s'", arg);
+}
+
+static void option_method(fastd_context_t *ctx, fastd_config_t *conf, const char *arg) {
+ if (!fastd_config_method(ctx, conf, arg))
+ exit_error(ctx, "invalid method `%s'", arg);
+}
+
+static void option_forward(fastd_context_t *ctx, fastd_config_t *conf) {
+ conf->forward = true;
+}
+
+static void option_on_up(fastd_context_t *ctx, fastd_config_t *conf, const char *arg) {
+ free(conf->on_up);
+ free(conf->on_up_dir);
+
+ conf->on_up = strdup(arg);
+ conf->on_up_dir = get_current_dir_name();
+}
+
+static void option_on_down(fastd_context_t *ctx, fastd_config_t *conf, const char *arg) {
+ free(conf->on_down);
+ free(conf->on_down_dir);
+
+ conf->on_down = strdup(arg);
+ conf->on_down_dir = get_current_dir_name();
+}
+
+static void option_on_establish(fastd_context_t *ctx, fastd_config_t *conf, const char *arg) {
+ free(conf->on_establish);
+ free(conf->on_establish_dir);
+
+ conf->on_establish = strdup(arg);
+ conf->on_establish_dir = get_current_dir_name();
+}
+
+static void option_on_disestablish(fastd_context_t *ctx, fastd_config_t *conf, const char *arg) {
+ free(conf->on_disestablish);
+ free(conf->on_disestablish_dir);
+
+ conf->on_disestablish = strdup(arg);
+ conf->on_disestablish_dir = get_current_dir_name();
+}
+
+static void option_on_verify(fastd_context_t *ctx, fastd_config_t *conf, const char *arg) {
+ free(conf->on_verify);
+ free(conf->on_verify_dir);
+
+ conf->on_verify = strdup(arg);
+ conf->on_verify_dir = get_current_dir_name();
+}
+
+static void option_daemon(fastd_context_t *ctx, fastd_config_t *conf) {
+ conf->daemon = true;
+}
+
+static void option_pid_file(fastd_context_t *ctx, fastd_config_t *conf, const char *arg) {
+ free(conf->pid_file);
+ conf->pid_file = strdup(arg);
+}
+
+static void option_generate_key(fastd_context_t *ctx, fastd_config_t *conf) {
+ conf->generate_key = true;
+ conf->show_key = false;
+}
+
+static void option_show_key(fastd_context_t *ctx, fastd_config_t *conf) {
+ conf->generate_key = false;
+ conf->show_key = true;
+}
+
+static void option_machine_readable(fastd_context_t *ctx, fastd_config_t *conf) {
+ conf->machine_readable = true;
+}
+
+
+static bool config_match(const char *opt, ...) {
+ va_list ap;
+ bool match = false;
+ const char *str;
+
+ va_start(ap, opt);
+
+ while((str = va_arg(ap, const char*)) != NULL) {
+ if (strcmp(opt, str) == 0) {
+ match = true;
+ break;
+ }
+ }
+
+ va_end(ap);
+
+ return match;
+}
+
+void fastd_config_handle_options(fastd_context_t *ctx, fastd_config_t *conf, int argc, char *const argv[]) {
+ int i = 1;
+
+ while (i < argc) {
+#define OR ,
+#define OPTION(func, options, message) \
+ ({ \
+ if(config_match(argv[i], options, NULL)) { \
+ i++; \
+ func(ctx, conf); \
+ continue; \
+ } \
+ })
+#define OPTION_ARG(func, options, arg, message) \
+ ({ \
+ if(config_match(argv[i], options, NULL)) { \
+ i+=2; \
+ if (i > argc) \
+ exit_error(ctx, "config error: option `%s' needs an argument; see --help for usage", argv[i-2]); \
+ func(ctx, conf, argv[i-1]); \
+ continue; \
+ } \
+ })
+#include "options.def.h"
+#undef OR
+#undef OPTION
+#undef OPTION_ARG
+
+ exit_error(ctx, "config error: unknown option `%s'; see --help for usage", argv[i]);
+ }
+}