]> git.ipfire.org Git - thirdparty/strongswan.git/commitdiff
swanctl: Add global --debug, --options and --uri arguments
authorTobias Brunner <tobias@strongswan.org>
Fri, 30 Jan 2026 11:20:05 +0000 (12:20 +0100)
committerTobias Brunner <tobias@strongswan.org>
Fri, 6 Feb 2026 10:25:13 +0000 (11:25 +0100)
Similarly to the previous commit for pki, this allows setting these
options before the command, and by pre-parsing them we can see log
messages during the initialization.

src/swanctl/command.c
src/swanctl/command.h
src/swanctl/swanctl.c

index 7e654b875141787a97e915d1fb9b2b49eb5da161..35a226a7473efab5532c87879e3963a7ae66794e 100644 (file)
@@ -1,4 +1,5 @@
 /*
+ * Copyright (C) 2015-2026 Tobias Brunner
  * Copyright (C) 2009 Martin Willi
  *
  * Copyright (C) secunet Security Networks AG
@@ -70,12 +71,51 @@ static struct option command_opts[MAX_COMMANDS > MAX_OPTIONS ?
 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"
+       },
+       {
+               "uri", 'u', 1, "service URI to connect to"
+       },
+};
+
+/**
+ * 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));
@@ -87,27 +127,26 @@ 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++)
                {
-                       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);
                }
        }
 }
@@ -129,18 +168,65 @@ int command_getopt(char **arg)
                        case 'u':
                                continue;
                        default:
-                               *arg = optarg;
+                               if (arg)
+                               {
+                                       *arg = optarg;
+                               }
                                return op;
                }
        }
 }
 
+/**
+ * Pre-process options common for all commands
+ */
+static bool process_common_opts(bool init)
+{
+       int prevoptind = optind;
+       bool success = TRUE;
+
+       while (TRUE)
+       {
+               switch (getopt_long(argc, argv, command_optstring, command_opts, NULL))
+               {
+                       case '+':
+                               if (!options->from(options, optarg, &argc, &argv, optind))
+                               {
+                                       success = FALSE;
+                                       break;
+                               }
+                               continue;
+                       case 'v':
+                               dbg_default_set_level(atoi(optarg));
+                               continue;
+                       case 'u':
+                               uri = 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 '?':
+                       case EOF:
+                               break;
+               }
+               break;
+       }
+       /* restore option parser state after pre-processing */
+       optind = prevoptind;
+       opterr = 1;
+       return success;
+}
+
 /**
  * Register a command
  */
 void command_register(command_t command)
 {
-       int i;
+       int i, j;
 
        if (registered == MAX_COMMANDS)
        {
@@ -162,29 +248,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"
-                       };
-                       cmds[registered].options[i++] = (command_option_t) {
-                               "uri",          'u', 1, "service URI to connect to"
-                       };
+                       for (j = 0; j < countof(shared_options); j++)
+                       {
+                               cmds[registered].options[i++] = shared_options[j];
+                       }
                }
                for (i = 0; cmds[registered].line[i]; i++)
                {
@@ -216,14 +297,25 @@ int command_usage(char *error, ...)
 
        if (active == help_idx)
        {
-               fprintf(out, "\nloaded plugins: %s\nusage:\n"
-                               "  swanctl command [options]\ncommands:\n",
-                               lib->plugins->loaded_plugins(lib->plugins));
+               fprintf(out, "\n");
+               if (lib)
+               {
+                       fprintf(out, "loaded plugins: %s\nusage:\n"
+                                       "  swanctl command [options]\ncommands:\n",
+                                       lib->plugins->loaded_plugins(lib->plugins));
+               }
                for (i = 0; i < MAX_COMMANDS && cmds[i].cmd; i++)
                {
                        fprintf(out, "  --%-16s (-%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, "  --%-16s (-%c)  %s\n",
+                                       shared_options[i].name, shared_options[i].op,
+                                       shared_options[i].desc);
+               }
        }
        else
        {
@@ -242,7 +334,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,
@@ -268,37 +360,6 @@ static void cleanup()
        options->destroy(options);
 }
 
-/**
- * Process options common for all commands
- */
-static bool process_common_opts()
-{
-       while (TRUE)
-       {
-               switch (getopt_long(argc, argv, command_optstring, command_opts, NULL))
-               {
-                       case '+':
-                               if (!options->from(options, optarg, &argc, &argv, optind))
-                               {
-                                       return FALSE;
-                               }
-                               continue;
-                       case 'v':
-                               dbg_default_set_level(atoi(optarg));
-                               continue;
-                       case 'u':
-                               uri = optarg;
-                               continue;
-                       default:
-                               continue;
-                       case '?':
-                               return FALSE;
-                       case EOF:
-                               return TRUE;
-               }
-       }
-}
-
 /**
  * Open vici connection, call a command
  */
@@ -320,43 +381,65 @@ static int call_command(command_t *cmd)
        return ret;
 }
 
-/**
- * Dispatch commands.
+/*
+ * Described in header
  */
-int command_dispatch(int c, char *v[])
+int command_init(int c, char *v[])
 {
        int op, i;
 
-       uri = lib->settings->get_str(lib->settings, "%s.socket",
-                       lib->settings->get_str(lib->settings, "%s.plugins.vici.socket",
-                                                                  NULL, lib->ns), lib->ns);
+       argc = c;
+       argv = v;
 
        options = options_create();
        atexit(cleanup);
+
        active = help_idx = registered;
-       argc = c;
-       argv = v;
-       command_register((command_t){NULL, 'h', "help", "show usage information"});
+       command_register((command_t){NULL, '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 (help_idx == i)
+                       /* handle common options again, now with specific options loaded */
+                       if (!process_common_opts(FALSE))
                        {
-                               return command_usage(NULL);
+                               return command_usage("invalid --options");
                        }
-                       if (!process_common_opts())
-                       {
-                               return command_usage("invalid options");
-                       }
-                       optind = 2;
-                       return call_command(&cmds[i]);
+                       return 0;
                }
        }
-       return command_usage(c > 1 ? "invalid operation" : NULL);
+       return command_usage(op != EOF ? "invalid command" : NULL);
+}
+
+
+/*
+ * Described in header
+ */
+int command_dispatch()
+{
+       /* no callback registered for --help and we don't want to connect to the
+        * socket anyway */
+       if (active == help_idx)
+       {
+               return command_usage(NULL);
+       }
+
+       if (!uri)
+       {
+               uri = lib->settings->get_str(lib->settings, "%s.socket",
+                               lib->settings->get_str(lib->settings, "%s.plugins.vici.socket",
+                                                                          NULL, lib->ns), lib->ns);
+       }
+       return call_command(&cmds[active]);
 }
index 052aeb40ecf7c56ec921c3e1e23a9019d0b8faf1..1384f56a7cc244a44dbb6d87db50f9d2543daf4f 100644 (file)
@@ -1,4 +1,5 @@
 /*
+ * Copyright (C) 2016-2026 Tobias Brunner
  * Copyright (C) 2009 Martin Willi
  *
  * Copyright (C) secunet Security Networks AG
@@ -26,9 +27,9 @@
 #include <library.h>
 
 /**
- * Maximum number of commands (+1).
+ * Maximum number of commands (+3).
  */
-#define MAX_COMMANDS 26
+#define MAX_COMMANDS 29
 
 /**
  * Maximum number of options in a command (+3)
@@ -96,10 +97,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 a3bbc28fd056ec32c7e4d77c7bd96f26844c3ff2..4fb628838b527297500639ebe0c0c5cea6b1d775 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2018 Tobias Brunner
+ * Copyright (C) 2018-2026 Tobias Brunner
  * Copyright (C) 2014 Martin Willi
  *
  * Copyright (C) secunet Security Networks AG
@@ -95,6 +95,14 @@ static void cleanup()
  */
 int main(int argc, char *argv[])
 {
+       level_t level;
+       int status;
+
+       status = command_init(argc, argv);
+       if (status)
+       {
+               return status;
+       }
        atexit(cleanup);
        if (!library_init(NULL, "swanctl"))
        {
@@ -114,9 +122,14 @@ int main(int argc, char *argv[])
 
        swanctl_dir = strdup(getenv("SWANCTL_DIR") ?: SWANCTLDIR);
 
-       dbg_default_set_level(0);
+       /* suppress log message when spawning threads by default */
+       level = dbg_default_get_level_group(DBG_JOB);
+       if (level == 1)
+       {
+               dbg_default_set_level_group(DBG_JOB, 0);
+       }
        lib->processor->set_threads(lib->processor, 4);
-       dbg_default_set_level(1);
+       dbg_default_set_level_group(DBG_JOB, level);
 
-       return command_dispatch(argc, argv);
+       return command_dispatch();
 }