various: convert "services" to option macros

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.
This commit is contained in:
Zbigniew Jędrzejewski-Szmek
2026-05-05 13:49:51 +02:00
parent e235b6a280
commit e71074f761
+63 -77
View 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();
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;
}
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" : "");
(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:
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.");
return bus_introspect_implementations(stdout, opts.arg, bus_objects);
OPTION_GROUP("Runtime scope"): {}
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 cannot be run in --system or --user mode, refusing.");
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"This service does not support the --system/--user options.");
*runtime_scope = c == ARG_SYSTEM ? RUNTIME_SCOPE_SYSTEM : RUNTIME_SCOPE_USER;
*runtime_scope = opts.opt->data;
break;
case '?':
return -EINVAL;
default:
assert_not_reached();
}
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 */
}