diff options
Diffstat (limited to '')
-rw-r--r-- | src/systemctl/systemctl-util.c | 929 |
1 files changed, 929 insertions, 0 deletions
diff --git a/src/systemctl/systemctl-util.c b/src/systemctl/systemctl-util.c new file mode 100644 index 0000000..b333850 --- /dev/null +++ b/src/systemctl/systemctl-util.c @@ -0,0 +1,929 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include <sys/reboot.h> +#include <unistd.h> + +#include "sd-bus.h" +#include "sd-daemon.h" + +#include "bus-common-errors.h" +#include "bus-locator.h" +#include "bus-map-properties.h" +#include "bus-unit-util.h" +#include "chase-symlinks.h" +#include "dropin.h" +#include "env-util.h" +#include "exit-status.h" +#include "fs-util.h" +#include "glob-util.h" +#include "macro.h" +#include "path-util.h" +#include "reboot-util.h" +#include "set.h" +#include "spawn-ask-password-agent.h" +#include "spawn-polkit-agent.h" +#include "stat-util.h" +#include "systemctl-util.h" +#include "systemctl.h" +#include "terminal-util.h" +#include "verbs.h" + +static sd_bus *buses[_BUS_FOCUS_MAX] = {}; + +int acquire_bus(BusFocus focus, sd_bus **ret) { + int r; + + assert(focus < _BUS_FOCUS_MAX); + assert(ret); + + /* We only go directly to the manager, if we are using a local transport */ + if (arg_transport != BUS_TRANSPORT_LOCAL) + focus = BUS_FULL; + + if (getenv_bool("SYSTEMCTL_FORCE_BUS") > 0) + focus = BUS_FULL; + + if (!buses[focus]) { + bool user; + + user = arg_scope != LOOKUP_SCOPE_SYSTEM; + + if (focus == BUS_MANAGER) + r = bus_connect_transport_systemd(arg_transport, arg_host, user, &buses[focus]); + else + r = bus_connect_transport(arg_transport, arg_host, user, &buses[focus]); + if (r < 0) + return bus_log_connect_error(r, arg_transport); + + (void) sd_bus_set_allow_interactive_authorization(buses[focus], arg_ask_password); + } + + *ret = buses[focus]; + return 0; +} + +void release_busses(void) { + for (BusFocus w = 0; w < _BUS_FOCUS_MAX; w++) + buses[w] = sd_bus_flush_close_unref(buses[w]); +} + +void ask_password_agent_open_maybe(void) { + /* Open the password agent as a child process if necessary */ + + if (arg_dry_run) + return; + + if (arg_scope != LOOKUP_SCOPE_SYSTEM) + return; + + ask_password_agent_open_if_enabled(arg_transport, arg_ask_password); +} + +void polkit_agent_open_maybe(void) { + /* Open the polkit agent as a child process if necessary */ + + if (arg_scope != LOOKUP_SCOPE_SYSTEM) + return; + + polkit_agent_open_if_enabled(arg_transport, arg_ask_password); +} + +int translate_bus_error_to_exit_status(int r, const sd_bus_error *error) { + assert(error); + + if (!sd_bus_error_is_set(error)) + return r; + + if (sd_bus_error_has_names(error, SD_BUS_ERROR_ACCESS_DENIED, + BUS_ERROR_ONLY_BY_DEPENDENCY, + BUS_ERROR_NO_ISOLATION, + BUS_ERROR_TRANSACTION_IS_DESTRUCTIVE)) + return EXIT_NOPERMISSION; + + if (sd_bus_error_has_name(error, BUS_ERROR_NO_SUCH_UNIT)) + return EXIT_NOTINSTALLED; + + if (sd_bus_error_has_names(error, BUS_ERROR_JOB_TYPE_NOT_APPLICABLE, + SD_BUS_ERROR_NOT_SUPPORTED)) + return EXIT_NOTIMPLEMENTED; + + if (sd_bus_error_has_name(error, BUS_ERROR_LOAD_FAILED)) + return EXIT_NOTCONFIGURED; + + if (r != 0) + return r; + + return EXIT_FAILURE; +} + +int get_state_one_unit(sd_bus *bus, const char *unit, UnitActiveState *ret_active_state) { + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + _cleanup_free_ char *buf = NULL, *dbus_path = NULL; + UnitActiveState state; + int r; + + assert(unit); + assert(ret_active_state); + + dbus_path = unit_dbus_path_from_name(unit); + if (!dbus_path) + return log_oom(); + + r = sd_bus_get_property_string( + bus, + "org.freedesktop.systemd1", + dbus_path, + "org.freedesktop.systemd1.Unit", + "ActiveState", + &error, + &buf); + if (r < 0) + return log_error_errno(r, "Failed to retrieve unit state: %s", bus_error_message(&error, r)); + + state = unit_active_state_from_string(buf); + if (state < 0) + return log_error_errno(state, "Invalid unit state '%s' for: %s", buf, unit); + + *ret_active_state = state; + return 0; +} + +int get_unit_list( + sd_bus *bus, + const char *machine, + char **patterns, + UnitInfo **unit_infos, + int c, + sd_bus_message **ret_reply) { + + _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; + int r; + bool fallback = false; + + assert(bus); + assert(unit_infos); + assert(ret_reply); + + r = bus_message_new_method_call(bus, &m, bus_systemd_mgr, "ListUnitsByPatterns"); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_append_strv(m, arg_states); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_append_strv(m, patterns); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_call(bus, m, 0, &error, &reply); + if (r < 0 && (sd_bus_error_has_names(&error, SD_BUS_ERROR_UNKNOWN_METHOD, + SD_BUS_ERROR_ACCESS_DENIED))) { + /* Fallback to legacy ListUnitsFiltered method */ + fallback = true; + log_debug_errno(r, "Failed to list units: %s Falling back to ListUnitsFiltered method.", bus_error_message(&error, r)); + m = sd_bus_message_unref(m); + sd_bus_error_free(&error); + + r = bus_message_new_method_call(bus, &m, bus_systemd_mgr, "ListUnitsFiltered"); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_append_strv(m, arg_states); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_call(bus, m, 0, &error, &reply); + } + if (r < 0) + return log_error_errno(r, "Failed to list units: %s", bus_error_message(&error, r)); + + r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssssssouso)"); + if (r < 0) + return bus_log_parse_error(r); + + for (;;) { + UnitInfo u; + + r = bus_parse_unit_info(reply, &u); + if (r < 0) + return bus_log_parse_error(r); + if (r == 0) + break; + + u.machine = machine; + + if (!output_show_unit(&u, fallback ? patterns : NULL)) + continue; + + if (!GREEDY_REALLOC(*unit_infos, c+1)) + return log_oom(); + + (*unit_infos)[c++] = u; + } + + r = sd_bus_message_exit_container(reply); + if (r < 0) + return bus_log_parse_error(r); + + *ret_reply = TAKE_PTR(reply); + return c; +} + +int expand_unit_names(sd_bus *bus, char **names, const char* suffix, char ***ret, bool *ret_expanded) { + _cleanup_strv_free_ char **mangled = NULL, **globs = NULL; + int r; + + assert(bus); + assert(ret); + + STRV_FOREACH(name, names) { + UnitNameMangle options = UNIT_NAME_MANGLE_GLOB | (arg_quiet ? 0 : UNIT_NAME_MANGLE_WARN); + char *t; + + r = unit_name_mangle_with_suffix(*name, NULL, options, suffix ?: ".service", &t); + if (r < 0) + return log_error_errno(r, "Failed to mangle name: %m"); + + if (string_is_glob(t)) + r = strv_consume(&globs, t); + else + r = strv_consume(&mangled, t); + if (r < 0) + return log_oom(); + } + + /* Query the manager only if any of the names are a glob, since this is fairly expensive */ + bool expanded = !strv_isempty(globs); + if (expanded) { + _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; + _cleanup_free_ UnitInfo *unit_infos = NULL; + size_t n; + + r = get_unit_list(bus, NULL, globs, &unit_infos, 0, &reply); + if (r < 0) + return r; + + n = strv_length(mangled); + + for (int i = 0; i < r; i++) { + if (!GREEDY_REALLOC(mangled, n+2)) + return log_oom(); + + mangled[n] = strdup(unit_infos[i].id); + if (!mangled[n]) + return log_oom(); + + mangled[++n] = NULL; + } + } + + if (ret_expanded) + *ret_expanded = expanded; + + *ret = TAKE_PTR(mangled); + return 0; +} + +int check_triggering_units(sd_bus *bus, const char *unit) { + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + _cleanup_free_ char *n = NULL, *dbus_path = NULL, *load_state = NULL; + _cleanup_strv_free_ char **triggered_by = NULL; + int r; + + r = unit_name_mangle(unit, 0, &n); + if (r < 0) + return log_error_errno(r, "Failed to mangle unit name: %m"); + + r = unit_load_state(bus, n, &load_state); + if (r < 0) + return r; + + if (streq(load_state, "masked")) + return 0; + + dbus_path = unit_dbus_path_from_name(n); + if (!dbus_path) + return log_oom(); + + r = sd_bus_get_property_strv( + bus, + "org.freedesktop.systemd1", + dbus_path, + "org.freedesktop.systemd1.Unit", + "TriggeredBy", + &error, + &triggered_by); + if (r < 0) + return log_error_errno(r, "Failed to get triggered by array of %s: %s", n, bus_error_message(&error, r)); + + bool first = true; + STRV_FOREACH(i, triggered_by) { + UnitActiveState active_state; + + r = get_state_one_unit(bus, *i, &active_state); + if (r < 0) + return r; + + if (!IN_SET(active_state, UNIT_ACTIVE, UNIT_RELOADING)) + continue; + + if (first) { + log_warning("Warning: Stopping %s, but it can still be activated by:", n); + first = false; + } + + log_warning(" %s", *i); + } + + return 0; +} + +int need_daemon_reload(sd_bus *bus, const char *unit) { + _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; + const char *path; + int b, r; + + /* We ignore all errors here, since this is used to show a + * warning only */ + + /* We don't use unit_dbus_path_from_name() directly since we + * don't want to load the unit if it isn't loaded. */ + + r = bus_call_method(bus, bus_systemd_mgr, "GetUnit", NULL, &reply, "s", unit); + if (r < 0) + return r; + + r = sd_bus_message_read(reply, "o", &path); + if (r < 0) + return r; + + r = sd_bus_get_property_trivial( + bus, + "org.freedesktop.systemd1", + path, + "org.freedesktop.systemd1.Unit", + "NeedDaemonReload", + NULL, + 'b', &b); + if (r < 0) + return r; + + return b; +} + +void warn_unit_file_changed(const char *unit) { + assert(unit); + + log_warning("Warning: The unit file, source configuration file or drop-ins of %s changed on disk. Run 'systemctl%s daemon-reload' to reload units.", + unit, + arg_scope == LOOKUP_SCOPE_SYSTEM ? "" : " --user"); +} + +int unit_file_find_path(LookupPaths *lp, const char *unit_name, char **ret_unit_path) { + assert(lp); + assert(unit_name); + + STRV_FOREACH(p, lp->search_path) { + _cleanup_free_ char *path = NULL, *lpath = NULL; + int r; + + path = path_join(*p, unit_name); + if (!path) + return log_oom(); + + r = chase_symlinks(path, arg_root, 0, &lpath, NULL); + if (r == -ENOENT) + continue; + if (r == -ENOMEM) + return log_oom(); + if (r < 0) + return log_error_errno(r, "Failed to access path \"%s\": %m", path); + + if (ret_unit_path) + *ret_unit_path = TAKE_PTR(lpath); + + return 1; + } + + if (ret_unit_path) + *ret_unit_path = NULL; + + return 0; +} + +int unit_find_paths( + sd_bus *bus, + const char *unit_name, + LookupPaths *lp, + bool force_client_side, + Hashmap **cached_name_map, + Hashmap **cached_id_map, + char **ret_fragment_path, + char ***ret_dropin_paths) { + + _cleanup_strv_free_ char **dropins = NULL; + _cleanup_free_ char *path = NULL; + int r; + + /** + * Finds where the unit is defined on disk. Returns 0 if the unit is not found. Returns 1 if it is + * found, and sets: + * + * - the path to the unit in *ret_frament_path, if it exists on disk, + * + * - and a strv of existing drop-ins in *ret_dropin_paths, if the arg is not NULL and any dropins + * were found. + * + * Returns -ERFKILL if the unit is masked, and -EKEYREJECTED if the unit file could not be loaded for + * some reason (the latter only applies if we are going through the service manager). As special + * exception it won't log for these two error cases. + */ + + assert(unit_name); + assert(ret_fragment_path); + assert(lp); + + /* Go via the bus to acquire the path, unless we are explicitly told not to, or when the unit name is a template */ + if (!force_client_side && + !install_client_side() && + !unit_name_is_valid(unit_name, UNIT_NAME_TEMPLATE)) { + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + _cleanup_free_ char *load_state = NULL, *dbus_path = NULL; + + dbus_path = unit_dbus_path_from_name(unit_name); + if (!dbus_path) + return log_oom(); + + r = sd_bus_get_property_string( + bus, + "org.freedesktop.systemd1", + dbus_path, + "org.freedesktop.systemd1.Unit", + "LoadState", + &error, + &load_state); + if (r < 0) + return log_error_errno(r, "Failed to get LoadState: %s", bus_error_message(&error, r)); + + if (streq(load_state, "masked")) + return -ERFKILL; /* special case: no logging */ + if (streq(load_state, "not-found")) { + r = 0; + goto finish; + } + if (!STR_IN_SET(load_state, "loaded", "bad-setting")) + return -EKEYREJECTED; /* special case: no logging */ + + r = sd_bus_get_property_string( + bus, + "org.freedesktop.systemd1", + dbus_path, + "org.freedesktop.systemd1.Unit", + "FragmentPath", + &error, + &path); + if (r < 0) + return log_error_errno(r, "Failed to get FragmentPath: %s", bus_error_message(&error, r)); + + if (ret_dropin_paths) { + r = sd_bus_get_property_strv( + bus, + "org.freedesktop.systemd1", + dbus_path, + "org.freedesktop.systemd1.Unit", + "DropInPaths", + &error, + &dropins); + if (r < 0) + return log_error_errno(r, "Failed to get DropInPaths: %s", bus_error_message(&error, r)); + } + } else { + const char *_path; + _cleanup_set_free_free_ Set *names = NULL; + + if (!*cached_name_map) { + r = unit_file_build_name_map(lp, NULL, cached_id_map, cached_name_map, NULL); + if (r < 0) + return r; + } + + r = unit_file_find_fragment(*cached_id_map, *cached_name_map, unit_name, &_path, &names); + if (r < 0) + return log_error_errno(r, "Failed to find fragment for '%s': %m", unit_name); + + if (_path) { + path = strdup(_path); + if (!path) + return log_oom(); + } + + if (ret_dropin_paths) { + r = unit_file_find_dropin_paths(arg_root, lp->search_path, NULL, + ".d", ".conf", + NULL, names, &dropins); + if (r < 0) + return r; + } + } + + finish: + if (isempty(path)) { + *ret_fragment_path = NULL; + r = 0; + } else { + *ret_fragment_path = TAKE_PTR(path); + r = 1; + } + + if (ret_dropin_paths) { + if (!strv_isempty(dropins)) { + *ret_dropin_paths = TAKE_PTR(dropins); + r = 1; + } else + *ret_dropin_paths = NULL; + } + + if (r == 0 && !arg_force) + log_error("No files found for %s.", unit_name); + + return r; +} + +static int unit_find_template_path( + const char *unit_name, + LookupPaths *lp, + char **ret_fragment_path, + char **ret_template) { + + _cleanup_free_ char *t = NULL, *f = NULL; + int r; + + /* Returns 1 if a fragment was found, 0 if not found, negative on error. */ + + r = unit_file_find_path(lp, unit_name, &f); + if (r < 0) + return r; + if (r > 0) { + if (ret_fragment_path) + *ret_fragment_path = TAKE_PTR(f); + if (ret_template) + *ret_template = NULL; + return r; /* found a real unit */ + } + + r = unit_name_template(unit_name, &t); + if (r == -EINVAL) { + if (ret_fragment_path) + *ret_fragment_path = NULL; + if (ret_template) + *ret_template = NULL; + + return 0; /* not a template, does not exist */ + } + if (r < 0) + return log_error_errno(r, "Failed to determine template name: %m"); + + r = unit_file_find_path(lp, t, ret_fragment_path); + if (r < 0) + return r; + + if (ret_template) + *ret_template = r > 0 ? TAKE_PTR(t) : NULL; + + return r; +} + +int unit_is_masked(sd_bus *bus, LookupPaths *lp, const char *name) { + _cleanup_free_ char *load_state = NULL; + int r; + + if (unit_name_is_valid(name, UNIT_NAME_TEMPLATE)) { + _cleanup_free_ char *path = NULL; + + /* A template cannot be loaded, but it can be still masked, so + * we need to use a different method. */ + + r = unit_file_find_path(lp, name, &path); + if (r < 0) + return r; + if (r == 0) + return false; + return null_or_empty_path(path); + } + + r = unit_load_state(bus, name, &load_state); + if (r < 0) + return r; + + return streq(load_state, "masked"); +} + +int unit_exists(LookupPaths *lp, const char *unit) { + typedef struct UnitStateInfo { + const char *load_state; + const char *active_state; + } UnitStateInfo; + + static const struct bus_properties_map property_map[] = { + { "LoadState", "s", NULL, offsetof(UnitStateInfo, load_state) }, + { "ActiveState", "s", NULL, offsetof(UnitStateInfo, active_state) }, + {}, + }; + + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; + _cleanup_free_ char *path = NULL; + UnitStateInfo info = {}; + sd_bus *bus; + int r; + + if (unit_name_is_valid(unit, UNIT_NAME_TEMPLATE)) + return unit_find_template_path(unit, lp, NULL, NULL); + + path = unit_dbus_path_from_name(unit); + if (!path) + return log_oom(); + + r = acquire_bus(BUS_MANAGER, &bus); + if (r < 0) + return r; + + r = bus_map_all_properties(bus, "org.freedesktop.systemd1", path, property_map, 0, &error, &m, &info); + if (r < 0) + return log_error_errno(r, "Failed to get properties: %s", bus_error_message(&error, r)); + + return !streq_ptr(info.load_state, "not-found") || !streq_ptr(info.active_state, "inactive"); +} + + +int append_unit_dependencies(sd_bus *bus, char **names, char ***ret) { + _cleanup_strv_free_ char **with_deps = NULL; + + assert(bus); + assert(ret); + + STRV_FOREACH(name, names) { + _cleanup_strv_free_ char **deps = NULL; + + if (strv_extend(&with_deps, *name) < 0) + return log_oom(); + + (void) unit_get_dependencies(bus, *name, &deps); + + if (strv_extend_strv(&with_deps, deps, true) < 0) + return log_oom(); + } + + *ret = TAKE_PTR(with_deps); + + return 0; +} + +int maybe_extend_with_unit_dependencies(sd_bus *bus, char ***list) { + _cleanup_strv_free_ char **list_with_deps = NULL; + int r; + + assert(bus); + assert(list); + + if (!arg_with_dependencies) + return 0; + + r = append_unit_dependencies(bus, *list, &list_with_deps); + if (r < 0) + return log_error_errno(r, "Failed to append unit dependencies: %m"); + + strv_free(*list); + *list = TAKE_PTR(list_with_deps); + return 0; +} + +int unit_get_dependencies(sd_bus *bus, const char *name, char ***ret) { + _cleanup_strv_free_ char **deps = NULL; + + static const struct bus_properties_map map[_DEPENDENCY_MAX][7] = { + [DEPENDENCY_FORWARD] = { + { "Requires", "as", NULL, 0 }, + { "Requisite", "as", NULL, 0 }, + { "Wants", "as", NULL, 0 }, + { "ConsistsOf", "as", NULL, 0 }, + { "BindsTo", "as", NULL, 0 }, + { "Upholds", "as", NULL, 0 }, + {} + }, + [DEPENDENCY_REVERSE] = { + { "RequiredBy", "as", NULL, 0 }, + { "RequisiteOf", "as", NULL, 0 }, + { "WantedBy", "as", NULL, 0 }, + { "PartOf", "as", NULL, 0 }, + { "BoundBy", "as", NULL, 0 }, + { "UpheldBy", "as", NULL, 0 }, + {} + }, + [DEPENDENCY_AFTER] = { + { "After", "as", NULL, 0 }, + {} + }, + [DEPENDENCY_BEFORE] = { + { "Before", "as", NULL, 0 }, + {} + }, + }; + + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + _cleanup_free_ char *dbus_path = NULL; + int r; + + assert(bus); + assert(name); + assert(ret); + + dbus_path = unit_dbus_path_from_name(name); + if (!dbus_path) + return log_oom(); + + r = bus_map_all_properties(bus, + "org.freedesktop.systemd1", + dbus_path, + map[arg_dependency], + BUS_MAP_STRDUP, + &error, + NULL, + &deps); + if (r < 0) + return log_error_errno(r, "Failed to get properties of %s: %s", name, bus_error_message(&error, r)); + + strv_uniq(deps); /* Sometimes a unit might have multiple deps on the other unit, + * but we still want to show it just once. */ + *ret = TAKE_PTR(deps); + + return 0; +} + +const char* unit_type_suffix(const char *unit) { + const char *dot; + + dot = strrchr(unit, '.'); + if (!dot) + return ""; + + return dot + 1; +} + +bool output_show_unit(const UnitInfo *u, char **patterns) { + assert(u); + + if (!strv_fnmatch_or_empty(patterns, u->id, FNM_NOESCAPE)) + return false; + + if (arg_types && !strv_contains(arg_types, unit_type_suffix(u->id))) + return false; + + if (arg_all) + return true; + + /* Note that '--all' is not purely a state filter, but also a filter that hides units that "follow" + * other units (which is used for device units that appear under different names). */ + if (!isempty(u->following)) + return false; + + if (!strv_isempty(arg_states)) + return true; + + /* By default show all units except the ones in inactive state and with no pending job */ + if (u->job_id > 0) + return true; + + if (streq(u->active_state, "inactive")) + return false; + + return true; +} + +bool install_client_side(void) { + /* Decides when to execute enable/disable/... operations client-side rather than server-side. */ + + if (running_in_chroot_or_offline()) + return true; + + if (sd_booted() <= 0) + return true; + + if (!isempty(arg_root)) + return true; + + if (arg_scope == LOOKUP_SCOPE_GLOBAL) + return true; + + /* Unsupported environment variable, mostly for debugging purposes */ + if (getenv_bool("SYSTEMCTL_INSTALL_CLIENT_SIDE") > 0) + return true; + + return false; +} + +int output_table(Table *table) { + int r; + + assert(table); + + if (OUTPUT_MODE_IS_JSON(arg_output)) + r = table_print_json(table, NULL, output_mode_to_json_format_flags(arg_output) | JSON_FORMAT_COLOR_AUTO); + else + r = table_print(table, NULL); + if (r < 0) + return table_log_print_error(r); + + return 0; +} + +bool show_preset_for_state(UnitFileState state) { + /* Don't show preset state in those unit file states, it'll only confuse users. */ + return !IN_SET(state, + UNIT_FILE_ALIAS, + UNIT_FILE_STATIC, + UNIT_FILE_GENERATED, + UNIT_FILE_TRANSIENT); +} + +UnitFileFlags unit_file_flags_from_args(void) { + return (arg_runtime ? UNIT_FILE_RUNTIME : 0) | + (arg_force ? UNIT_FILE_FORCE : 0); +} + +int mangle_names(const char *operation, char **original_names, char ***ret_mangled_names) { + _cleanup_strv_free_ char **l = NULL; + char **i; + int r; + + assert(ret_mangled_names); + + l = i = new(char*, strv_length(original_names) + 1); + if (!l) + return log_oom(); + + STRV_FOREACH(name, original_names) { + + /* When enabling units qualified path names are OK, too, hence allow them explicitly. */ + + if (is_path(*name)) + r = path_make_absolute_cwd(*name, i); + else + r = unit_name_mangle_with_suffix(*name, operation, + arg_quiet ? 0 : UNIT_NAME_MANGLE_WARN, + ".service", i); + if (r < 0) { + *i = NULL; + return log_error_errno(r, "Failed to mangle unit name or path '%s': %m", *name); + } + + i++; + } + + *i = NULL; + *ret_mangled_names = TAKE_PTR(l); + + return 0; +} + +int halt_now(enum action a) { + /* The kernel will automatically flush ATA disks and suchlike on reboot(), but the file systems need + * to be synced explicitly in advance. */ + if (!arg_no_sync && !arg_dry_run) + (void) sync(); + + /* Make sure C-A-D is handled by the kernel from this point on... */ + if (!arg_dry_run) + (void) reboot(RB_ENABLE_CAD); + + switch (a) { + + case ACTION_HALT: + if (!arg_quiet) + log_info("Halting."); + if (arg_dry_run) + return 0; + (void) reboot(RB_HALT_SYSTEM); + return -errno; + + case ACTION_POWEROFF: + if (!arg_quiet) + log_info("Powering off."); + if (arg_dry_run) + return 0; + (void) reboot(RB_POWER_OFF); + return -errno; + + case ACTION_KEXEC: + case ACTION_REBOOT: + return reboot_with_parameter(REBOOT_FALLBACK | + (arg_quiet ? 0 : REBOOT_LOG) | + (arg_dry_run ? REBOOT_DRY_RUN : 0)); + + default: + assert_not_reached(); + } +} |