From c9615f73521986b3607b852c139036d58973043c Mon Sep 17 00:00:00 2001 From: =?utf8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Fri, 5 Feb 2021 13:54:33 +0100 Subject: [PATCH] systemctl: add "reload-or-restart --marked" This is almost equivalent to 'busctl call-method org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager EnqueueMarkedJobs', but waits for the jobs to finish. --- man/systemctl.xml | 12 +++++ src/systemctl/systemctl-start-unit.c | 65 +++++++++++++++++++++++----- src/systemctl/systemctl.c | 31 ++++++++++++- src/systemctl/systemctl.h | 1 + 4 files changed, 96 insertions(+), 13 deletions(-) diff --git a/man/systemctl.xml b/man/systemctl.xml index db4e2b2c65c..642a5ecf2d1 100644 --- a/man/systemctl.xml +++ b/man/systemctl.xml @@ -2316,6 +2316,18 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err a directory, but a regular file, device node, socket or FIFO. + + + + Only allowed with reload-or-restart. Enqueues restart jobs for all + units that have the needs-restart mark, and reload jobs for units that have the + needs-reload mark. When a unit marked for reload does not support reload, restart + will be queued. Those properties can be set using set-property Marks. + + Unless is used, systemctl will wait for the + queued jobs to finish. + + diff --git a/src/systemctl/systemctl-start-unit.c b/src/systemctl/systemctl-start-unit.c index d08e363c9fa..c40e807212d 100644 --- a/src/systemctl/systemctl-start-unit.c +++ b/src/systemctl/systemctl-start-unit.c @@ -176,6 +176,43 @@ fail: return r; } +static int enqueue_marked_jobs( + sd_bus *bus, + BusWaitForJobs *w) { + + _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + int r; + + log_debug("%s dbus call org.freedesktop.systemd1.Manager EnqueueMarkedJobs()", + arg_dry_run ? "Would execute" : "Executing"); + + if (arg_dry_run) + return 0; + + r = bus_call_method(bus, bus_systemd_mgr, "EnqueueMarkedJobs", &error, &reply, NULL); + if (r < 0) + return log_error_errno(r, "Failed to start jobs: %s", bus_error_message(&error, r)); + + _cleanup_strv_free_ char **paths = NULL; + r = sd_bus_message_read_strv(reply, &paths); + if (r < 0) + return bus_log_parse_error(r); + + if (w) { + char **path; + + STRV_FOREACH(path, paths) { + log_debug("Adding %s to the set", *path); + r = bus_wait_for_jobs_add(w, *path); + if (r < 0) + return log_error_errno(r, "Failed to watch job %s: %m", *path); + } + } + + return 0; +} + const struct action_metadata action_table[_ACTION_MAX] = { [ACTION_HALT] = { SPECIAL_HALT_TARGET, "halt", "replace-irreversibly" }, [ACTION_POWEROFF] = { SPECIAL_POWEROFF_TARGET, "poweroff", "replace-irreversibly" }, @@ -265,7 +302,7 @@ int start_unit(int argc, char *argv[], void *userdata) { job_type = "start"; mode = "isolate"; suffix = ".target"; - } else { + } else if (!arg_marked) { /* A command in style of "systemctl start …", "sysemctl stop …" and so on */ method = verb_to_method(argv[0]); job_type = verb_to_job_type(argv[0]); @@ -289,7 +326,7 @@ int start_unit(int argc, char *argv[], void *userdata) { names = strv_new(one_name); if (!names) return log_oom(); - } else { + } else if (!arg_marked) { bool expanded; r = expand_unit_names(bus, strv_skip(argv, 1), suffix, &names, &expanded); @@ -322,19 +359,23 @@ int start_unit(int argc, char *argv[], void *userdata) { return log_error_errno(r, "Failed to allocate unit watch context: %m"); } - STRV_FOREACH(name, names) { - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + if (arg_marked) + ret = enqueue_marked_jobs(bus, w); - r = start_unit_one(bus, method, job_type, *name, mode, &error, w, wu); - if (ret == EXIT_SUCCESS && r < 0) - ret = translate_bus_error_to_exit_status(r, &error); + else + STRV_FOREACH(name, names) { + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - if (r >= 0 && streq(method, "StopUnit")) { - r = strv_push(&stopped_units, *name); - if (r < 0) - return log_oom(); + r = start_unit_one(bus, method, job_type, *name, mode, &error, w, wu); + if (ret == EXIT_SUCCESS && r < 0) + ret = translate_bus_error_to_exit_status(r, &error); + + if (r >= 0 && streq(method, "StopUnit")) { + r = strv_push(&stopped_units, *name); + if (r < 0) + return log_oom(); + } } - } if (!arg_no_block) { const char* extra_args[4]; diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c index 16d0555c7ca..315202a64b0 100644 --- a/src/systemctl/systemctl.c +++ b/src/systemctl/systemctl.c @@ -108,6 +108,7 @@ char **arg_clean_what = NULL; TimestampStyle arg_timestamp_style = TIMESTAMP_PRETTY; bool arg_read_only = false; bool arg_mkdir = false; +bool arg_marked = false; STATIC_DESTRUCTOR_REGISTER(arg_wall, strv_freep); STATIC_DESTRUCTOR_REGISTER(arg_root, freep); @@ -295,6 +296,7 @@ static int systemctl_help(void) { " 'us+utc': 'Day YYYY-MM-DD HH:MM:SS.UUUUUU UTC\n" " --read-only Create read-only bind mount\n" " --mkdir Create directory before mounting, if missing\n" + " --marked Restart/reload previously marked units\n" "\nSee the %2$s for details.\n", program_invocation_short_name, link, @@ -413,6 +415,7 @@ static int systemctl_parse_argv(int argc, char *argv[]) { ARG_TIMESTAMP_STYLE, ARG_READ_ONLY, ARG_MKDIR, + ARG_MARKED, }; static const struct option options[] = { @@ -471,6 +474,7 @@ static int systemctl_parse_argv(int argc, char *argv[]) { { "timestamp", required_argument, NULL, ARG_TIMESTAMP_STYLE }, { "read-only", no_argument, NULL, ARG_READ_ONLY }, { "mkdir", no_argument, NULL, ARG_MKDIR }, + { "marked", no_argument, NULL, ARG_MARKED }, {} }; @@ -900,6 +904,10 @@ static int systemctl_parse_argv(int argc, char *argv[]) { arg_mkdir = true; break; + case ARG_MARKED: + arg_marked = true; + break; + case '.': /* Output an error mimicking getopt, and print a hint afterwards */ log_error("%s: invalid option -- '.'", program_invocation_name); @@ -923,6 +931,27 @@ static int systemctl_parse_argv(int argc, char *argv[]) { return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "--wait may not be combined with --no-block."); + bool do_reload_or_restart = streq_ptr(argv[optind], "reload-or-restart"); + if (arg_marked) { + if (!do_reload_or_restart) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "--marked may only be used with 'reload-or-restart'."); + if (optind + 1 < argc) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "No additional arguments allowed with 'reload-or-restart --marked'."); + if (arg_wait) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "--marked --wait is not supported."); + if (arg_show_transaction) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "--marked --show-transaction is not supported."); + + } else if (do_reload_or_restart) { + if (optind + 1 >= argc) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "List of units to restart/reload is required."); + } + return 1; } @@ -1004,7 +1033,7 @@ static int systemctl_main(int argc, char *argv[]) { { "reload", 2, VERB_ANY, VERB_ONLINE_ONLY, start_unit }, { "restart", 2, VERB_ANY, VERB_ONLINE_ONLY, start_unit }, { "try-restart", 2, VERB_ANY, VERB_ONLINE_ONLY, start_unit }, - { "reload-or-restart", 2, VERB_ANY, VERB_ONLINE_ONLY, start_unit }, + { "reload-or-restart", VERB_ANY, VERB_ANY, VERB_ONLINE_ONLY, start_unit }, { "reload-or-try-restart", 2, VERB_ANY, VERB_ONLINE_ONLY, start_unit }, /* For compatibility with old systemctl <= 228 */ { "try-reload-or-restart", 2, VERB_ANY, VERB_ONLINE_ONLY, start_unit }, { "force-reload", 2, VERB_ANY, VERB_ONLINE_ONLY, start_unit }, /* For compatibility with SysV */ diff --git a/src/systemctl/systemctl.h b/src/systemctl/systemctl.h index 0ebe4580c60..3c5a4023a9f 100644 --- a/src/systemctl/systemctl.h +++ b/src/systemctl/systemctl.h @@ -93,5 +93,6 @@ extern char **arg_clean_what; extern TimestampStyle arg_timestamp_style; extern bool arg_read_only; extern bool arg_mkdir; +extern bool arg_marked; int systemctl_dispatch_parse_argv(int argc, char *argv[]); -- 2.39.5