chiark / gitweb /
systemctl: properly handle job results
[elogind.git] / src / systemctl.c
index 4a8b9a196f3c286de07892268f896a034adf2ed4..b33a89f63d76d9bd3ae05d28db2f5b35a75aca4d 100644 (file)
@@ -1120,7 +1120,7 @@ finish:
 
 typedef struct WaitData {
         Set *set;
-        bool failed;
+        char *result;
 } WaitData;
 
 static DBusHandlerResult wait_filter(DBusConnection *connection, DBusMessage *message, void *data) {
@@ -1144,26 +1144,52 @@ static DBusHandlerResult wait_filter(DBusConnection *connection, DBusMessage *me
 
         } else if (dbus_message_is_signal(message, "org.freedesktop.systemd1.Manager", "JobRemoved")) {
                 uint32_t id;
-                const char *path;
+                const char *path, *result;
                 dbus_bool_t success = true;
 
-                if (!dbus_message_get_args(message, &error,
-                                           DBUS_TYPE_UINT32, &id,
-                                           DBUS_TYPE_OBJECT_PATH, &path,
-                                           DBUS_TYPE_BOOLEAN, &success,
-                                           DBUS_TYPE_INVALID))
-                        log_error("Failed to parse message: %s", bus_error_message(&error));
-                else {
+                if (dbus_message_get_args(message, &error,
+                                          DBUS_TYPE_UINT32, &id,
+                                          DBUS_TYPE_OBJECT_PATH, &path,
+                                          DBUS_TYPE_STRING, &result,
+                                          DBUS_TYPE_INVALID)) {
                         char *p;
 
+                        if ((p = set_remove(d->set, (char*) path)))
+                                free(p);
+
+                        if (*result)
+                                d->result = strdup(result);
+
+                        goto finish;
+                }
+#ifndef LEGACY
+                dbus_error_free(&error);
+
+                if (dbus_message_get_args(message, &error,
+                                          DBUS_TYPE_UINT32, &id,
+                                          DBUS_TYPE_OBJECT_PATH, &path,
+                                          DBUS_TYPE_BOOLEAN, &success,
+                                          DBUS_TYPE_INVALID)) {
+                        char *p;
+
+                        /* Compatibility with older systemd versions <
+                         * 19 during upgrades. This should be dropped
+                         * one day */
+
                         if ((p = set_remove(d->set, (char*) path)))
                                 free(p);
 
                         if (!success)
-                                d->failed = true;
+                                d->result = strdup("failed");
+
+                        goto finish;
                 }
+#endif
+
+                log_error("Failed to parse message: %s", bus_error_message(&error));
         }
 
+finish:
         dbus_error_free(&error);
         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
 }
@@ -1204,7 +1230,6 @@ static int wait_for_jobs(DBusConnection *bus, Set *s) {
 
         zero(d);
         d.set = s;
-        d.failed = false;
 
         if (!dbus_connection_add_filter(bus, wait_filter, &d, NULL)) {
                 log_error("Failed to add filter.");
@@ -1216,10 +1241,27 @@ static int wait_for_jobs(DBusConnection *bus, Set *s) {
                dbus_connection_read_write_dispatch(bus, -1))
                 ;
 
-        if (!arg_quiet && d.failed)
-                log_error("Job failed. See system logs and 'systemctl status' for details.");
+        if (!arg_quiet && d.result) {
+                if (streq(d.result, "timeout"))
+                        log_error("Job timed out.");
+                else if (streq(d.result, "canceled"))
+                        log_error("Job canceled.");
+                else if (streq(d.result, "dependency"))
+                        log_error("A dependency job failed. See system logs for details.");
+                else if (!streq(d.result, "done"))
+                        log_error("Job failed. See system logs and 'systemctl status' for details.");
+        }
+
+        if (streq_ptr(d.result, "timeout"))
+                r = -ETIME;
+        else if (streq_ptr(d.result, "canceled"))
+                r = -ECANCELED;
+        else if (!streq_ptr(d.result, "done"))
+                r = -EIO;
+        else
+                r = 0;
 
-        r = d.failed ? -EIO : 0;
+        free(d.result);
 
 finish:
         /* This is slightly dirty, since we don't undo the filter registration. */
@@ -2750,11 +2792,12 @@ static DBusHandlerResult monitor_filter(DBusConnection *connection, DBusMessage
         } else if (dbus_message_is_signal(message, "org.freedesktop.systemd1.Manager", "JobNew") ||
                    dbus_message_is_signal(message, "org.freedesktop.systemd1.Manager", "JobRemoved")) {
                 uint32_t id;
-                const char *path;
+                const char *path, *result;
 
                 if (!dbus_message_get_args(message, &error,
                                            DBUS_TYPE_UINT32, &id,
                                            DBUS_TYPE_OBJECT_PATH, &path,
+                                           DBUS_TYPE_STRING, &result,
                                            DBUS_TYPE_INVALID))
                         log_error("Failed to parse message: %s", bus_error_message(&error));
                 else if (streq(dbus_message_get_member(message), "JobNew"))
@@ -4668,6 +4711,18 @@ static int parse_time_spec(const char *t, usec_t *_u) {
         return 0;
 }
 
+static bool kexec_loaded(void) {
+       bool loaded = false;
+       char *s;
+
+       if (read_one_line_file("/sys/kernel/kexec_loaded", &s) >= 0) {
+               if (s[0] == '1')
+                       loaded = true;
+               free(s);
+       }
+       return loaded;
+}
+
 static int shutdown_parse_argv(int argc, char *argv[]) {
 
         enum {
@@ -4705,7 +4760,10 @@ static int shutdown_parse_argv(int argc, char *argv[]) {
                         break;
 
                 case 'r':
-                        arg_action = ACTION_REBOOT;
+                        if (kexec_loaded())
+                                arg_action = ACTION_KEXEC;
+                        else
+                                arg_action = ACTION_REBOOT;
                         break;
 
                 case 'h':
@@ -4898,7 +4956,10 @@ static int parse_argv(int argc, char *argv[]) {
                         arg_action = ACTION_POWEROFF;
                         return halt_parse_argv(argc, argv);
                 } else if (strstr(program_invocation_short_name, "reboot")) {
-                        arg_action = ACTION_REBOOT;
+                        if (kexec_loaded())
+                                arg_action = ACTION_KEXEC;
+                        else
+                                arg_action = ACTION_REBOOT;
                         return halt_parse_argv(argc, argv);
                 } else if (strstr(program_invocation_short_name, "shutdown")) {
                         arg_action = ACTION_POWEROFF;
@@ -5498,6 +5559,7 @@ int main(int argc, char*argv[]) {
         case ACTION_HALT:
         case ACTION_POWEROFF:
         case ACTION_REBOOT:
+        case ACTION_KEXEC:
                 r = halt_main(bus);
                 break;