chiark / gitweb /
systemctl: generalize action table to be usable in more action<->verb mappings
[elogind.git] / src / systemctl / systemctl.c
index becd68ff6e8215dcd33ad4572e24b92a65651958..8174376257e6e1600d442d337b04864f5c8bc109 100644 (file)
@@ -1336,7 +1336,9 @@ static void check_triggering_units(
         _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
         DBusMessageIter iter, sub;
         const char *interface = "org.freedesktop.systemd1.Unit",
-                   *triggered_by_property = "TriggeredBy";
+                   *load_state_property = "LoadState",
+                   *triggered_by_property = "TriggeredBy",
+                   *state;
         char _cleanup_free_ *unit_path = NULL, *n = NULL;
         bool print_warning_label = true;
         int r;
@@ -1353,6 +1355,41 @@ static void check_triggering_units(
                 return;
         }
 
+        r = bus_method_call_with_reply(
+                        bus,
+                        "org.freedesktop.systemd1",
+                        unit_path,
+                        "org.freedesktop.DBus.Properties",
+                        "Get",
+                        &reply,
+                        NULL,
+                        DBUS_TYPE_STRING, &interface,
+                        DBUS_TYPE_STRING, &load_state_property,
+                        DBUS_TYPE_INVALID);
+        if (r < 0)
+                return;
+
+        if (!dbus_message_iter_init(reply, &iter) ||
+            dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) {
+                log_error("Failed to parse reply.");
+                return;
+        }
+
+        dbus_message_iter_recurse(&iter, &sub);
+
+        if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRING)  {
+            log_error("Failed to parse reply.");
+            return;
+        }
+
+        dbus_message_iter_get_basic(&sub, &state);
+
+        if (streq(state, "masked"))
+            return;
+
+        dbus_message_unref(reply);
+        reply = NULL;
+
         r = bus_method_call_with_reply(
                         bus,
                         "org.freedesktop.systemd1",
@@ -1481,53 +1518,39 @@ static int start_unit_one(
         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) {
-        if (streq(verb, "halt"))
-                return ACTION_HALT;
-        else if (streq(verb, "poweroff"))
-                return ACTION_POWEROFF;
-        else if (streq(verb, "reboot"))
-                return ACTION_REBOOT;
-        else if (streq(verb, "kexec"))
-                return ACTION_KEXEC;
-        else if (streq(verb, "rescue"))
-                return ACTION_RESCUE;
-        else if (streq(verb, "emergency"))
-                return ACTION_EMERGENCY;
-        else if (streq(verb, "default"))
-                return ACTION_DEFAULT;
-        else if (streq(verb, "exit"))
-                return ACTION_EXIT;
-        else if (streq(verb, "suspend"))
-                return ACTION_SUSPEND;
-        else if (streq(verb, "hibernate"))
-                return ACTION_HIBERNATE;
-        else if (streq(verb, "hybrid-sleep"))
-                return ACTION_HYBRID_SLEEP;
-        else
-                return ACTION_INVALID;
+        enum action i;
+
+        for (i = ACTION_INVALID; i < _ACTION_MAX; i++)
+                if (action_table[i].verb && streq(verb, action_table[i].verb))
+                        return i;
+        return ACTION_INVALID;
 }
 
 static int start_unit(DBusConnection *bus, char **args) {
 
-        static const char * const table[_ACTION_MAX] = {
-                [ACTION_HALT] = SPECIAL_HALT_TARGET,
-                [ACTION_POWEROFF] = SPECIAL_POWEROFF_TARGET,
-                [ACTION_REBOOT] = SPECIAL_REBOOT_TARGET,
-                [ACTION_KEXEC] = SPECIAL_KEXEC_TARGET,
-                [ACTION_RUNLEVEL2] = SPECIAL_RUNLEVEL2_TARGET,
-                [ACTION_RUNLEVEL3] = SPECIAL_RUNLEVEL3_TARGET,
-                [ACTION_RUNLEVEL4] = SPECIAL_RUNLEVEL4_TARGET,
-                [ACTION_RUNLEVEL5] = SPECIAL_RUNLEVEL5_TARGET,
-                [ACTION_RESCUE] = SPECIAL_RESCUE_TARGET,
-                [ACTION_EMERGENCY] = SPECIAL_EMERGENCY_TARGET,
-                [ACTION_DEFAULT] = SPECIAL_DEFAULT_TARGET,
-                [ACTION_EXIT] = SPECIAL_EXIT_TARGET,
-                [ACTION_SUSPEND] = SPECIAL_SUSPEND_TARGET,
-                [ACTION_HIBERNATE] = SPECIAL_HIBERNATE_TARGET,
-                [ACTION_HYBRID_SLEEP] = SPECIAL_HYBRID_SLEEP_TARGET
-        };
-
         int r, ret = 0;
         const char *method, *mode, *one_name;
         Set _cleanup_set_free_free_ *s = NULL;
@@ -1541,6 +1564,7 @@ static int start_unit(DBusConnection *bus, char **args) {
         ask_password_agent_open_if_enabled();
 
         if (arg_action == ACTION_SYSTEMCTL) {
+                enum action action;
                 method =
                         streq(args[0], "stop") ||
                         streq(args[0], "condstop")              ? "StopUnit" :
@@ -1557,28 +1581,21 @@ static int start_unit(DBusConnection *bus, char **args) {
 
                         streq(args[0], "force-reload")          ? "ReloadOrTryRestartUnit" :
                                                                   "StartUnit";
+                action = verb_to_action(args[0]);
 
-                mode =
-                        (streq(args[0], "isolate") ||
-                         streq(args[0], "rescue")  ||
-                         streq(args[0], "emergency")) ? "isolate" : arg_job_mode;
+                mode = streq(args[0], "isolate") ? "isolate" :
+                       action_table[action].mode ?: arg_job_mode;
 
-                one_name = table[verb_to_action(args[0])];
+                one_name = action_table[action].target;
 
         } else {
-                assert(arg_action < ELEMENTSOF(table));
-                assert(table[arg_action]);
+                assert(arg_action < ELEMENTSOF(action_table));
+                assert(action_table[arg_action].target);
 
                 method = "StartUnit";
 
-                mode = (arg_action == ACTION_EMERGENCY ||
-                        arg_action == ACTION_RESCUE ||
-                        arg_action == ACTION_RUNLEVEL2 ||
-                        arg_action == ACTION_RUNLEVEL3 ||
-                        arg_action == ACTION_RUNLEVEL4 ||
-                        arg_action == ACTION_RUNLEVEL5) ? "isolate" : "replace";
-
-                one_name = table[arg_action];
+                mode = action_table[arg_action].mode;
+                one_name = action_table[arg_action].target;
         }
 
         if (!arg_no_block) {
@@ -1801,12 +1818,7 @@ static int check_inhibitors(DBusConnection *bus, enum action a) {
                 return 0;
 
         log_error("Please retry operation after closing inhibitors and logging out other users.\nAlternatively, ignore inhibitors and users with 'systemctl %s -i'.",
-                  a == ACTION_HALT ? "halt" :
-                  a == ACTION_POWEROFF ? "poweroff" :
-                  a == ACTION_REBOOT ? "reboot" :
-                  a == ACTION_KEXEC ? "kexec" :
-                  a == ACTION_SUSPEND ? "suspend" :
-                  a == ACTION_HIBERNATE ? "hibernate" : "hybrid-sleep");
+                  action_table[a].verb);
 
         return -EPERM;
 #else
@@ -4268,6 +4280,7 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
 
         enum {
                 ARG_FAIL = 0x100,
+                ARG_IRREVERSIBLE,
                 ARG_IGNORE_DEPENDENCIES,
                 ARG_VERSION,
                 ARG_USER,
@@ -4296,6 +4309,7 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
                 { "failed",    no_argument,       NULL, ARG_FAILED    },
                 { "full",      no_argument,       NULL, ARG_FULL      },
                 { "fail",      no_argument,       NULL, ARG_FAIL      },
+                { "irreversible", no_argument,    NULL, ARG_IRREVERSIBLE },
                 { "ignore-dependencies", no_argument, NULL, ARG_IGNORE_DEPENDENCIES },
                 { "ignore-inhibitors", no_argument, NULL, 'i'         },
                 { "user",      no_argument,       NULL, ARG_USER      },
@@ -4357,18 +4371,33 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
                         log_info("Use -t help to see a list of allowed values.");
                         return -EINVAL;
                 case 'p': {
-                        char **l;
+                        char *word, *state;
+                        size_t size;
+                        /* Make sure that if the empty property list
+                           was specified, we won't show any properties. */
+                        const char *source = isempty(optarg) ? " " : optarg;
+
+                        FOREACH_WORD_SEPARATOR(word, size, source, ",", state) {
+                                char _cleanup_free_ *prop;
+                                char **tmp;
+
+                                prop = strndup(word, size);
+                                if (!prop)
+                                        return -ENOMEM;
 
-                        if (!(l = strv_append(arg_property, optarg)))
-                                return -ENOMEM;
+                                tmp = strv_append(arg_property, prop);
+                                if (!tmp)
+                                        return -ENOMEM;
 
-                        strv_free(arg_property);
-                        arg_property = l;
+                                strv_free(arg_property);
+                                arg_property = tmp;
+                        }
 
                         /* If the user asked for a particular
                          * property, show it to him, even if it is
                          * empty. */
                         arg_all = true;
+
                         break;
                 }
 
@@ -4380,6 +4409,10 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
                         arg_job_mode = "fail";
                         break;
 
+                case ARG_IRREVERSIBLE:
+                        arg_job_mode = "replace-irreversibly";
+                        break;
+
                 case ARG_IGNORE_DEPENDENCIES:
                         arg_job_mode = "ignore-dependencies";
                         break;