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=a423fdbf93b7fb0f66751093882e656e4b100457;hb=5430f7f2bc7330f3088b894166bf3524a067e3d8;hpb=87d2c1ff6a7375f03476767e6f59454bcc5cd04b diff --git a/src/systemctl.c b/src/systemctl.c index a423fdbf9..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; @@ -75,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; @@ -117,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; @@ -148,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 */ @@ -158,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) { @@ -220,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!", @@ -236,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; } @@ -250,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) { @@ -377,16 +398,15 @@ 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 = ""; @@ -551,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; @@ -570,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); @@ -580,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); } @@ -603,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()) { @@ -640,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", @@ -1414,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")) @@ -1576,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 = @@ -1658,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; } @@ -1980,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; @@ -2044,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 = ""; @@ -2063,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); @@ -2083,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") || @@ -2140,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 = ""; @@ -2240,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"); } @@ -2297,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; @@ -2368,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")) @@ -3443,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; @@ -3626,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); @@ -3658,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 { @@ -3778,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); } @@ -3946,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" @@ -4097,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[] = { @@ -4121,7 +4286,7 @@ static int systemctl_parse_argv(int argc, char *argv[]) { { "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 }, @@ -4130,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 } }; @@ -4138,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) { @@ -4239,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: @@ -4279,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; @@ -4451,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 } }; @@ -4482,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; @@ -4514,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; } @@ -4981,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") && @@ -5012,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); @@ -5100,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, @@ -5125,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; @@ -5137,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) @@ -5147,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; } @@ -5202,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; @@ -5290,7 +5516,8 @@ finish: strv_free(arg_property); pager_close(); - agent_close(); + ask_password_agent_close(); + polkit_agent_close(); return retval; }