From: Tobias Brunner Date: Thu, 29 Jan 2026 16:32:03 +0000 (+0100) Subject: pki: Add global --debug and --options arguments X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=be68bd50b7d43f50f9e9f5a4c3ed9b2689face8d;p=thirdparty%2Fstrongswan.git pki: Add global --debug and --options arguments This allows setting the log level before initializing the library and loading the plugins, as well as having the top-level command itself in an options file. --- diff --git a/src/pki/command.c b/src/pki/command.c index abf0ba61c1..18a3b7dedb 100644 --- a/src/pki/command.c +++ b/src/pki/command.c @@ -1,4 +1,5 @@ /* + * Copyright (C) 2012-2026 Tobias Brunner * Copyright (C) 2009 Martin Willi * * Copyright (C) secunet Security Networks AG @@ -63,12 +64,48 @@ static struct option command_opts[MAX_COMMANDS > MAX_OPTIONS ? MAX_COMMANDS : MA */ static char command_optstring[(MAX_COMMANDS > MAX_OPTIONS ? MAX_COMMANDS : MAX_OPTIONS) * 3]; +/** + * Common options + */ +static command_option_t shared_options[] = { + { + "debug", 'v', 1, "set debug level, default: 1" + }, + { + "options", '+', 1, "read command line options from file" + }, +}; + +/** + * Add a single option to command_opts/command_optstr at the current locations. + */ +static void build_option(command_option_t *option, int i, int *pos) +{ + command_opts[i].name = option->name; + command_opts[i].has_arg = option->arg; + command_opts[i].val = option->op; + + command_optstring[(*pos)++] = option->op; + switch (option->arg) + { + case optional_argument: + command_optstring[(*pos)++] = ':'; + /* FALL */ + case required_argument: + command_optstring[(*pos)++] = ':'; + /* FALL */ + case no_argument: + default: + break; + } +} + /** * Build command_opts/command_optstr for the active command */ static void build_opts() { - int i, pos = 0; + int i, j, pos = 0; memset(command_opts, 0, sizeof(command_opts)); memset(command_optstring, 0, sizeof(command_optstring)); @@ -80,33 +117,32 @@ static void build_opts() command_opts[i].val = cmds[i].op; command_optstring[i] = cmds[i].op; } + /* add shared options not as actual option but as commands with + * argument (which commands usually don't take) */ + if (i > MAX_COMMANDS - countof(shared_options)) + { + fprintf(stderr, "unable to add global shared options, please " + "increase MAX_COMMANDS\n"); + } + else + { + for (j = 0, pos = i; j < countof(shared_options); j++, i++) + { + build_option(&shared_options[j], i, &pos); + } + } } else { - for (i = 0; cmds[active].options[i].name; i++) + for (i = 0; i < MAX_OPTIONS && cmds[active].options[i].name; i++) { - command_opts[i].name = cmds[active].options[i].name; - command_opts[i].has_arg = cmds[active].options[i].arg; - command_opts[i].val = cmds[active].options[i].op; - command_optstring[pos++] = cmds[active].options[i].op; - switch (cmds[active].options[i].arg) - { - case optional_argument: - command_optstring[pos++] = ':'; - /* FALL */ - case required_argument: - command_optstring[pos++] = ':'; - /* FALL */ - case no_argument: - default: - break; - } + build_option(&cmds[active].options[i], i, &pos); } } } /** - * getopt_long wrapper + * Wrapper around getopt_long() that skippes common options */ int command_getopt(char **arg) { @@ -121,17 +157,25 @@ int command_getopt(char **arg) case 'v': continue; default: - *arg = optarg; + if (arg) + { + *arg = optarg; + } return op; } } } /** - * Process options common for all commands + * Pre-process options common for all commands */ -static bool process_common_opts() +static bool process_common_opts(bool init) { + int prevoptind = optind; + bool success = TRUE; + + /* don't report any errors during this pre-processing */ + opterr = 0; while (TRUE) { switch (getopt_long(argc, argv, command_optstring, command_opts, NULL)) @@ -139,20 +183,30 @@ static bool process_common_opts() case '+': if (!options->from(options, optarg, &argc, &argv, optind)) { - return FALSE; + success = FALSE; + break; } continue; case 'v': dbg_default_set_level(atoi(optarg)); continue; default: + if (init) + { /* stop if we found a known command during initialization, + * otherwise we'd process e.g. --options twice */ + break; + } continue; case '?': - return FALSE; case EOF: - return TRUE; + break; } + break; } + /* restore option parser state after pre-processing */ + optind = prevoptind; + opterr = 1; + return success; } /** @@ -160,7 +214,7 @@ static bool process_common_opts() */ void command_register(command_t command) { - int i; + int i, j; if (registered == MAX_COMMANDS) { @@ -173,26 +227,24 @@ void command_register(command_t command) /* append default options, but not to --help */ if (!active) { - for (i = 0; i < countof(cmds[registered].options) - 1; i++) + for (i = 0; i < MAX_OPTIONS; i++) { if (!cmds[registered].options[i].name) { break; } } - if (i > countof(cmds[registered].options) - 3) + if (i > MAX_OPTIONS - countof(shared_options)) { fprintf(stderr, "command '%s' registered too many options, please " "increase MAX_OPTIONS\n", command.cmd); } else { - cmds[registered].options[i++] = (command_option_t) { - "debug", 'v', 1, "set debug level, default: 1" - }; - cmds[registered].options[i++] = (command_option_t) { - "options", '+', 1, "read command line options from file" - }; + for (j = 0; j < countof(shared_options); j++) + { + cmds[registered].options[i++] = shared_options[j]; + } } for (i = 0; cmds[registered].line[i]; i++) { @@ -223,14 +275,25 @@ int command_usage(char *error) if (active == help_idx) { - fprintf(out, "\nloaded plugins: %s\nusage:\n" - " pki command [options]\ncommands:\n", - lib->plugins->loaded_plugins(lib->plugins)); + fprintf(out, "\n"); + if (lib) + { + fprintf(out, "loaded plugins: %s\nusage:\n" + " pki command [options]\ncommands:\n", + lib->plugins->loaded_plugins(lib->plugins)); + } for (i = 0; i < MAX_COMMANDS && cmds[i].cmd; i++) { fprintf(out, " --%-7s (-%c) %s\n", cmds[i].cmd, cmds[i].op, cmds[i].description); } + fprintf(out, "options:\n"); + for (i = 0; i < countof(shared_options); i++) + { + fprintf(out, " --%-7s (-%c) %s\n", + shared_options[i].name, shared_options[i].op, + shared_options[i].desc); + } } else { @@ -249,7 +312,7 @@ int command_usage(char *error) } } fprintf(out, "options:\n"); - for (i = 0; cmds[active].options[i].name; i++) + for (i = 0; i < MAX_OPTIONS && cmds[active].options[i].name; i++) { fprintf(out, " --%-15s (-%c) %s\n", cmds[active].options[i].name, cmds[active].options[i].op, @@ -280,35 +343,51 @@ static void cleanup() options->destroy(options); } -/** - * Dispatch commands. +/* + * Described in header */ -int command_dispatch(int c, char *v[]) +int command_init(int c, char *v[]) { int op, i; + argc = c; + argv = v; + options = options_create(); atexit(cleanup); + active = help_idx = registered; - argc = c; - argv = v; - command_register((command_t){help, 'h', "help", "show usage information"}); + command_register((command_t){help, 'h', "help", "show usage, version and " + "plugin information"}); build_opts(); - op = getopt_long(c, v, command_optstring, command_opts, NULL); + /* handle common options until we find a command */ + if (!process_common_opts(TRUE)) + { + return command_usage("invalid --options"); + } + op = command_getopt(NULL); for (i = 0; i < MAX_COMMANDS && cmds[i].cmd; i++) { if (cmds[i].op == op) { active = i; build_opts(); - if (!process_common_opts()) + /* handle common options again, now with specific options loaded */ + if (!process_common_opts(FALSE)) { - return command_usage("invalid options"); + return command_usage("invalid --options"); } - optind = 2; - return cmds[i].call(); + return 0; } } - return command_usage(c > 1 ? "invalid operation" : NULL); + return command_usage(op != EOF ? "invalid command" : NULL); +} + +/* + * Described in header + */ +int command_dispatch() +{ + return cmds[active].call(); } diff --git a/src/pki/command.h b/src/pki/command.h index f46b49de03..d1d41492ab 100644 --- a/src/pki/command.h +++ b/src/pki/command.h @@ -1,4 +1,5 @@ /* + * Copyright (C) 2014-2026 Tobias Brunner * Copyright (C) 2009 Martin Willi * * Copyright (C) secunet Security Networks AG @@ -23,14 +24,14 @@ #define COMMAND_H_ /** - * Maximum number of commands (+1). + * Maximum number of commands (+2). */ -#define MAX_COMMANDS 19 +#define MAX_COMMANDS 21 /** - * Maximum number of options in a command (+3) + * Maximum number of options in a command (+2) */ -#define MAX_OPTIONS 36 +#define MAX_OPTIONS 35 /** * Maximum number of usage summary lines (+1) @@ -83,10 +84,15 @@ int command_getopt(char **arg); */ void command_register(command_t command); +/** + * Initialize command parsing/dispatching. + */ +int command_init(int argc, char *argv[]); + /** * Dispatch commands. */ -int command_dispatch(int argc, char *argv[]); +int command_dispatch(); /** * Show usage information of active command. diff --git a/src/pki/pki.c b/src/pki/pki.c index 0b03bf7aa6..c50d44a6af 100644 --- a/src/pki/pki.c +++ b/src/pki/pki.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012-2023 Tobias Brunner + * Copyright (C) 2012-2026 Tobias Brunner * Copyright (C) 2009 Martin Willi * * Copyright (C) secunet Security Networks AG @@ -485,6 +485,13 @@ static void remove_callback() int main(int argc, char *argv[]) { char *plugins; + int status; + + status = command_init(argc, argv); + if (status) + { + return status; + } atexit(library_deinit); if (!library_init(NULL, "pki")) @@ -509,5 +516,5 @@ int main(int argc, char *argv[]) add_callback(); atexit(remove_callback); - return command_dispatch(argc, argv); + return command_dispatch(); }