Systemd/src/systemctl/systemctl-util.c
Lennart Poettering daf71ef61c systemctl: split up humungous systemctl.c file
This is just some refactoring: shifting around of code, not change in
codeflow.

This splits up the way too huge systemctl.c in multiple more easily
digestable files. It roughly follows the rule that each family of verbs
gets its own .c/.h file pair, and so do all the compat executable names
we support. Plus three extra files for sysv compat (which existed before
already, but I renamed slightly, to get the systemctl- prefix lik
everything else), a -util file with generic stuff everything uses, and a
-logind file with everything that talks directly to logind instead of
PID1.

systemctl is still a bit too complex for my taste, but I think this way
itc omes in a more digestable bits at least.

No change of behaviour, just reshuffling of some code.
2020-10-07 23:12:15 +02:00

937 lines
30 KiB
C

/* SPDX-License-Identifier: LGPL-2.1+ */
#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 "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 != UNIT_FILE_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 log_error_errno(r, "Failed to connect to bus: %m");
(void) sd_bus_set_allow_interactive_authorization(buses[focus], arg_ask_password);
}
*ret = buses[focus];
return 0;
}
void release_busses(void) {
BusFocus w;
for (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 != UNIT_FILE_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 != UNIT_FILE_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(SYNTHETIC_ERRNO(EINVAL), "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;
size_t size = c;
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, size, 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;
char **name;
int r, i;
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 allocated, n;
r = get_unit_list(bus, NULL, globs, &unit_infos, 0, &reply);
if (r < 0)
return r;
n = strv_length(mangled);
allocated = n + 1;
for (i = 0; i < r; i++) {
if (!GREEDY_REALLOC(mangled, allocated, 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;
bool print_warning_label = true;
UnitActiveState active_state;
char **i;
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));
STRV_FOREACH(i, triggered_by) {
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 (print_warning_label) {
log_warning("Warning: Stopping %s, but it can still be activated by:", n);
print_warning_label = 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("%sWarning:%s The unit file, source configuration file or drop-ins of %s changed on disk. Run 'systemctl%s daemon-reload' to reload units.",
ansi_highlight_red(),
ansi_normal(),
unit,
arg_scope == UNIT_FILE_SYSTEM ? "" : " --user");
}
int unit_file_find_path(LookupPaths *lp, const char *unit_name, char **ret_unit_path) {
char **p;
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).
*/
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;
if (streq(load_state, "not-found")) {
r = 0;
goto not_found;
}
if (!STR_IN_SET(load_state, "loaded", "bad-setting"))
return -EKEYREJECTED;
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 r;
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;
}
}
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;
}
not_found:
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;
char **name;
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][6] = {
[DEPENDENCY_FORWARD] = {
{ "Requires", "as", NULL, 0 },
{ "Requisite", "as", NULL, 0 },
{ "Wants", "as", NULL, 0 },
{ "ConsistsOf", "as", NULL, 0 },
{ "BindsTo", "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 },
{}
},
[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_find(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 == UNIT_FILE_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, **name;
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)) {
*i = strdup(*name);
if (!*i)
return log_oom();
} 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: %m");
}
}
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("Unknown action.");
}
}