+ assert(name);
+
+ n = unit_name_mangle(name, MANGLE_NOGLOB);
+ if (!n)
+ return log_oom();
+
+ /* 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 = sd_bus_call_method(
+ bus,
+ "org.freedesktop.systemd1",
+ "/org/freedesktop/systemd1",
+ "org.freedesktop.systemd1.Manager",
+ "GetUnit",
+ NULL,
+ &reply,
+ "s", n);
+ if (r < 0) {
+ if (!quiet)
+ puts("unknown");
+ return 0;
+ }
+
+ r = sd_bus_message_read(reply, "o", &path);
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ r = sd_bus_get_property_string(
+ bus,
+ "org.freedesktop.systemd1",
+ path,
+ "org.freedesktop.systemd1.Unit",
+ "ActiveState",
+ NULL,
+ &state);
+ if (r < 0) {
+ if (!quiet)
+ puts("unknown");
+ return 0;
+ }
+
+ if (!quiet)
+ puts(state);
+
+ return nulstr_contains(good_states, state);
+}
+
+static int check_triggering_units(
+ sd_bus *bus,
+ const char *name) {
+
+ _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_free_ char *path = NULL, *n = NULL, *state = NULL;
+ _cleanup_strv_free_ char **triggered_by = NULL;
+ bool print_warning_label = true;
+ char **i;
+ int r;
+
+ n = unit_name_mangle(name, MANGLE_NOGLOB);
+ if (!n)
+ return log_oom();
+
+ path = unit_dbus_path_from_name(n);
+ if (!path)
+ return log_oom();
+
+ r = sd_bus_get_property_string(
+ bus,
+ "org.freedesktop.systemd1",
+ path,
+ "org.freedesktop.systemd1.Unit",
+ "LoadState",
+ &error,
+ &state);
+ if (r < 0) {
+ log_error("Failed to get load state of %s: %s", n, bus_error_message(&error, r));
+ return r;
+ }
+
+ if (streq(state, "masked"))
+ return 0;
+
+ r = sd_bus_get_property_strv(
+ bus,
+ "org.freedesktop.systemd1",
+ path,
+ "org.freedesktop.systemd1.Unit",
+ "TriggeredBy",
+ &error,
+ &triggered_by);
+ if (r < 0) {
+ log_error("Failed to get triggered by array of %s: %s", n, bus_error_message(&error, r));
+ return r;
+ }
+
+ STRV_FOREACH(i, triggered_by) {
+ r = check_one_unit(bus, *i, "active\0reloading\0", true);
+ if (r < 0)
+ return log_error_errno(r, "Failed to check unit: %m");
+
+ if (r == 0)
+ 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;
+}
+
+static const struct {
+ const char *verb;
+ const char *method;
+} unit_actions[] = {
+ { "start", "StartUnit" },
+ { "stop", "StopUnit" },
+ { "condstop", "StopUnit" },
+ { "reload", "ReloadUnit" },
+ { "restart", "RestartUnit" },
+ { "try-restart", "TryRestartUnit" },
+ { "condrestart", "TryRestartUnit" },
+ { "reload-or-restart", "ReloadOrRestartUnit" },
+ { "reload-or-try-restart", "ReloadOrTryRestartUnit" },
+ { "condreload", "ReloadOrTryRestartUnit" },
+ { "force-reload", "ReloadOrTryRestartUnit" }
+};
+
+static const char *verb_to_method(const char *verb) {
+ uint i;
+
+ for (i = 0; i < ELEMENTSOF(unit_actions); i++)
+ if (streq_ptr(unit_actions[i].verb, verb))
+ return unit_actions[i].method;
+
+ return "StartUnit";
+}
+
+static const char *method_to_verb(const char *method) {
+ uint i;
+
+ for (i = 0; i < ELEMENTSOF(unit_actions); i++)
+ if (streq_ptr(unit_actions[i].method, method))
+ return unit_actions[i].verb;
+
+ return "n/a";
+}
+
+static int start_unit_one(
+ sd_bus *bus,
+ const char *method,
+ const char *name,
+ const char *mode,
+ sd_bus_error *error,
+ Set *s) {
+
+ _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *reply = NULL;
+ const char *path;
+ int r;
+
+ assert(method);
+ assert(name);
+ assert(mode);
+ assert(error);
+
+ log_debug("Calling manager for %s on %s, %s", method, name, mode);
+
+ r = sd_bus_message_new_method_call(
+ bus,
+ &m,
+ "org.freedesktop.systemd1",
+ "/org/freedesktop/systemd1",
+ "org.freedesktop.systemd1.Manager",
+ method);
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_message_set_allow_interactive_authorization(m, arg_ask_password);
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_message_append(m, "ss", name, mode);
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_call(bus, m, 0, error, &reply);
+ if (r < 0) {
+ const char *verb;
+
+ if (r == -ENOENT && arg_action != ACTION_SYSTEMCTL)
+ /* There's always a fallback possible for
+ * legacy actions. */
+ return -EADDRNOTAVAIL;
+
+ verb = method_to_verb(method);
+
+ log_error("Failed to %s %s: %s", verb, name, bus_error_message(error, r));
+ return r;
+ }
+
+ r = sd_bus_message_read(reply, "o", &path);
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ if (need_daemon_reload(bus, name) > 0)
+ log_warning("Warning: Unit file of %s changed on disk, 'systemctl%s daemon-reload' recommended.",
+ name, arg_scope == UNIT_FILE_SYSTEM ? "" : " --user");
+
+ if (s) {
+ char *p;
+
+ p = strdup(path);
+ if (!p)
+ return log_oom();
+
+ log_debug("Adding %s to the set", p);
+ r = set_consume(s, p);
+ if (r < 0)
+ return log_oom();
+ }
+
+ return 0;
+}
+
+static int expand_names(sd_bus *bus, char **names, const char* suffix, char ***ret) {
+
+ _cleanup_strv_free_ char **mangled = NULL, **globs = NULL;
+ char **name;
+ int r = 0, i;
+
+ STRV_FOREACH(name, names) {
+ char *t;
+
+ if (suffix)
+ t = unit_name_mangle_with_suffix(*name, MANGLE_GLOB, suffix);
+ else
+ t = unit_name_mangle(*name, MANGLE_GLOB);
+ if (!t)
+ return log_oom();
+
+ 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 */
+ if (!strv_isempty(globs)) {
+ _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
+ _cleanup_free_ UnitInfo *unit_infos = NULL;
+
+ r = get_unit_list(bus, NULL, globs, &unit_infos, 0, &reply);
+ if (r < 0)
+ return r;
+
+ for (i = 0; i < r; i++)
+ if (strv_extend(&mangled, unit_infos[i].id) < 0)
+ return log_oom();
+ }
+
+ *ret = mangled;
+ mangled = NULL; /* do not free */
+
+ return 0;
+}
+
+static const struct {
+ const char *target;
+ const char *verb;
+ const char *mode;
+} action_table[_ACTION_MAX] = {
+ [ACTION_HALT] = { SPECIAL_HALT_TARGET, "halt", "replace-irreversibly" },
+ [ACTION_POWEROFF] = { SPECIAL_POWEROFF_TARGET, "poweroff", "replace-irreversibly" },
+ [ACTION_REBOOT] = { SPECIAL_REBOOT_TARGET, "reboot", "replace-irreversibly" },
+ [ACTION_KEXEC] = { SPECIAL_KEXEC_TARGET, "kexec", "replace-irreversibly" },
+ [ACTION_RUNLEVEL2] = { SPECIAL_RUNLEVEL2_TARGET, NULL, "isolate" },
+ [ACTION_RUNLEVEL3] = { SPECIAL_RUNLEVEL3_TARGET, NULL, "isolate" },
+ [ACTION_RUNLEVEL4] = { SPECIAL_RUNLEVEL4_TARGET, NULL, "isolate" },
+ [ACTION_RUNLEVEL5] = { SPECIAL_RUNLEVEL5_TARGET, NULL, "isolate" },
+ [ACTION_RESCUE] = { SPECIAL_RESCUE_TARGET, "rescue", "isolate" },
+ [ACTION_EMERGENCY] = { SPECIAL_EMERGENCY_TARGET, "emergency", "isolate" },
+ [ACTION_DEFAULT] = { SPECIAL_DEFAULT_TARGET, "default", "isolate" },
+ [ACTION_EXIT] = { SPECIAL_EXIT_TARGET, "exit", "replace-irreversibly" },
+ [ACTION_SUSPEND] = { SPECIAL_SUSPEND_TARGET, "suspend", "replace-irreversibly" },
+ [ACTION_HIBERNATE] = { SPECIAL_HIBERNATE_TARGET, "hibernate", "replace-irreversibly" },
+ [ACTION_HYBRID_SLEEP] = { SPECIAL_HYBRID_SLEEP_TARGET, "hybrid-sleep", "replace-irreversibly" },
+};
+
+static enum action verb_to_action(const char *verb) {
+ enum action i;
+
+ for (i = _ACTION_INVALID; i < _ACTION_MAX; i++)
+ if (streq_ptr(action_table[i].verb, verb))
+ return i;
+
+ return _ACTION_INVALID;
+}
+
+static int start_unit(sd_bus *bus, char **args) {
+ _cleanup_set_free_free_ Set *s = NULL;
+ _cleanup_strv_free_ char **names = NULL;
+ const char *method, *mode, *one_name, *suffix = NULL;
+ char **name;
+ int r = 0;
+
+ assert(bus);
+
+ ask_password_agent_open_if_enabled();
+
+ if (arg_action == ACTION_SYSTEMCTL) {
+ enum action action;
+ method = verb_to_method(args[0]);
+ action = verb_to_action(args[0]);
+
+ if (streq(args[0], "isolate")) {
+ mode = "isolate";
+ suffix = ".target";
+ } else
+ mode = action_table[action].mode ?: arg_job_mode;
+
+ one_name = action_table[action].target;