]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
various: convert "services" to option macros
authorZbigniew Jędrzejewski-Szmek <zbyszek@amutable.com>
Wed, 29 Apr 2026 21:22:45 +0000 (23:22 +0200)
committerZbigniew Jędrzejewski-Szmek <zbyszek@amutable.com>
Tue, 5 May 2026 11:49:51 +0000 (13:49 +0200)
Here we have the unusual situation that the option list is
conditionalized. I thought about embedding some "tag" information in
individual options to allow the options to be filtered by some arbitrary
conditions. But it seems that using groups works quite well. It wouldn't
scale well if there was a lot more options and conditions, but for the
current set it's good enough.

For options that are not supported in a given service, we print a custom
message ("This service does not support [this] option"), instead of the
generic "Unknown option …". I think this is actually better: we don't
have to pretent that we don't know about the option, and can directly
say that the it's a valid option in general but this service does not
support it (yet).

This converts systemd-homed, systemd-hostnamed, systemd-importd,
systemd-localed, systemd-logind, systemd-machined, systemd-networkd,
systemd-portabled, systemd-resolved, systemd-sysupdated,
systemd-timedated, and systemd-timesyncd.

When we add introspection of the option data, we'll somehow have to deal
with conditionalization. But let's cross that bridge when we need to.

src/shared/service-util.c

index 70d59af6c51d1154c202e040c8f471f673bf6b2a..7c1df8e73adad201cf6498f4b3363b357ebdfca5 100644 (file)
@@ -1,53 +1,53 @@
 /* SPDX-License-Identifier: LGPL-2.1-or-later */
 
-#include <getopt.h>
 #include <stdio.h>
 
-#include "alloc-util.h"
 #include "build.h"
 #include "bus-object.h"
+#include "format-table.h"
+#include "help-util.h"
 #include "log.h"
-#include "pretty-print.h"
+#include "options.h"
 #include "runtime-scope.h"
 #include "service-util.h"
 
-typedef enum HelpFlags {
-        HELP_WITH_BUS_INTROSPECT = 1 << 0,
-        HELP_WITH_RUNTIME_SCOPE  = 1 << 1,
-} HelpFlags;
-
-static int help(const char *program_path,
-                const char *service,
+static int help(const char *service,
                 const char *description,
-                HelpFlags flags) {
+                bool with_bus_introspect,
+                bool with_runtime_scope) {
 
-        _cleanup_free_ char *link = NULL;
+        static const char* const groups[] = {
+                NULL,
+                "Bus introspection",
+                "Runtime scope",
+        };
+
+        bool conds[ELEMENTSOF(groups)] = { true, with_bus_introspect, with_runtime_scope };
+        Table* tables[ELEMENTSOF(groups)] = {};
+        CLEANUP_ELEMENTS(tables, table_unref_array_clear);
         int r;
 
-        r = terminal_urlify_man(service, "8", &link);
-        if (r < 0)
-                return log_oom();
-
-        printf("%1$s [OPTIONS...]\n"
-               "\n%5$s%7$s%6$s\n"
-               "\nThis program takes no positional arguments.\n"
-               "\n%3$sOptions:%4$s\n"
-               "  -h --help                 Show this help\n"
-               "     --version              Show package version\n"
-               "%8$s"
-               "%9$s"
-               "\nSee the %2$s for details.\n",
-               program_path,
-               link,
-               ansi_underline(),
-               ansi_normal(),
-               ansi_highlight(),
-               ansi_normal(),
-               description,
-               FLAGS_SET(flags, HELP_WITH_BUS_INTROSPECT) ? "     --bus-introspect=PATH  Write D-Bus XML introspection data\n" : "",
-               FLAGS_SET(flags, HELP_WITH_RUNTIME_SCOPE)  ? "     --system               Start service in system mode\n"
-                                                            "     --user                 Start service in user mode\n" : "");
+        for (size_t i = 0; i < ELEMENTSOF(groups); i++)
+                if (conds[i]) {
+                        r = option_parser_get_help_table_group(groups[i], &tables[i]);
+                        if (r < 0)
+                                return r;
+                }
+
+        (void) table_sync_column_widths(0, tables[0], tables[1] ?: tables[2], tables[1] ? tables[2] : NULL);
+
+        help_cmdline("[OPTIONS...]");
+        help_abstract(description);
 
+        help_section("Options");
+        for (size_t i = 0; i < ELEMENTSOF(groups); i++)
+                if (conds[i]) {
+                        r = table_print_or_warn(tables[i]);
+                        if (r < 0)
+                                return r;
+                }
+
+        help_man_page_reference(service, "8");
         return 0; /* No further action */
 }
 
@@ -58,64 +58,50 @@ int service_parse_argv(
                 RuntimeScope *runtime_scope,
                 int argc, char *argv[]) {
 
-        enum {
-                ARG_VERSION = 0x100,
-                ARG_BUS_INTROSPECT,
-                ARG_SYSTEM,
-                ARG_USER,
-        };
-
-        static const struct option options[] = {
-                { "help",           no_argument,       NULL, 'h'                },
-                { "version",        no_argument,       NULL, ARG_VERSION        },
-                { "bus-introspect", required_argument, NULL, ARG_BUS_INTROSPECT },
-                { "system",         no_argument,       NULL, ARG_SYSTEM         },
-                { "user",           no_argument,       NULL, ARG_USER           },
-                {}
-        };
-
-        int c;
-
         assert(argc >= 0);
         assert(argv);
 
-        while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
+        OptionParser opts = { argc, argv };
+
+        FOREACH_OPTION_OR_RETURN(c, &opts)
                 switch (c) {
 
-                case 'h':
-                        return help(argv[0],
-                                    service,
+                OPTION_COMMON_HELP:
+                        return help(service,
                                     description,
-                                    (bus_objects ? HELP_WITH_BUS_INTROSPECT : 0) |
-                                    (runtime_scope ? HELP_WITH_RUNTIME_SCOPE : 0));
+                                    /* with_bus_introspect= */ bus_objects,
+                                    /* with_runtime_scope= */ runtime_scope);
 
-                case ARG_VERSION:
+                OPTION_COMMON_VERSION:
                         return version();
 
-                case ARG_BUS_INTROSPECT:
-                        return bus_introspect_implementations(
-                                        stdout,
-                                        optarg,
-                                        bus_objects);
+                OPTION_GROUP("Bus introspection"): {}
 
-                case ARG_SYSTEM:
-                case ARG_USER:
-                        if (!runtime_scope)
-                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "This service cannot be run in --system or --user mode, refusing.");
+                OPTION_LONG("bus-introspect", "PATH", "Write D-Bus XML introspection data"):
+                        /* The option is defined in the shared option table, but it's not supported in this binary,
+                         * so we pretend it doesn't exist. */
+                        if (!bus_objects)
+                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+                                                       "This service does not support the --bus-introspect= option.");
 
-                        *runtime_scope = c == ARG_SYSTEM ? RUNTIME_SCOPE_SYSTEM : RUNTIME_SCOPE_USER;
-                        break;
+                        return bus_introspect_implementations(stdout, opts.arg, bus_objects);
 
-                case '?':
-                        return -EINVAL;
+                OPTION_GROUP("Runtime scope"): {}
 
-                default:
-                        assert_not_reached();
+                OPTION_LONG_DATA("system", NULL, /* data= */ RUNTIME_SCOPE_SYSTEM,
+                                 "Start service in system mode"): {}
+                OPTION_LONG_DATA("user", NULL, /* data= */ RUNTIME_SCOPE_USER,
+                                 "Start service in user mode"):
+                        if (!runtime_scope)
+                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+                                                       "This service does not support the --system/--user options.");
+
+                        *runtime_scope = opts.opt->data;
+                        break;
                 }
 
-        if (optind < argc)
-                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
-                                       "This program takes no arguments.");
+        if (option_parser_get_n_args(&opts) > 0)
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "This program takes no arguments.");
 
         return 1; /* Further action */
 }