X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=src%2Fsystemctl.c;h=28bdfa96a4e06d9b9c393e7b3357dd96b28f79e8;hp=e85a2fd44c249337bba3e08bfc7a6c8dbd426156;hb=5430f7f2bc7330f3088b894166bf3524a067e3d8;hpb=8ea913b2eaadbd92e069ea6b71cc5f5df409decf diff --git a/src/systemctl.c b/src/systemctl.c index e85a2fd44..28bdfa96a 100644 --- a/src/systemctl.c +++ b/src/systemctl.c @@ -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 . ***/ @@ -35,6 +35,9 @@ #include #include +#include +#include + #include "log.h" #include "util.h" #include "macro.h" @@ -49,15 +52,15 @@ #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; @@ -137,6 +145,8 @@ static bool on_tty(void) { } static void pager_open_if_enabled(void) { + + /* Cache result before we open the pager */ on_tty(); if (arg_no_pager) @@ -145,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 */ @@ -155,15 +165,28 @@ static void agent_open_if_enabled(void) { if (arg_scope != UNIT_FILE_SYSTEM) return; - agent_open(); + ask_password_agent_open(); } -static const char *ansi_highlight(bool b) { +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_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) { @@ -217,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!", @@ -233,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; } @@ -247,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) { @@ -312,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; @@ -349,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" @@ -539,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; @@ -558,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); @@ -568,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); } @@ -591,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()) { @@ -628,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", @@ -1055,7 +1106,7 @@ finish: } static int load_unit(DBusConnection *bus, char **args) { - DBusMessage *m = NULL, *reply = NULL; + DBusMessage *m = NULL; DBusError error; int r; char **name; @@ -1066,6 +1117,7 @@ static int load_unit(DBusConnection *bus, char **args) { assert(args); STRV_FOREACH(name, args+1) { + DBusMessage *reply; if (!(m = dbus_message_new_method_call( "org.freedesktop.systemd1", @@ -1103,9 +1155,6 @@ finish: if (m) dbus_message_unref(m); - if (reply) - dbus_message_unref(reply); - dbus_error_free(&error); return r; @@ -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; } @@ -1795,7 +1944,7 @@ finish: } static int kill_unit(DBusConnection *bus, char **args) { - DBusMessage *m = NULL, *reply = NULL; + DBusMessage *m = NULL; int r = 0; DBusError error; char **name; @@ -1812,6 +1961,7 @@ static int kill_unit(DBusConnection *bus, char **args) { arg_kill_mode = streq(arg_kill_who, "all") ? "control-group" : "process"; STRV_FOREACH(name, args+1) { + DBusMessage *reply; if (!(m = dbus_message_new_method_call( "org.freedesktop.systemd1", @@ -1851,9 +2001,6 @@ finish: if (m) dbus_message_unref(m); - if (reply) - dbus_message_unref(reply); - dbus_error_free(&error); return r; @@ -1972,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; @@ -2036,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 = ""; @@ -2055,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); @@ -2075,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") || @@ -2132,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 = ""; @@ -2156,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) @@ -2176,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); @@ -2210,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); @@ -2234,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"); } @@ -2291,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; @@ -2362,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")) @@ -3231,7 +3389,7 @@ finish: } static int reset_failed(DBusConnection *bus, char **args) { - DBusMessage *m = NULL, *reply = NULL; + DBusMessage *m = NULL; int r; DBusError error; char **name; @@ -3243,6 +3401,7 @@ static int reset_failed(DBusConnection *bus, char **args) { return daemon_reload(bus, args); STRV_FOREACH(name, args+1) { + DBusMessage *reply; if (!(m = dbus_message_new_method_call( "org.freedesktop.systemd1", @@ -3279,9 +3438,6 @@ finish: if (m) dbus_message_unref(m); - if (reply) - dbus_message_unref(reply); - dbus_error_free(&error); return r; @@ -3439,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; @@ -3622,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); @@ -3654,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 { @@ -3774,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); } @@ -3930,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" @@ -3941,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" @@ -4080,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, @@ -4091,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[] = { @@ -4108,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 }, @@ -4123,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 } }; @@ -4131,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) { @@ -4196,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; @@ -4228,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: @@ -4268,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; @@ -4440,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 } }; @@ -4471,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; @@ -4503,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; } @@ -4970,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") && @@ -4982,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; } } @@ -4992,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); @@ -5080,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, @@ -5105,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; @@ -5117,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) @@ -5127,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; } @@ -5182,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; @@ -5270,7 +5516,8 @@ finish: strv_free(arg_property); pager_close(); - agent_close(); + ask_password_agent_close(); + polkit_agent_close(); return retval; }