chiark / gitweb /
relicense to LGPLv2.1 (with exceptions)
[elogind.git] / src / systemctl.c
index fdff2d12506ba9aaa4e7daaa12b91125576f91b3..28bdfa96a4e06d9b9c393e7b3357dd96b28f79e8 100644 (file)
@@ -6,16 +6,16 @@
   Copyright 2010 Lennart Poettering
 
   systemd is free software; you can redistribute it and/or modify it
-  under the terms of the GNU General Public License as published by
-  the Free Software Foundation; either version 2 of the License, or
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
   (at your option) any later version.
 
   systemd is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  General Public License for more details.
+  Lesser General Public License for more details.
 
-  You should have received a copy of the GNU General Public License
+  You should have received a copy of the GNU Lesser General Public License
   along with systemd; If not, see <http://www.gnu.org/licenses/>.
 ***/
 
@@ -35,6 +35,9 @@
 #include <sys/prctl.h>
 #include <dbus/dbus.h>
 
+#include <systemd/sd-daemon.h>
+#include <systemd/sd-shutdown.h>
+
 #include "log.h"
 #include "util.h"
 #include "macro.h"
 #include "list.h"
 #include "path-lookup.h"
 #include "conf-parser.h"
-#include "sd-daemon.h"
-#include "shutdownd.h"
 #include "exit-status.h"
 #include "bus-errors.h"
 #include "build.h"
 #include "unit-name.h"
 #include "pager.h"
-#include "spawn-agent.h"
+#include "spawn-ask-password-agent.h"
+#include "spawn-polkit-agent.h"
 #include "install.h"
+#include "logs-show.h"
 
 static const char *arg_type = NULL;
 static char **arg_property = NULL;
@@ -66,6 +69,7 @@ static const char *arg_job_mode = "replace";
 static UnitFileScope arg_scope = UNIT_FILE_SYSTEM;
 static bool arg_immediate = false;
 static bool arg_no_block = false;
+static bool arg_no_legend = false;
 static bool arg_no_pager = false;
 static bool arg_no_wtmp = false;
 static bool arg_no_sync = false;
@@ -74,8 +78,8 @@ 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 bool arg_ask_password = false;
+static int arg_force = 0;
+static bool arg_ask_password = true;
 static bool arg_failed = false;
 static bool arg_runtime = false;
 static char **arg_wall = NULL;
@@ -116,10 +120,14 @@ static enum transport {
         TRANSPORT_POLKIT
 } arg_transport = TRANSPORT_NORMAL;
 static const char *arg_host = NULL;
+static bool arg_follow = false;
+static unsigned arg_lines = 10;
+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;
@@ -147,7 +155,7 @@ static void pager_open_if_enabled(void) {
         pager_open();
 }
 
-static void agent_open_if_enabled(void) {
+static void ask_password_agent_open_if_enabled(void) {
 
         /* Open the password agent as a child process if necessary */
 
@@ -157,15 +165,28 @@ static void agent_open_if_enabled(void) {
         if (arg_scope != UNIT_FILE_SYSTEM)
                 return;
 
-        agent_open();
+        ask_password_agent_open();
+}
+
+static void polkit_agent_open_if_enabled(void) {
+
+        /* Open the polkit agent as a child process if necessary */
+
+        if (!arg_ask_password)
+                return;
+
+        if (arg_scope != UNIT_FILE_SYSTEM)
+                return;
+
+        polkit_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) {
@@ -219,7 +240,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!",
@@ -235,7 +256,8 @@ static void warn_wall(enum action action) {
         if (arg_wall) {
                 char *p;
 
-                if (!(p = strv_join(arg_wall, " "))) {
+                p = strv_join(arg_wall, " ");
+                if (!p) {
                         log_error("Failed to join strings.");
                         return;
                 }
@@ -249,10 +271,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) {
@@ -314,35 +336,60 @@ static bool output_show_unit(const struct unit_info *u) {
 }
 
 static void output_units_list(const struct unit_info *unit_infos, unsigned c) {
-        unsigned active_len, sub_len, job_len, n_shown = 0;
+        unsigned id_len, max_id_len, active_len, sub_len, job_len, desc_len, n_shown = 0;
         const struct unit_info *u;
 
+        max_id_len = sizeof("UNIT")-1;
         active_len = sizeof("ACTIVE")-1;
         sub_len = sizeof("SUB")-1;
         job_len = sizeof("JOB")-1;
+        desc_len = 0;
 
         for (u = unit_infos; u < unit_infos + c; u++) {
                 if (!output_show_unit(u))
                         continue;
 
+                max_id_len = MAX(max_id_len, strlen(u->id));
                 active_len = MAX(active_len, strlen(u->active_state));
                 sub_len = MAX(sub_len, strlen(u->sub_state));
                 if (u->job_id != 0)
                         job_len = MAX(job_len, strlen(u->job_type));
         }
 
-        if (on_tty()) {
-                printf("%-25s %-6s %-*s %-*s %-*s", "UNIT", "LOAD",
+        if (!arg_full) {
+                unsigned basic_len;
+                id_len = MIN(max_id_len, 25);
+                basic_len = 5 + id_len + 6 + active_len + sub_len + job_len;
+                if (basic_len < (unsigned) columns()) {
+                        unsigned extra_len, incr;
+                        extra_len = columns() - basic_len;
+                        /* Either UNIT already got 25, or is fully satisfied.
+                         * Grant up to 25 to DESC now. */
+                        incr = MIN(extra_len, 25);
+                        desc_len += incr;
+                        extra_len -= incr;
+                        /* split the remaining space between UNIT and DESC,
+                         * but do not give UNIT more than it needs. */
+                        if (extra_len > 0) {
+                                incr = MIN(extra_len / 2, max_id_len - id_len);
+                                id_len += incr;
+                                desc_len += extra_len - incr;
+                        }
+                }
+        } else
+                id_len = max_id_len;
+
+        if (!arg_no_legend) {
+                printf("%-*s %-6s %-*s %-*s %-*s ", id_len, "UNIT", "LOAD",
                        active_len, "ACTIVE", sub_len, "SUB", job_len, "JOB");
-                if (columns() >= 80+12 || arg_full || !arg_no_pager)
-                        printf(" %s\n", "DESCRIPTION");
+                if (!arg_full && arg_no_pager)
+                        printf("%.*s\n", desc_len, "DESCRIPTION");
                 else
-                        printf("\n");
+                        printf("%s\n", "DESCRIPTION");
         }
 
         for (u = unit_infos; u < unit_infos + c; u++) {
                 char *e;
-                int a = 0, b = 0;
                 const char *on_loaded, *off_loaded;
                 const char *on_active, *off_active;
 
@@ -351,52 +398,35 @@ static void output_units_list(const struct unit_info *unit_infos, unsigned c) {
 
                 n_shown++;
 
-                if (!streq(u->load_state, "loaded") &&
-                    !streq(u->load_state, "banned")) {
-                        on_loaded = ansi_highlight(true);
-                        off_loaded = ansi_highlight(false);
+                if (streq(u->load_state, "error")) {
+                        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 = "";
 
-                e = arg_full ? NULL : ellipsize(u->id, 25, 33);
+                e = arg_full ? NULL : ellipsize(u->id, id_len, 33);
 
-                printf("%-25s %s%-6s%s %s%-*s %-*s%s%n",
-                       e ? e : u->id,
+                printf("%-*s %s%-6s%s %s%-*s %-*s%s %-*s ",
+                       id_len, e ? e : u->id,
                        on_loaded, u->load_state, off_loaded,
                        on_active, active_len, u->active_state,
                        sub_len, u->sub_state, off_active,
-                       &a);
-
-                free(e);
-
-                a -= strlen(on_loaded) + strlen(off_loaded);
-                a -= strlen(on_active) + strlen(off_active);
-
-                if (u->job_id != 0)
-                        printf(" %-*s", job_len, u->job_type);
+                       job_len, u->job_id ? u->job_type : "");
+                if (!arg_full && arg_no_pager)
+                        printf("%.*s\n", desc_len, u->description);
                 else
-                        b = 1 + job_len;
+                        printf("%s\n", u->description);
 
-                if (a + b + 1 < columns()) {
-                        if (u->job_id == 0)
-                                printf(" %-*s", job_len, "");
-
-                        if (arg_full || !arg_no_pager)
-                                printf(" %s", u->description);
-                        else
-                                printf(" %.*s", columns() - a - b - 1, u->description);
-                }
-
-                fputs("\n", stdout);
+                free(e);
         }
 
-        if (on_tty()) {
+        if (!arg_no_legend) {
                 printf("\nLOAD   = Reflects whether the unit definition was properly loaded.\n"
                        "ACTIVE = The high-level unit activation state, i.e. generalization of SUB.\n"
                        "SUB    = The low-level unit activation state, values depend on unit type.\n"
@@ -541,11 +571,30 @@ static bool output_show_unit_file(const UnitFileList *u) {
 }
 
 static void output_unit_file_list(const UnitFileList *units, unsigned c) {
-        unsigned n_shown = 0;
+        unsigned max_id_len, id_cols, state_cols, n_shown = 0;
         const UnitFileList *u;
 
-        if (on_tty())
-                printf("%-25s %-6s\n", "UNIT FILE", "STATE");
+        max_id_len = sizeof("UNIT FILE")-1;
+        state_cols = sizeof("STATE")-1;
+        for (u = units; u < units + c; u++) {
+                if (!output_show_unit_file(u))
+                        continue;
+
+                max_id_len = MAX(max_id_len, strlen(file_name_from_path(u->path)));
+                state_cols = MAX(state_cols, strlen(unit_file_state_to_string(u->state)));
+        }
+
+        if (!arg_full) {
+                unsigned basic_cols;
+                id_cols = MIN(max_id_len, 25);
+                basic_cols = 1 + id_cols + state_cols;
+                if (basic_cols < (unsigned) columns())
+                        id_cols += MIN(columns() - basic_cols, max_id_len - id_cols);
+        } else
+                id_cols = max_id_len;
+
+        if (!arg_no_legend)
+                printf("%-*s %-*s\n", id_cols, "UNIT FILE", state_cols, "STATE");
 
         for (u = units; u < units + c; u++) {
                 char *e;
@@ -560,8 +609,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);
@@ -570,16 +619,16 @@ static void output_unit_file_list(const UnitFileList *units, unsigned c) {
 
                 id = file_name_from_path(u->path);
 
-                e = arg_full ? NULL : ellipsize(id, 25, 33);
+                e = arg_full ? NULL : ellipsize(id, id_cols, 33);
 
-                printf("%-25s %s%-6s%s\n",
-                       e ? e : id,
-                       on, unit_file_state_to_string(u->state), off);
+                printf("%-*s %s%-*s%s\n",
+                       id_cols, e ? e : id,
+                       on, state_cols, unit_file_state_to_string(u->state), off);
 
                 free(e);
         }
 
-        if (on_tty())
+        if (!arg_no_legend)
                 printf("\n%u unit files listed.\n", n_shown);
 }
 
@@ -593,8 +642,6 @@ static int list_unit_files(DBusConnection *bus, char **args) {
 
         dbus_error_init(&error);
 
-        assert(bus);
-
         pager_open_if_enabled();
 
         if (avoid_bus()) {
@@ -630,6 +677,8 @@ static int list_unit_files(DBusConnection *bus, char **args) {
 
                 hashmap_free(h);
         } else {
+                assert(bus);
+
                 m = dbus_message_new_method_call(
                                 "org.freedesktop.systemd1",
                                 "/org/freedesktop/systemd1",
@@ -1404,9 +1453,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"))
@@ -1566,7 +1615,7 @@ static int start_unit(DBusConnection *bus, char **args) {
 
         assert(bus);
 
-        agent_open_if_enabled();
+        ask_password_agent_open_if_enabled();
 
         if (arg_action == ACTION_SYSTEMCTL) {
                 method =
@@ -1648,24 +1697,124 @@ finish:
         return ret;
 }
 
+/* Ask systemd-logind, which might grant access to unprivileged users
+ * through PolicyKit */
+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);
+
+        polkit_agent_open_if_enabled();
+
+        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);
 
-        if (arg_force &&
-            (streq(args[0], "halt") ||
-             streq(args[0], "poweroff") ||
-             streq(args[0], "reboot") ||
-             streq(args[0], "kexec") ||
-             streq(args[0], "exit")))
+        a = verb_to_action(args[0]);
+
+        if (arg_force >= 2 &&
+            (a == ACTION_HALT ||
+             a == ACTION_POWEROFF ||
+             a == ACTION_REBOOT))
+                halt_now(a);
+
+        if (arg_force >= 1 &&
+            (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);
+        /* first try logind, to allow authentication with polkit */
+        if (geteuid() != 0 &&
+            (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;
 }
@@ -1970,8 +2119,10 @@ 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;
         usec_t active_enter_timestamp;
         usec_t active_exit_timestamp;
         usec_t inactive_enter_timestamp;
@@ -2034,10 +2185,9 @@ static void print_status_info(UnitStatusInfo *i) {
         if (i->following)
                 printf("\t  Follow: unit currently follows state of %s\n", i->following);
 
-        if (streq_ptr(i->load_state, "failed") ||
-            streq_ptr(i->load_state, "banned")) {
-                on = ansi_highlight(true);
-                off = ansi_highlight(false);
+        if (streq_ptr(i->load_state, "error")) {
+                on = ansi_highlight_red(true);
+                off = ansi_highlight_red(false);
         } else
                 on = off = "";
 
@@ -2053,8 +2203,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);
@@ -2073,6 +2223,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")    ||
@@ -2130,8 +2283,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 = "";
 
@@ -2154,8 +2307,6 @@ static void print_status_info(UnitStatusInfo *i) {
 
                 printf(")%s\n", off);
 
-                on = off = NULL;
-
                 if (i->main_pid == p->pid &&
                     i->start_timestamp == p->start_timestamp &&
                     i->exit_timestamp == p->start_timestamp)
@@ -2174,7 +2325,7 @@ static void print_status_info(UnitStatusInfo *i) {
 
                         if (i->running) {
                                 char *t = NULL;
-                                get_process_name(i->main_pid, &t);
+                                get_process_comm(i->main_pid, &t);
                                 if (t) {
                                         printf(" (%s)", t);
                                         free(t);
@@ -2208,7 +2359,7 @@ static void print_status_info(UnitStatusInfo *i) {
 
                         printf(" Control: %u", (unsigned) i->control_pid);
 
-                        get_process_name(i->control_pid, &t);
+                        get_process_comm(i->control_pid, &t);
                         if (t) {
                                 printf(" (%s)", t);
                                 free(t);
@@ -2232,14 +2383,19 @@ 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);
                 }
         }
 
+        if (i->id && arg_transport != TRANSPORT_SSH) {
+                printf("\n");
+                show_journal_by_unit(i->id, arg_output, 0, i->inactive_exit_timestamp_monotonic, arg_lines, arg_all, arg_follow);
+        }
+
         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");
 }
 
@@ -2289,6 +2445,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;
@@ -2360,6 +2518,8 @@ static int status_property(const char *name, DBusMessageIter *iter, UnitStatusIn
                         i->inactive_enter_timestamp = (usec_t) u;
                 else if (streq(name, "InactiveExitTimestamp"))
                         i->inactive_exit_timestamp = (usec_t) u;
+                else if (streq(name, "InactiveExitTimestampMonotonic"))
+                        i->inactive_exit_timestamp_monotonic = (usec_t) u;
                 else if (streq(name, "ActiveExitTimestamp"))
                         i->active_exit_timestamp = (usec_t) u;
                 else if (streq(name, "ConditionTimestamp"))
@@ -3435,7 +3595,7 @@ finish:
 static int enable_sysv_units(char **args) {
         int r = 0;
 
-#if defined (HAVE_SYSV_COMPAT) && (defined(TARGET_FEDORA) || defined(TARGET_MANDRIVA) || defined(TARGET_SUSE) || defined(TARGET_MEEGO) || defined(TARGET_ALTLINUX))
+#if defined (HAVE_SYSV_COMPAT) && (defined(TARGET_FEDORA) || defined(TARGET_MANDRIVA) || defined(TARGET_SUSE) || defined(TARGET_MEEGO) || defined(TARGET_ALTLINUX) || defined(TARGET_MAGEIA))
         const char *verb = args[0];
         unsigned f = 1, t = 1;
         LookupPaths paths;
@@ -3618,12 +3778,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);
@@ -3650,11 +3813,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 {
@@ -3770,10 +3935,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);
                 }
@@ -3926,6 +4093,7 @@ static int systemctl_help(void) {
                "     --no-wall        Don't send wall message before halt/power-off/reboot\n"
                "     --no-reload      When enabling/disabling unit files, don't reload daemon\n"
                "                      configuration\n"
+               "     --no-legend      Do not print a legend (column headers and hints)\n"
                "     --no-pager       Do not pipe output into a pager\n"
                "     --no-ask-password\n"
                "                      Do not ask for system passwords\n"
@@ -3937,7 +4105,11 @@ static int systemctl_help(void) {
                "  -f --force          When enabling unit files, override existing symlinks\n"
                "                      When shutting down, execute action immediately\n"
                "     --root=PATH      Enable unit files in the specified root directory\n"
-               "     --runtime        Enable unit files only temporarily until next reboot\n\n"
+               "     --runtime        Enable unit files only temporarily until next reboot\n"
+               "  -n --lines=INTEGER  Journal entries to show\n"
+               "     --follow         Follow journal\n"
+               "  -o --output=STRING  Change journal output mode (short, short-monotonic,\n"
+               "                      verbose, export, json, cat)\n\n"
                "Unit Commands:\n"
                "  list-units                      List loaded units\n"
                "  start [NAME...]                 Start (activate) one or more units\n"
@@ -4076,6 +4248,7 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
                 ARG_SYSTEM,
                 ARG_GLOBAL,
                 ARG_NO_BLOCK,
+                ARG_NO_LEGEND,
                 ARG_NO_PAGER,
                 ARG_NO_WALL,
                 ARG_ORDER,
@@ -4087,7 +4260,9 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
                 ARG_KILL_WHO,
                 ARG_NO_ASK_PASSWORD,
                 ARG_FAILED,
-                ARG_RUNTIME
+                ARG_RUNTIME,
+                ARG_FOLLOW,
+                ARG_FORCE
         };
 
         static const struct option options[] = {
@@ -4104,13 +4279,14 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
                 { "system",    no_argument,       NULL, ARG_SYSTEM    },
                 { "global",    no_argument,       NULL, ARG_GLOBAL    },
                 { "no-block",  no_argument,       NULL, ARG_NO_BLOCK  },
+                { "no-legend", no_argument,       NULL, ARG_NO_LEGEND },
                 { "no-pager",  no_argument,       NULL, ARG_NO_PAGER  },
                 { "no-wall",   no_argument,       NULL, ARG_NO_WALL   },
                 { "quiet",     no_argument,       NULL, 'q'           },
                 { "order",     no_argument,       NULL, ARG_ORDER     },
                 { "require",   no_argument,       NULL, ARG_REQUIRE   },
                 { "root",      required_argument, NULL, ARG_ROOT      },
-                { "force",     no_argument,       NULL, 'f'           },
+                { "force",     no_argument,       NULL, ARG_FORCE     },
                 { "no-reload", no_argument,       NULL, ARG_NO_RELOAD },
                 { "kill-mode", required_argument, NULL, ARG_KILL_MODE }, /* undocumented on purpose */
                 { "kill-who",  required_argument, NULL, ARG_KILL_WHO  },
@@ -4119,6 +4295,9 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
                 { "host",      required_argument, NULL, 'H'           },
                 { "privileged",no_argument,       NULL, 'P'           },
                 { "runtime",   no_argument,       NULL, ARG_RUNTIME   },
+                { "lines",     required_argument, NULL, 'n'           },
+                { "follow",    no_argument,       NULL, ARG_FOLLOW    },
+                { "output",    required_argument, NULL, 'o'           },
                 { NULL,        0,                 NULL, 0             }
         };
 
@@ -4127,10 +4306,7 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
         assert(argc >= 0);
         assert(argv);
 
-        /* Only when running as systemctl we ask for passwords */
-        arg_ask_password = true;
-
-        while ((c = getopt_long(argc, argv, "ht:p:aqfs:H:P", options, NULL)) >= 0) {
+        while ((c = getopt_long(argc, argv, "ht:p:aqfs:H:Pn:o:", options, NULL)) >= 0) {
 
                 switch (c) {
 
@@ -4192,6 +4368,10 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
                         arg_no_block = true;
                         break;
 
+                case ARG_NO_LEGEND:
+                        arg_no_legend = true;
+                        break;
+
                 case ARG_NO_PAGER:
                         arg_no_pager = true;
                         break;
@@ -4224,8 +4404,18 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
                         arg_quiet = true;
                         break;
 
+                case ARG_FORCE:
+                        arg_force ++;
+                        break;
+
+                case ARG_FOLLOW:
+                        arg_follow = true;
+                        break;
+
                 case 'f':
-                        arg_force = true;
+                        /* -f is short for both --follow and --force! */
+                        arg_force ++;
+                        arg_follow = true;
                         break;
 
                 case ARG_NO_RELOAD:
@@ -4264,6 +4454,21 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
                         arg_runtime = true;
                         break;
 
+                case 'n':
+                        if (safe_atou(optarg, &arg_lines) < 0) {
+                                log_error("Failed to parse lines '%s'", optarg);
+                                return -EINVAL;
+                        }
+                        break;
+
+                case 'o':
+                        arg_output = output_mode_from_string(optarg);
+                        if (arg_output < 0) {
+                                log_error("Unknown output '%s'.", optarg);
+                                return -EINVAL;
+                        }
+                        break;
+
                 case '?':
                         return -EINVAL;
 
@@ -4436,6 +4641,7 @@ static int shutdown_parse_argv(int argc, char *argv[]) {
                 { "halt",      no_argument,       NULL, 'H'         },
                 { "poweroff",  no_argument,       NULL, 'P'         },
                 { "reboot",    no_argument,       NULL, 'r'         },
+                { "kexec",     no_argument,       NULL, 'K'         }, /* not documented extension */
                 { "no-wall",   no_argument,       NULL, ARG_NO_WALL },
                 { NULL,        0,                 NULL, 0           }
         };
@@ -4467,6 +4673,10 @@ static int shutdown_parse_argv(int argc, char *argv[]) {
                                 arg_action = ACTION_REBOOT;
                         break;
 
+                case 'K':
+                        arg_action = ACTION_KEXEC;
+                        break;
+
                 case 'h':
                         if (arg_action != ACTION_HALT)
                                 arg_action = ACTION_POWEROFF;
@@ -4499,7 +4709,8 @@ static int shutdown_parse_argv(int argc, char *argv[]) {
         }
 
         if (argc > optind) {
-                if ((r = parse_time_spec(argv[optind], &arg_when)) < 0) {
+                r = parse_time_spec(argv[optind], &arg_when);
+                if (r < 0) {
                         log_error("Failed to parse time specification: %s", argv[optind]);
                         return r;
                 }
@@ -4966,7 +5177,8 @@ static int systemctl_main(DBusConnection *bus, int argc, char *argv[], DBusError
          * enable/disable */
         if (!streq(verbs[i].verb, "enable") &&
             !streq(verbs[i].verb, "disable") &&
-            !streq(verbs[i].verb, "is-enable") &&
+            !streq(verbs[i].verb, "is-enabled") &&
+            !streq(verbs[i].verb, "list-unit-files") &&
             !streq(verbs[i].verb, "reenable") &&
             !streq(verbs[i].verb, "preset") &&
             !streq(verbs[i].verb, "mask") &&
@@ -4978,8 +5190,17 @@ static int systemctl_main(DBusConnection *bus, int argc, char *argv[], DBusError
                         return 0;
                 }
 
+                if (!bus) {
+                        log_error("Failed to get D-Bus connection: %s",
+                                  dbus_error_is_set(error) ? error->message : "No connection to service manager.");
+                        return -EIO;
+                }
+
+        } else {
+
                 if (!bus && !avoid_bus()) {
-                        log_error("Failed to get D-Bus connection: %s", error->message);
+                        log_error("Failed to get D-Bus connection: %s",
+                                  dbus_error_is_set(error) ? error->message : "No connection to service manager.");
                         return -EIO;
                 }
         }
@@ -4988,39 +5209,42 @@ static int systemctl_main(DBusConnection *bus, int argc, char *argv[], DBusError
 }
 
 static int send_shutdownd(usec_t t, char mode, bool dry_run, bool warn, const char *message) {
-        int fd = -1;
+        int fd;
         struct msghdr msghdr;
-        struct iovec iovec;
+        struct iovec iovec[2];
         union sockaddr_union sockaddr;
-        struct shutdownd_command c;
+        struct sd_shutdown_command c;
+
+        fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0);
+        if (fd < 0)
+                return -errno;
 
         zero(c);
-        c.elapse = t;
+        c.usec = t;
         c.mode = mode;
         c.dry_run = dry_run;
         c.warn_wall = warn;
 
-        if (message)
-                strncpy(c.wall_message, message, sizeof(c.wall_message));
-
-        if ((fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0)) < 0)
-                return -errno;
-
         zero(sockaddr);
         sockaddr.sa.sa_family = AF_UNIX;
-        sockaddr.un.sun_path[0] = 0;
         strncpy(sockaddr.un.sun_path, "/run/systemd/shutdownd", sizeof(sockaddr.un.sun_path));
 
-        zero(iovec);
-        iovec.iov_base = (char*) &c;
-        iovec.iov_len = sizeof(c);
-
         zero(msghdr);
         msghdr.msg_name = &sockaddr;
         msghdr.msg_namelen = offsetof(struct sockaddr_un, sun_path) + sizeof("/run/systemd/shutdownd") - 1;
 
-        msghdr.msg_iov = &iovec;
-        msghdr.msg_iovlen = 1;
+        zero(iovec);
+        iovec[0].iov_base = (char*) &c;
+        iovec[0].iov_len = offsetof(struct sd_shutdown_command, wall_message);
+
+        if (isempty(message))
+                msghdr.msg_iovlen = 1;
+        else {
+                iovec[1].iov_base = (char*) message;
+                iovec[1].iov_len = strlen(message);
+                msghdr.msg_iovlen = 2;
+        }
+        msghdr.msg_iov = iovec;
 
         if (sendmsg(fd, &msghdr, MSG_NOSIGNAL) < 0) {
                 close_nointr_nofail(fd);
@@ -5076,22 +5300,66 @@ 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) {
+                /* Try logind if we are a normal user and no special
+                 * mode applies. Maybe PolicyKit allows us to shutdown
+                 * the machine. */
+
+                if (arg_when <= 0 &&
+                    !arg_dry &&
+                    !arg_immediate &&
+                    (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;
         }
 
         if (arg_when > 0) {
                 char *m;
-                char date[FORMAT_TIMESTAMP_MAX];
 
                 m = strv_join(arg_wall, " ");
                 r = send_shutdownd(arg_when,
                                    arg_action == ACTION_HALT     ? 'H' :
                                    arg_action == ACTION_POWEROFF ? 'P' :
+                                   arg_action == ACTION_KEXEC    ? 'K' :
                                                                    'r',
                                    arg_dry,
                                    !arg_no_wall,
@@ -5101,6 +5369,8 @@ static int halt_main(DBusConnection *bus) {
                 if (r < 0)
                         log_warning("Failed to talk to shutdownd, proceeding with immediate shutdown: %s", strerror(-r));
                 else {
+                        char date[FORMAT_TIMESTAMP_MAX];
+
                         log_info("Shutdown scheduled for %s, use 'shutdown -c' to cancel.",
                                  format_timestamp(date, sizeof(date), arg_when));
                         return 0;
@@ -5113,8 +5383,11 @@ static int halt_main(DBusConnection *bus) {
         if (!arg_no_wtmp) {
                 if (sd_booted() > 0)
                         log_debug("Not writing utmp record, assuming that systemd-update-utmp is used.");
-                else if ((r = utmp_put_shutdown(0)) < 0)
-                        log_warning("Failed to write utmp record: %s", strerror(-r));
+                else {
+                        r = utmp_put_shutdown();
+                        if (r < 0)
+                                log_warning("Failed to write utmp record: %s", strerror(-r));
+                }
         }
 
         if (!arg_no_sync)
@@ -5123,31 +5396,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;
 }
@@ -5178,7 +5427,8 @@ int main(int argc, char*argv[]) {
         log_parse_environment();
         log_open();
 
-        if ((r = parse_argv(argc, argv)) < 0)
+        r = parse_argv(argc, argv);
+        if (r < 0)
                 goto finish;
         else if (r == 0) {
                 retval = EXIT_SUCCESS;
@@ -5266,7 +5516,8 @@ finish:
         strv_free(arg_property);
 
         pager_close();
-        agent_close();
+        ask_password_agent_close();
+        polkit_agent_close();
 
         return retval;
 }