]> git.ipfire.org Git - thirdparty/strongswan.git/commitdiff
pki: Add global --debug and --options arguments
authorTobias Brunner <tobias@strongswan.org>
Thu, 29 Jan 2026 16:32:03 +0000 (17:32 +0100)
committerTobias Brunner <tobias@strongswan.org>
Fri, 6 Feb 2026 10:21:34 +0000 (11:21 +0100)
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.

src/pki/command.c
src/pki/command.h
src/pki/pki.c

index abf0ba61c10e99ab7dd3f094ab274eb842729f26..18a3b7dedbd0300c15ebf9ee8f26f8985b7940cb 100644 (file)
@@ -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();
 }
index f46b49de03a271c8943ef9188d9b7226cac34393..d1d41492ab9d105d4075f5f692306e8b3c0e2ddd 100644 (file)
@@ -1,4 +1,5 @@
 /*
+ * Copyright (C) 2014-2026 Tobias Brunner
  * Copyright (C) 2009 Martin Willi
  *
  * Copyright (C) secunet Security Networks AG
 #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.
index 0b03bf7aa6e14ff2297b05c9972a12665d73dce8..c50d44a6afa3bed0761f8d7df384dd2c7ea365e5 100644 (file)
@@ -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();
 }