diff --git a/Makefile b/Makefile index 6ce44b3..6762eec 100644 --- a/Makefile +++ b/Makefile @@ -39,7 +39,11 @@ LIBIFUPDOWN_SRC = \ LIBIFUPDOWN_OBJ = ${LIBIFUPDOWN_SRC:.c=.o} LIBIFUPDOWN_LIB = libifupdown.a -MULTICALL_SRC = cmd/multicall.c +MULTICALL_SRC = \ + cmd/multicall.c \ + cmd/multicall-options.c \ + cmd/multicall-exec-options.c \ + cmd/multicall-match-options.c MULTICALL_OBJ = ${MULTICALL_SRC:.c=.o} MULTICALL = ifupdown diff --git a/cmd/ifquery.c b/cmd/ifquery.c index 2778dea..0612823 100644 --- a/cmd/ifquery.c +++ b/cmd/ifquery.c @@ -21,11 +21,6 @@ #include "libifupdown/libifupdown.h" #include "cmd/multicall.h" -static struct lif_execute_opts exec_opts = { - .interfaces_file = INTERFACES_FILE, - .executor_path = EXECUTOR_PATH -}; - void print_interface(struct lif_interface *iface) { @@ -163,38 +158,6 @@ print_interface_property(struct lif_interface *iface, const char *property) } } -void -ifquery_usage(int status) -{ - fprintf(stderr, "usage: ifquery [options] \n"); - fprintf(stderr, " ifquery [options] --list\n"); - - fprintf(stderr, "\nOptions:\n"); - fprintf(stderr, " -h, --help this help\n"); - fprintf(stderr, " -V, --version show this program's version\n"); - fprintf(stderr, " -i, --interfaces FILE use FILE for interface definitions\n"); - fprintf(stderr, " -L, --list list matching interfaces\n"); - fprintf(stderr, " -a, --auto only match against interfaces hinted as 'auto'\n"); - fprintf(stderr, " -I, --include PATTERN only match against interfaces matching PATTERN\n"); - fprintf(stderr, " -X, --exclude PATTERN never match against interfaces matching PATTERN\n"); - fprintf(stderr, " -P, --pretty-print pretty print the interfaces instead of just listing\n"); - fprintf(stderr, " -S, --state-file FILE use FILE for state\n"); - fprintf(stderr, " -s, --state show configured state\n"); - fprintf(stderr, " -D, --dot generate a dependency graph\n"); - fprintf(stderr, " -p, --property PROPERTY print values of properties matching PROPERTY\n"); - - exit(status); -} - -struct match_options { - bool is_auto; - char *exclude_pattern; - char *include_pattern; - bool pretty_print; - bool dot; - char *property; -}; - void list_interfaces(struct lif_dict *collection, struct match_options *opts) { @@ -256,83 +219,56 @@ list_state(struct lif_dict *state, struct match_options *opts) } } +static bool listing = false, listing_stat = false; + +static void +handle_local(int short_opt, const struct if_option *opt, const char *opt_arg, const struct if_applet *applet) +{ + (void) opt; + (void) applet; + + switch (short_opt) { + case 'L': + listing = true; + break; + case 'P': + match_opts.pretty_print = true; + break; + case 's': + listing_stat = true; + break; + case 'D': + match_opts.dot = true; + break; + case 'p': + match_opts.property = opt_arg; + break; + } +} + +static struct if_option local_options[] = { + {'s', "state", NULL, "show configured state", false, handle_local}, + {'p', "property", "property PROPERTY", "print values of properties matching PROPERTY", true, handle_local}, + {'D', "dot", NULL, "generate a dependency graph", false, handle_local}, + {'L', "list", NULL, "list matching interfaces", false, handle_local}, + {'P', "pretty-print", NULL, "pretty print the interfaces instead of just listing", false, handle_local}, +}; + +static struct if_option_group local_option_group = { + .desc = "Program-specific options", + .group_size = ARRAY_SIZE(local_options), + .group = local_options +}; + int ifquery_main(int argc, char *argv[]) { struct lif_dict state = {}; struct lif_dict collection = {}; - struct option long_options[] = { - {"help", no_argument, 0, 'h'}, - {"version", no_argument, 0, 'V'}, - {"interfaces", required_argument, 0, 'i'}, - {"list", no_argument, 0, 'L'}, - {"auto", no_argument, 0, 'a'}, - {"include", required_argument, 0, 'I'}, - {"exclude", required_argument, 0, 'X'}, - {"pretty-print", no_argument, 0, 'P'}, - {"state-file", required_argument, 0, 'S'}, - {"state", no_argument, 0, 's'}, - {"dot", no_argument, 0, 'D'}, - {"property", required_argument, 0, 'p'}, - {"executor-path", required_argument, 0, 'E'}, - {NULL, 0, 0, 0} - }; - struct match_options match_opts = {}; - bool listing = false, listing_stat = false; - char *state_file = STATE_FILE; - for (;;) + if (!lif_state_read_path(&state, exec_opts.state_file)) { - int c = getopt_long(argc, argv, "hVi:LaI:X:PS:sDp:E:", long_options, NULL); - if (c == -1) - break; - - switch (c) { - case 'h': - ifquery_usage(EXIT_SUCCESS); - break; - case 'V': - lif_common_version(); - break; - case 'i': - exec_opts.interfaces_file = optarg; - break; - case 'L': - listing = true; - break; - case 'a': - match_opts.is_auto = true; - break; - case 'I': - match_opts.include_pattern = optarg; - break; - case 'X': - match_opts.exclude_pattern = optarg; - break; - case 'P': - match_opts.pretty_print = true; - break; - case 'S': - state_file = optarg; - break; - case 's': - listing_stat = true; - break; - case 'D': - match_opts.dot = true; - break; - case 'p': - match_opts.property = optarg; - break; - case 'E': - exec_opts.executor_path = optarg; - break; - } - } - - if (!lif_state_read_path(&state, state_file)) - { - fprintf(stderr, "%s: could not parse %s\n", argv0, state_file); + fprintf(stderr, "%s: could not parse %s\n", argv0, exec_opts.state_file); return EXIT_FAILURE; } @@ -344,7 +280,7 @@ ifquery_main(int argc, char *argv[]) /* --list --state is not allowed */ if (listing && listing_stat) - ifquery_usage(EXIT_FAILURE); + generic_usage(self_applet, EXIT_FAILURE); if (listing) { @@ -358,7 +294,7 @@ ifquery_main(int argc, char *argv[]) } if (optind >= argc) - ifquery_usage(EXIT_FAILURE); + generic_usage(self_applet, EXIT_FAILURE); int idx = optind; for (; idx < argc; idx++) @@ -390,6 +326,8 @@ ifquery_main(int argc, char *argv[]) struct if_applet ifquery_applet = { .name = "ifquery", + .desc = "query interface configuration", .main = ifquery_main, - .usage = ifquery_usage + .usage = "ifquery [options] \n ifquery [options] --list", + .groups = { &global_option_group, &match_option_group, &exec_option_group, &local_option_group }, }; diff --git a/cmd/ifupdown.c b/cmd/ifupdown.c index 9a9aada..c1bce3a 100644 --- a/cmd/ifupdown.c +++ b/cmd/ifupdown.c @@ -24,40 +24,7 @@ #include "libifupdown/libifupdown.h" #include "cmd/multicall.h" -struct match_options { - bool is_auto; - char *exclude_pattern; - char *include_pattern; -}; - static bool up; -static struct lif_execute_opts exec_opts = { - .executor_path = EXECUTOR_PATH, - .interfaces_file = INTERFACES_FILE, - .state_file = STATE_FILE, -}; - -void -ifupdown_usage(int status) -{ - fprintf(stderr, "usage: %s [options] \n", argv0); - - fprintf(stderr, "\nOptions:\n"); - fprintf(stderr, " -h, --help this help\n"); - fprintf(stderr, " -V, --version show this program's version\n"); - fprintf(stderr, " -i, --interfaces FILE use FILE for interface definitions\n"); - fprintf(stderr, " -S, --state-file FILE use FILE for state\n"); - fprintf(stderr, " -a, --auto only match against interfaces hinted as 'auto'\n"); - fprintf(stderr, " -I, --include PATTERN only match against interfaces matching PATTERN\n"); - fprintf(stderr, " -X, --exclude PATTERN never match against interfaces matching PATTERN\n"); - fprintf(stderr, " -n, --no-act do not actually run any commands\n"); - fprintf(stderr, " -v, --verbose show what commands are being run\n"); - fprintf(stderr, " -E, --executor-path PATH use PATH for executor directory\n"); - fprintf(stderr, " -f, --force force (de)configuration\n"); - fprintf(stderr, " -L, --no-lock do not use a lockfile to serialize state changes\n"); - - exit(status); -} bool is_ifdown() @@ -195,68 +162,6 @@ ifupdown_main(int argc, char *argv[]) struct lif_dict state = {}; struct lif_dict collection = {}; - struct option long_options[] = { - {"help", no_argument, 0, 'h'}, - {"version", no_argument, 0, 'V'}, - {"interfaces", required_argument, 0, 'i'}, - {"auto", no_argument, 0, 'a'}, - {"include", required_argument, 0, 'I'}, - {"exclude", required_argument, 0, 'X'}, - {"state-file", required_argument, 0, 'S'}, - {"no-act", no_argument, 0, 'n'}, - {"verbose", no_argument, 0, 'v'}, - {"executor-path", required_argument, 0, 'E'}, - {"force", no_argument, 0, 'f'}, - {"no-lock", no_argument, 0, 'L'}, - {NULL, 0, 0, 0} - }; - struct match_options match_opts = {}; - - for (;;) - { - int c = getopt_long(argc, argv, "hVi:aI:X:S:nvE:fL", long_options, NULL); - if (c == -1) - break; - - switch (c) { - case 'h': - ifupdown_usage(EXIT_SUCCESS); - break; - case 'V': - lif_common_version(); - break; - case 'i': - exec_opts.interfaces_file = optarg; - break; - case 'a': - match_opts.is_auto = true; - break; - case 'I': - match_opts.include_pattern = optarg; - break; - case 'X': - match_opts.exclude_pattern = optarg; - break; - case 'S': - exec_opts.state_file = optarg; - break; - case 'n': - exec_opts.mock = true; - exec_opts.verbose = true; - break; - case 'v': - exec_opts.verbose = true; - break; - case 'E': - exec_opts.executor_path = optarg; - break; - case 'f': - break; - case 'L': - exec_opts.no_lock = true; - break; - } - } if (!lif_state_read_path(&state, exec_opts.state_file)) { @@ -284,7 +189,7 @@ ifupdown_main(int argc, char *argv[]) return EXIT_SUCCESS; } else if (optind >= argc) - ifupdown_usage(EXIT_FAILURE); + generic_usage(self_applet, EXIT_FAILURE); int idx = optind; for (; idx < argc; idx++) @@ -331,12 +236,16 @@ ifupdown_main(int argc, char *argv[]) struct if_applet ifup_applet = { .name = "ifup", + .desc = "bring interfaces up", .main = ifupdown_main, - .usage = ifupdown_usage + .usage = "ifup [options] ", + .groups = { &global_option_group, &match_option_group, &exec_option_group, }, }; struct if_applet ifdown_applet = { .name = "ifdown", + .desc = "take interfaces down", .main = ifupdown_main, - .usage = ifupdown_usage + .usage = "ifdown [options] ", + .groups = { &global_option_group, &match_option_group, &exec_option_group, }, }; diff --git a/cmd/multicall-exec-options.c b/cmd/multicall-exec-options.c new file mode 100644 index 0000000..0ebe85d --- /dev/null +++ b/cmd/multicall-exec-options.c @@ -0,0 +1,77 @@ +/* + * cmd/multicall-exec-options.c + * Purpose: multi-call binary frontend -- option handling + * + * Copyright (c) 2020 Ariadne Conill + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * This software is provided 'as is' and without any warranty, express or + * implied. In no event shall the authors be liable for any damages arising + * from the use of this software. + */ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include "cmd/multicall.h" + +struct lif_execute_opts exec_opts = { + .interfaces_file = INTERFACES_FILE, + .executor_path = EXECUTOR_PATH, + .state_file = STATE_FILE +}; + +static void handle_exec(int short_opt, const struct if_option *opt, const char *opt_arg, const struct if_applet *applet) +{ + (void) opt; + (void) applet; + + switch (short_opt) + { + case 'i': + exec_opts.interfaces_file = opt_arg; + break; + case 'S': + exec_opts.state_file = opt_arg; + break; + case 'n': + exec_opts.mock = true; + exec_opts.verbose = true; + break; + case 'v': + exec_opts.verbose = true; + break; + case 'E': + exec_opts.executor_path = opt_arg; + break; + case 'f': + break; + case 'l': + exec_opts.no_lock = true; + break; + default: + break; + } +} + +static struct if_option exec_options[] = { + {'f', "force", NULL, "force (de)configuration", true, handle_exec}, + {'i', "interfaces", "interfaces FILE", "use FILE for interface definitions", true, handle_exec}, + {'l', "no-lock", NULL, "do not use a lockfile to serialize state changes", false, handle_exec}, + {'n', "no-act", NULL, "do not actually run any commands", false, handle_exec}, + {'v', "verbose", NULL, "show what commands are being run", false, handle_exec}, + {'E', "executor-path", "executor-path PATH", "use PATH for executor directory", true, handle_exec}, + {'S', "state-file", "state-file FILE", "use FILE for state", true, handle_exec}, +}; + +struct if_option_group exec_option_group = { + .desc = "Execution", + .group_size = ARRAY_SIZE(exec_options), + .group = exec_options +}; diff --git a/cmd/multicall-match-options.c b/cmd/multicall-match-options.c new file mode 100644 index 0000000..d89056a --- /dev/null +++ b/cmd/multicall-match-options.c @@ -0,0 +1,57 @@ +/* + * cmd/multicall-match-options.c + * Purpose: multi-call binary frontend -- option handling + * + * Copyright (c) 2020 Ariadne Conill + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * This software is provided 'as is' and without any warranty, express or + * implied. In no event shall the authors be liable for any damages arising + * from the use of this software. + */ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include "cmd/multicall.h" + +struct match_options match_opts = {}; + +static void handle_match(int short_opt, const struct if_option *opt, const char *opt_arg, const struct if_applet *applet) +{ + (void) opt; + (void) applet; + + switch (short_opt) + { + case 'a': + match_opts.is_auto = true; + break; + case 'I': + match_opts.include_pattern = opt_arg; + break; + case 'X': + match_opts.exclude_pattern = opt_arg; + break; + default: + break; + } +} + +static struct if_option match_options[] = { + {'a', "auto", NULL, "only match against interfaces hinted as 'auto'", false, handle_match}, + {'I', "include", "include PATTERN", "only match against interfaces matching PATTERN", true, handle_match}, + {'X', "exclude", "exclude PATTERN", "never match against interfaces matching PATTERN", true, handle_match}, +}; + +struct if_option_group match_option_group = { + .desc = "Matching interfaces", + .group_size = ARRAY_SIZE(match_options), + .group = match_options +}; diff --git a/cmd/multicall-options.c b/cmd/multicall-options.c new file mode 100644 index 0000000..9037264 --- /dev/null +++ b/cmd/multicall-options.c @@ -0,0 +1,159 @@ +/* + * cmd/multicall-options.c + * Purpose: multi-call binary frontend -- option handling + * + * Copyright (c) 2020 Ariadne Conill + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * This software is provided 'as is' and without any warranty, express or + * implied. In no event shall the authors be liable for any damages arising + * from the use of this software. + */ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include "cmd/multicall.h" + +void +generic_usage(const struct if_applet *applet, int result) +{ + fprintf(stderr, "%s", applet->name); + if (applet->desc != NULL) + fprintf(stderr, " - %s", applet->desc); + + fprintf(stderr, "\n"); + + if (applet->usage != NULL) + fprintf(stderr, "\nUsage:\n %s\n", applet->usage); + + size_t iter; + for (iter = 0; applet->groups[iter] != NULL; iter++) + { + const struct if_option_group *group = applet->groups[iter]; + + fprintf(stderr, "\n%s:\n", group->desc); + + size_t group_iter; + for (group_iter = 0; group_iter < group->group_size; group_iter++) + { + const struct if_option *opt = &group->group[group_iter]; + + fprintf(stderr, " "); + + if (opt->short_opt) + fprintf(stderr, "-%c", opt->short_opt); + else + fprintf(stderr, " "); + + if (opt->long_opt) + fprintf(stderr, "%c --%-30s", opt->short_opt ? ',' : ' ', + opt->long_opt_desc ? opt->long_opt_desc : opt->long_opt); + else + fprintf(stderr, "%34s", ""); + + fprintf(stderr, "%s\n", opt->desc); + } + } + + exit(result); +} + +static void +generic_usage_request(int short_opt, const struct if_option *option, const char *opt_arg, const struct if_applet *applet) +{ + (void) short_opt; + (void) option; + (void) opt_arg; + + generic_usage(applet, EXIT_SUCCESS); +} + +static struct if_option global_options[] = { + {'h', "help", NULL, "this help", false, generic_usage_request}, + {'V', "version", NULL, "show this program's version", false, (void *) lif_common_version}, +}; + +struct if_option_group global_option_group = { + .desc = "Global options", + .group_size = ARRAY_SIZE(global_options), + .group = global_options +}; + +const struct if_option * +lookup_option(const struct if_applet *applet, int opt) +{ + size_t iter; + for (iter = 0; applet->groups[iter] != NULL; iter++) + { + const struct if_option_group *group = applet->groups[iter]; + size_t group_iter; + + for (group_iter = 0; group_iter < group->group_size; group_iter++) + { + const struct if_option *option = &group->group[group_iter]; + + if (option->short_opt == opt) + return option; + } + } + + return NULL; +} + +void +process_options(const struct if_applet *applet, int argc, char *argv[]) +{ + char short_opts[256] = {}, *p = short_opts; + struct option long_opts[256] = {}; + + size_t iter, long_opt_iter = 0; + for (iter = 0; applet->groups[iter] != NULL; iter++) + { + const struct if_option_group *group = applet->groups[iter]; + size_t group_iter; + + for (group_iter = 0; group_iter < group->group_size; group_iter++) + { + const struct if_option *opt = &group->group[group_iter]; + + if (opt->short_opt) + { + *p++ = opt->short_opt; + if (opt->require_argument) + *p++ = ':'; + } + + if (opt->long_opt) + { + /* XXX: handle long-opts without short-opts */ + long_opts[long_opt_iter] = (struct option) { + .name = opt->long_opt, + .has_arg = opt->require_argument ? required_argument : no_argument, + .val = opt->short_opt + }; + + long_opt_iter++; + } + } + } + + for (;;) + { + int c = getopt_long(argc, argv, short_opts, long_opts, NULL); + if (c == -1) + break; + + const struct if_option *opt = lookup_option(applet, c); + if (opt == NULL) + break; + + opt->handle(c, opt, optarg, applet); + } +} diff --git a/cmd/multicall.c b/cmd/multicall.c index c6ae3d4..ccb7688 100644 --- a/cmd/multicall.c +++ b/cmd/multicall.c @@ -13,10 +13,12 @@ * from the use of this software. */ +#define _GNU_SOURCE #include #include #include #include +#include #include "cmd/multicall.h" char *argv0; @@ -25,6 +27,7 @@ extern struct if_applet ifquery_applet; extern struct if_applet ifup_applet; extern struct if_applet ifdown_applet; struct if_applet ifupdown_applet; +const struct if_applet *self_applet = NULL; struct if_applet *applet_table[] = { &ifdown_applet, @@ -33,8 +36,6 @@ struct if_applet *applet_table[] = { &ifupdown_applet, }; -#define ARRAY_SIZE(x) (sizeof(x) / sizeof(*x)) - int applet_cmp(const void *a, const void *b) { @@ -50,7 +51,7 @@ int main(int argc, char *argv[]) { argv0 = basename(argv[0]); - struct if_applet **app; + const struct if_applet **app; app = bsearch(argv0, applet_table, ARRAY_SIZE(applet_table), sizeof(*applet_table), @@ -62,6 +63,9 @@ main(int argc, char *argv[]) multicall_usage(EXIT_FAILURE); } + self_applet = *app; + process_options(*app, argc, argv); + return (*app)->main(argc, argv); } @@ -74,6 +78,8 @@ multicall_main(int argc, char *argv[]) return main(argc - 1, argv + 1); } +struct if_applet ifupdown_applet; + void multicall_usage(int status) { @@ -96,5 +102,5 @@ multicall_usage(int status) struct if_applet ifupdown_applet = { .name = "ifupdown", .main = multicall_main, - .usage = multicall_usage, + .groups = { &global_option_group, NULL } }; diff --git a/cmd/multicall.h b/cmd/multicall.h index 6fb26a4..23cb836 100644 --- a/cmd/multicall.h +++ b/cmd/multicall.h @@ -13,17 +13,63 @@ * from the use of this software. */ +#include + #ifndef IFUPDOWN_CMD_MULTICALL_H__GUARD #define IFUPDOWN_CMD_MULTICALL_H__GUARD #include "libifupdown/libifupdown.h" +#define ARRAY_SIZE(x) (sizeof(x) / sizeof(*x)) + +struct if_applet; + +struct if_option { + char short_opt; + const char *long_opt; + const char *long_opt_desc; + const char *desc; + bool require_argument; + void (*const handle)(int short_opt, const struct if_option *opt, const char *opt_arg, const struct if_applet *applet); +}; + +struct if_option_group { + const char *desc; + size_t group_size; + const struct if_option *group; +}; + struct if_applet { const char *name; + const char *desc; + const char *usage; int (*const main)(int argc, char *argv[]); - void (*const usage)(int status); + const struct if_option_group *groups[16]; }; extern char *argv0; +extern const struct if_applet *self_applet; +extern struct if_option_group global_option_group; + +struct match_options { + bool is_auto; + const char *exclude_pattern; + const char *include_pattern; + bool pretty_print; + bool dot; + const char *property; +}; + +extern struct match_options match_opts; + +extern void process_options(const struct if_applet *applet, int argc, char *argv[]); +extern const struct if_option *lookup_option(const struct if_applet *applet, int opt); + +extern struct if_option_group match_option_group; + +extern struct lif_execute_opts exec_opts; +extern struct if_option_group exec_option_group; + +void generic_usage(const struct if_applet *applet, int result); #endif