chiark / gitweb /
journal: fix memory leak
[elogind.git] / src / systemctl.c
index b72fcbf8076225f9d080a4c6cf36d051831161bb..8f99a724b816a9415d0d5c776a0fddc98d674c33 100644 (file)
@@ -77,7 +77,7 @@ static bool arg_no_reload = false;
 static bool arg_dry = false;
 static bool arg_quiet = false;
 static bool arg_full = false;
-static bool arg_force = false;
+static int arg_force = 0;
 static bool arg_ask_password = false;
 static bool arg_failed = false;
 static bool arg_runtime = false;
@@ -126,6 +126,7 @@ static OutputMode arg_output = OUTPUT_SHORT;
 static bool private_bus = false;
 
 static int daemon_reload(DBusConnection *bus, char **args);
+static void halt_now(enum action a);
 
 static bool on_tty(void) {
         static int t = -1;
@@ -166,12 +167,12 @@ static void agent_open_if_enabled(void) {
         agent_open();
 }
 
-static const char *ansi_highlight(bool b) {
+static const char *ansi_highlight_red(bool b) {
 
         if (!on_tty())
                 return "";
 
-        return b ? ANSI_HIGHLIGHT_ON : ANSI_HIGHLIGHT_OFF;
+        return b ? ANSI_HIGHLIGHT_RED_ON : ANSI_HIGHLIGHT_OFF;
 }
 
 static const char *ansi_highlight_green(bool b) {
@@ -225,7 +226,7 @@ static int translate_bus_error_to_exit_status(int r, const DBusError *error) {
         return EXIT_FAILURE;
 }
 
-static void warn_wall(enum action action) {
+static void warn_wall(enum action a) {
         static const char *table[_ACTION_MAX] = {
                 [ACTION_HALT]      = "The system is going down for system halt NOW!",
                 [ACTION_REBOOT]    = "The system is going down for reboot NOW!",
@@ -255,10 +256,10 @@ static void warn_wall(enum action action) {
                 free(p);
         }
 
-        if (!table[action])
+        if (!table[a])
                 return;
 
-        utmp_wall(table[action], NULL);
+        utmp_wall(table[a], NULL);
 }
 
 static bool avoid_bus(void) {
@@ -383,14 +384,14 @@ static void output_units_list(const struct unit_info *unit_infos, unsigned c) {
                 n_shown++;
 
                 if (streq(u->load_state, "error")) {
-                        on_loaded = ansi_highlight(true);
-                        off_loaded = ansi_highlight(false);
+                        on_loaded = ansi_highlight_red(true);
+                        off_loaded = ansi_highlight_red(false);
                 } else
                         on_loaded = off_loaded = "";
 
                 if (streq(u->active_state, "failed")) {
-                        on_active = ansi_highlight(true);
-                        off_active = ansi_highlight(false);
+                        on_active = ansi_highlight_red(true);
+                        off_active = ansi_highlight_red(false);
                 } else
                         on_active = off_active = "";
 
@@ -593,8 +594,8 @@ static void output_unit_file_list(const UnitFileList *units, unsigned c) {
                 if (u->state == UNIT_FILE_MASKED ||
                     u->state == UNIT_FILE_MASKED_RUNTIME ||
                     u->state == UNIT_FILE_DISABLED) {
-                        on  = ansi_highlight(true);
-                        off = ansi_highlight(false);
+                        on  = ansi_highlight_red(true);
+                        off = ansi_highlight_red(false);
                 } else if (u->state == UNIT_FILE_ENABLED) {
                         on  = ansi_highlight_green(true);
                         off = ansi_highlight_green(false);
@@ -1437,9 +1438,9 @@ static int wait_for_jobs(DBusConnection *bus, Set *s) {
                 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.");
+                        log_error("A dependency job failed. See system journal for details.");
                 else if (!streq(d.result, "done") && !streq(d.result, "skipped"))
-                        log_error("Job failed. See system logs and 'systemctl status' for details.");
+                        log_error("Job failed. See system journal and 'systemctl status' for details.");
         }
 
         if (streq_ptr(d.result, "timeout"))
@@ -1681,24 +1682,125 @@ finish:
         return ret;
 }
 
+/* ask systemd-logind, which might grant access to unprivileged users through polkit */
+static int reboot_with_logind(DBusConnection *bus, enum action a) {
+#ifdef HAVE_LOGIND
+        const char *method;
+        DBusMessage *m = NULL, *reply = NULL;
+        DBusError error;
+        dbus_bool_t interactive = true;
+        int r;
+
+        dbus_error_init(&error);
+
+        switch (a) {
+
+        case ACTION_REBOOT:
+                method = "Reboot";
+                break;
+
+        case ACTION_POWEROFF:
+                method = "PowerOff";
+                break;
+
+        default:
+                return -EINVAL;
+        }
+
+        m = dbus_message_new_method_call(
+                                "org.freedesktop.login1",
+                                "/org/freedesktop/login1",
+                                "org.freedesktop.login1.Manager",
+                                method);
+        if (!m) {
+                log_error("Could not allocate message.");
+                r = -ENOMEM;
+                goto finish;
+        }
+
+        if (!dbus_message_append_args(m,
+                                      DBUS_TYPE_BOOLEAN, &interactive,
+                                      DBUS_TYPE_INVALID)) {
+                log_error("Could not append arguments to message.");
+                r = -ENOMEM;
+                goto finish;
+        }
+
+        reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
+        if (!reply) {
+                if (error_is_no_service(&error)) {
+                        log_debug("Failed to issue method call: %s", bus_error_message(&error));
+                        r = -ENOENT;
+                        goto finish;
+                }
+
+                if (dbus_error_has_name(&error, DBUS_ERROR_ACCESS_DENIED)) {
+                        log_debug("Failed to issue method call: %s", bus_error_message(&error));
+                        r = -EACCES;
+                        goto finish;
+                }
+
+                log_info("Failed to issue method call: %s", bus_error_message(&error));
+                r = -EIO;
+                goto finish;
+        }
+
+        r = 0;
+
+finish:
+        if (m)
+                dbus_message_unref(m);
+
+        if (reply)
+                dbus_message_unref(reply);
+
+        dbus_error_free(&error);
+
+        return r;
+#else
+        return -ENOSYS;
+#endif
+}
+
 static int start_special(DBusConnection *bus, char **args) {
+        enum action a;
         int r;
 
         assert(bus);
         assert(args);
 
+        a = verb_to_action(args[0]);
+
+        if (arg_force >= 2 && a == ACTION_HALT)
+                halt_now(ACTION_HALT);
+
+        if (arg_force >= 2 && a == ACTION_POWEROFF)
+                halt_now(ACTION_POWEROFF);
+
+        if (arg_force >= 2 && a == ACTION_REBOOT)
+                halt_now(ACTION_REBOOT);
+
         if (arg_force &&
-            (streq(args[0], "halt") ||
-             streq(args[0], "poweroff") ||
-             streq(args[0], "reboot") ||
-             streq(args[0], "kexec") ||
-             streq(args[0], "exit")))
+            (a == ACTION_HALT ||
+             a == ACTION_POWEROFF ||
+             a == ACTION_REBOOT ||
+             a == ACTION_KEXEC ||
+             a == ACTION_EXIT))
                 return daemon_reload(bus, args);
 
-        r = start_unit(bus, args);
+        if (geteuid() != 0) {
+                /* first try logind, to allow authentication with polkit */
+                if (a == ACTION_POWEROFF ||
+                    a == ACTION_REBOOT) {
+                        r = reboot_with_logind(bus, a);
+                        if (r >= 0)
+                                return r;
+                }
+        }
 
+        r = start_unit(bus, args);
         if (r >= 0)
-                warn_wall(verb_to_action(args[0]));
+                warn_wall(a);
 
         return r;
 }
@@ -2003,6 +2105,7 @@ typedef struct UnitStatusInfo {
         const char *default_control_group;
 
         const char *load_error;
+        const char *result;
 
         usec_t inactive_exit_timestamp;
         usec_t inactive_exit_timestamp_monotonic;
@@ -2069,8 +2172,8 @@ static void print_status_info(UnitStatusInfo *i) {
                 printf("\t  Follow: unit currently follows state of %s\n", i->following);
 
         if (streq_ptr(i->load_state, "error")) {
-                on = ansi_highlight(true);
-                off = ansi_highlight(false);
+                on = ansi_highlight_red(true);
+                off = ansi_highlight_red(false);
         } else
                 on = off = "";
 
@@ -2086,8 +2189,8 @@ static void print_status_info(UnitStatusInfo *i) {
         ss = streq_ptr(i->active_state, i->sub_state) ? NULL : i->sub_state;
 
         if (streq_ptr(i->active_state, "failed")) {
-                on = ansi_highlight(true);
-                off = ansi_highlight(false);
+                on = ansi_highlight_red(true);
+                off = ansi_highlight_red(false);
         } else if (streq_ptr(i->active_state, "active") || streq_ptr(i->active_state, "reloading")) {
                 on = ansi_highlight_green(true);
                 off = ansi_highlight_green(false);
@@ -2106,6 +2209,9 @@ static void print_status_info(UnitStatusInfo *i) {
                        strna(i->active_state),
                        off);
 
+        if (!isempty(i->result) && !streq(i->result, "success"))
+                printf(" (Result: %s)", i->result);
+
         timestamp = (streq_ptr(i->active_state, "active")      ||
                      streq_ptr(i->active_state, "reloading"))   ? i->active_enter_timestamp :
                     (streq_ptr(i->active_state, "inactive")    ||
@@ -2163,8 +2269,8 @@ static void print_status_info(UnitStatusInfo *i) {
                         good = is_clean_exit(p->code, p->status);
 
                 if (!good) {
-                        on = ansi_highlight(true);
-                        off = ansi_highlight(false);
+                        on = ansi_highlight_red(true);
+                        off = ansi_highlight_red(false);
                 } else
                         on = off = "";
 
@@ -2263,7 +2369,7 @@ static void print_status_info(UnitStatusInfo *i) {
                         else
                                 c = 0;
 
-                        show_cgroup_by_path(i->default_control_group, "\t\t  ", c);
+                        show_cgroup_by_path(i->default_control_group, "\t\t  ", c, false);
                 }
         }
 
@@ -2274,8 +2380,8 @@ static void print_status_info(UnitStatusInfo *i) {
 
         if (i->need_daemon_reload)
                 printf("\n%sWarning:%s Unit file changed on disk, 'systemctl %s daemon-reload' recommended.\n",
-                       ansi_highlight(true),
-                       ansi_highlight(false),
+                       ansi_highlight_red(true),
+                       ansi_highlight_red(false),
                        arg_scope == UNIT_FILE_SYSTEM ? "--system" : "--user");
 }
 
@@ -2325,6 +2431,8 @@ static int status_property(const char *name, DBusMessageIter *iter, UnitStatusIn
                                 i->following = s;
                         else if (streq(name, "UnitFileState"))
                                 i->unit_file_state = s;
+                        else if (streq(name, "Result"))
+                                i->result = s;
                 }
 
                 break;
@@ -3656,12 +3764,15 @@ static int enable_unit(DBusConnection *bus, char **args) {
         int r;
         DBusError error;
 
-        dbus_error_init(&error);
-
         r = enable_sysv_units(args);
         if (r < 0)
                 return r;
 
+        if (!args[1])
+                return 0;
+
+        dbus_error_init(&error);
+
         if (!bus || avoid_bus()) {
                 if (streq(verb, "enable")) {
                         r = unit_file_enable(arg_scope, arg_runtime, arg_root, args+1, arg_force, &changes, &n_changes);
@@ -3688,11 +3799,13 @@ static int enable_unit(DBusConnection *bus, char **args) {
                         goto finish;
                 }
 
-                for (i = 0; i < n_changes; i++) {
-                        if (changes[i].type == UNIT_FILE_SYMLINK)
-                                log_info("ln -s '%s' '%s'", changes[i].source, changes[i].path);
-                        else
-                                log_info("rm '%s'", changes[i].path);
+                if (!arg_quiet) {
+                        for (i = 0; i < n_changes; i++) {
+                                if (changes[i].type == UNIT_FILE_SYMLINK)
+                                        log_info("ln -s '%s' '%s'", changes[i].source, changes[i].path);
+                                else
+                                        log_info("rm '%s'", changes[i].path);
+                        }
                 }
 
         } else {
@@ -3808,10 +3921,12 @@ static int enable_unit(DBusConnection *bus, char **args) {
                                 goto finish;
                         }
 
-                        if (streq(type, "symlink"))
-                                log_info("ln -s '%s' '%s'", source, path);
-                        else
-                                log_info("rm '%s'", path);
+                        if (!arg_quiet) {
+                                if (streq(type, "symlink"))
+                                        log_info("ln -s '%s' '%s'", source, path);
+                                else
+                                        log_info("rm '%s'", path);
+                        }
 
                         dbus_message_iter_next(&sub);
                 }
@@ -4278,7 +4393,7 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
                         break;
 
                 case 'f':
-                        arg_force = true;
+                        arg_force ++;
                         break;
 
                 case ARG_NO_RELOAD:
@@ -5158,10 +5273,47 @@ done:
         return 0;
 }
 
+static void halt_now(enum action a) {
+
+       /* Make sure C-A-D is handled by the kernel from this
+         * point on... */
+        reboot(RB_ENABLE_CAD);
+
+        switch (a) {
+
+        case ACTION_HALT:
+                log_info("Halting.");
+                reboot(RB_HALT_SYSTEM);
+                break;
+
+        case ACTION_POWEROFF:
+                log_info("Powering off.");
+                reboot(RB_POWER_OFF);
+                break;
+
+        case ACTION_REBOOT:
+                log_info("Rebooting.");
+                reboot(RB_AUTOBOOT);
+                break;
+
+        default:
+                assert_not_reached("Unknown halt action.");
+        }
+
+        assert_not_reached("Uh? This shouldn't happen.");
+}
+
 static int halt_main(DBusConnection *bus) {
         int r;
 
         if (geteuid() != 0) {
+                if (arg_action == ACTION_POWEROFF ||
+                    arg_action == ACTION_REBOOT) {
+                        r = reboot_with_logind(bus, arg_action);
+                        if (r >= 0)
+                                return r;
+                }
+
                 log_error("Must be root.");
                 return -EPERM;
         }
@@ -5205,31 +5357,7 @@ static int halt_main(DBusConnection *bus) {
         if (arg_dry)
                 return 0;
 
-        /* Make sure C-A-D is handled by the kernel from this
-         * point on... */
-        reboot(RB_ENABLE_CAD);
-
-        switch (arg_action) {
-
-        case ACTION_HALT:
-                log_info("Halting.");
-                reboot(RB_HALT_SYSTEM);
-                break;
-
-        case ACTION_POWEROFF:
-                log_info("Powering off.");
-                reboot(RB_POWER_OFF);
-                break;
-
-        case ACTION_REBOOT:
-                log_info("Rebooting.");
-                reboot(RB_AUTOBOOT);
-                break;
-
-        default:
-                assert_not_reached("Unknown halt action.");
-        }
-
+        halt_now(arg_action);
         /* We should never reach this. */
         return -ENOSYS;
 }