X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=src%2Fsystemctl.c;h=8f99a724b816a9415d0d5c776a0fddc98d674c33;hp=6f87b06dd3401c5640bf1e89bdb6fc98940be98f;hb=071fd8c21eb43f9e465309304c6072ba45ee8dd1;hpb=6f003b43043da7b57c0ebe15b86856c8248fea4a diff --git a/src/systemctl.c b/src/systemctl.c index 6f87b06dd..8f99a724b 100644 --- a/src/systemctl.c +++ b/src/systemctl.c @@ -35,6 +35,8 @@ #include #include +#include + #include "log.h" #include "util.h" #include "macro.h" @@ -49,7 +51,6 @@ #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" @@ -76,7 +77,7 @@ static bool arg_no_reload = false; static bool arg_dry = false; static bool arg_quiet = false; static bool arg_full = false; -static bool arg_force = false; +static int arg_force = 0; static bool arg_ask_password = false; static bool arg_failed = false; static bool arg_runtime = false; @@ -118,10 +119,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; @@ -162,12 +167,12 @@ static void agent_open_if_enabled(void) { agent_open(); } -static const char *ansi_highlight(bool b) { +static const char *ansi_highlight_red(bool b) { if (!on_tty()) return ""; - return b ? ANSI_HIGHLIGHT_ON : ANSI_HIGHLIGHT_OFF; + return b ? ANSI_HIGHLIGHT_RED_ON : ANSI_HIGHLIGHT_OFF; } static const char *ansi_highlight_green(bool b) { @@ -221,7 +226,7 @@ static int translate_bus_error_to_exit_status(int r, const DBusError *error) { return EXIT_FAILURE; } -static void warn_wall(enum action action) { +static void warn_wall(enum action a) { static const char *table[_ACTION_MAX] = { [ACTION_HALT] = "The system is going down for system halt NOW!", [ACTION_REBOOT] = "The system is going down for reboot NOW!", @@ -251,10 +256,10 @@ static void warn_wall(enum action action) { free(p); } - if (!table[action]) + if (!table[a]) return; - utmp_wall(table[action], NULL); + utmp_wall(table[a], NULL); } static bool avoid_bus(void) { @@ -379,14 +384,14 @@ static void output_units_list(const struct unit_info *unit_infos, unsigned c) { n_shown++; if (streq(u->load_state, "error")) { - on_loaded = ansi_highlight(true); - off_loaded = ansi_highlight(false); + on_loaded = ansi_highlight_red(true); + off_loaded = ansi_highlight_red(false); } else on_loaded = off_loaded = ""; if (streq(u->active_state, "failed")) { - on_active = ansi_highlight(true); - off_active = ansi_highlight(false); + on_active = ansi_highlight_red(true); + off_active = ansi_highlight_red(false); } else on_active = off_active = ""; @@ -589,8 +594,8 @@ static void output_unit_file_list(const UnitFileList *units, unsigned c) { if (u->state == UNIT_FILE_MASKED || u->state == UNIT_FILE_MASKED_RUNTIME || u->state == UNIT_FILE_DISABLED) { - on = ansi_highlight(true); - off = ansi_highlight(false); + on = ansi_highlight_red(true); + off = ansi_highlight_red(false); } else if (u->state == UNIT_FILE_ENABLED) { on = ansi_highlight_green(true); off = ansi_highlight_green(false); @@ -1433,9 +1438,9 @@ static int wait_for_jobs(DBusConnection *bus, Set *s) { else if (streq(d.result, "canceled")) log_error("Job canceled."); else if (streq(d.result, "dependency")) - log_error("A dependency job failed. See system logs for details."); + log_error("A dependency job failed. See system journal for details."); else if (!streq(d.result, "done") && !streq(d.result, "skipped")) - log_error("Job failed. See system logs and 'systemctl status' for details."); + log_error("Job failed. See system journal and 'systemctl status' for details."); } if (streq_ptr(d.result, "timeout")) @@ -1677,24 +1682,125 @@ finish: return ret; } +/* ask systemd-logind, which might grant access to unprivileged users through polkit */ +static int reboot_with_logind(DBusConnection *bus, enum action a) { +#ifdef HAVE_LOGIND + const char *method; + DBusMessage *m = NULL, *reply = NULL; + DBusError error; + dbus_bool_t interactive = true; + int r; + + dbus_error_init(&error); + + switch (a) { + + case ACTION_REBOOT: + method = "Reboot"; + break; + + case ACTION_POWEROFF: + method = "PowerOff"; + break; + + default: + return -EINVAL; + } + + m = dbus_message_new_method_call( + "org.freedesktop.login1", + "/org/freedesktop/login1", + "org.freedesktop.login1.Manager", + method); + if (!m) { + log_error("Could not allocate message."); + r = -ENOMEM; + goto finish; + } + + if (!dbus_message_append_args(m, + DBUS_TYPE_BOOLEAN, &interactive, + DBUS_TYPE_INVALID)) { + log_error("Could not append arguments to message."); + r = -ENOMEM; + goto finish; + } + + reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error); + if (!reply) { + if (error_is_no_service(&error)) { + log_debug("Failed to issue method call: %s", bus_error_message(&error)); + r = -ENOENT; + goto finish; + } + + if (dbus_error_has_name(&error, DBUS_ERROR_ACCESS_DENIED)) { + log_debug("Failed to issue method call: %s", bus_error_message(&error)); + r = -EACCES; + goto finish; + } + + log_info("Failed to issue method call: %s", bus_error_message(&error)); + r = -EIO; + goto finish; + } + + r = 0; + +finish: + if (m) + dbus_message_unref(m); + + if (reply) + dbus_message_unref(reply); + + dbus_error_free(&error); + + return r; +#else + return -ENOSYS; +#endif +} + static int start_special(DBusConnection *bus, char **args) { + enum action a; int r; assert(bus); assert(args); + a = verb_to_action(args[0]); + + if (arg_force >= 2 && a == ACTION_HALT) + halt_now(ACTION_HALT); + + if (arg_force >= 2 && a == ACTION_POWEROFF) + halt_now(ACTION_POWEROFF); + + if (arg_force >= 2 && a == ACTION_REBOOT) + halt_now(ACTION_REBOOT); + if (arg_force && - (streq(args[0], "halt") || - streq(args[0], "poweroff") || - streq(args[0], "reboot") || - streq(args[0], "kexec") || - streq(args[0], "exit"))) + (a == ACTION_HALT || + a == ACTION_POWEROFF || + a == ACTION_REBOOT || + a == ACTION_KEXEC || + a == ACTION_EXIT)) return daemon_reload(bus, args); - r = start_unit(bus, args); + if (geteuid() != 0) { + /* first try logind, to allow authentication with polkit */ + if (a == ACTION_POWEROFF || + a == ACTION_REBOOT) { + r = reboot_with_logind(bus, a); + if (r >= 0) + return r; + } + } + r = start_unit(bus, args); if (r >= 0) - warn_wall(verb_to_action(args[0])); + warn_wall(a); return r; } @@ -1999,8 +2105,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; @@ -2064,8 +2172,8 @@ static void print_status_info(UnitStatusInfo *i) { printf("\t Follow: unit currently follows state of %s\n", i->following); if (streq_ptr(i->load_state, "error")) { - on = ansi_highlight(true); - off = ansi_highlight(false); + on = ansi_highlight_red(true); + off = ansi_highlight_red(false); } else on = off = ""; @@ -2081,8 +2189,8 @@ static void print_status_info(UnitStatusInfo *i) { ss = streq_ptr(i->active_state, i->sub_state) ? NULL : i->sub_state; if (streq_ptr(i->active_state, "failed")) { - on = ansi_highlight(true); - off = ansi_highlight(false); + on = ansi_highlight_red(true); + off = ansi_highlight_red(false); } else if (streq_ptr(i->active_state, "active") || streq_ptr(i->active_state, "reloading")) { on = ansi_highlight_green(true); off = ansi_highlight_green(false); @@ -2101,6 +2209,9 @@ static void print_status_info(UnitStatusInfo *i) { strna(i->active_state), off); + if (!isempty(i->result) && !streq(i->result, "success")) + printf(" (Result: %s)", i->result); + timestamp = (streq_ptr(i->active_state, "active") || streq_ptr(i->active_state, "reloading")) ? i->active_enter_timestamp : (streq_ptr(i->active_state, "inactive") || @@ -2158,8 +2269,8 @@ static void print_status_info(UnitStatusInfo *i) { good = is_clean_exit(p->code, p->status); if (!good) { - on = ansi_highlight(true); - off = ansi_highlight(false); + on = ansi_highlight_red(true); + off = ansi_highlight_red(false); } else on = off = ""; @@ -2258,19 +2369,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_service(i->id, OUTPUT_SHORT, NULL, 0, 0, 0, arg_all); + show_journal_by_unit(i->id, arg_output, NULL, 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"); } @@ -2320,6 +2431,8 @@ static int status_property(const char *name, DBusMessageIter *iter, UnitStatusIn i->following = s; else if (streq(name, "UnitFileState")) i->unit_file_state = s; + else if (streq(name, "Result")) + i->result = s; } break; @@ -2391,6 +2504,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")) @@ -3649,12 +3764,15 @@ static int enable_unit(DBusConnection *bus, char **args) { int r; DBusError error; - dbus_error_init(&error); - r = enable_sysv_units(args); if (r < 0) return r; + if (!args[1]) + return 0; + + dbus_error_init(&error); + if (!bus || avoid_bus()) { if (streq(verb, "enable")) { r = unit_file_enable(arg_scope, arg_runtime, arg_root, args+1, arg_force, &changes, &n_changes); @@ -3681,11 +3799,13 @@ static int enable_unit(DBusConnection *bus, char **args) { goto finish; } - for (i = 0; i < n_changes; i++) { - if (changes[i].type == UNIT_FILE_SYMLINK) - log_info("ln -s '%s' '%s'", changes[i].source, changes[i].path); - else - log_info("rm '%s'", changes[i].path); + if (!arg_quiet) { + for (i = 0; i < n_changes; i++) { + if (changes[i].type == UNIT_FILE_SYMLINK) + log_info("ln -s '%s' '%s'", changes[i].source, changes[i].path); + else + log_info("rm '%s'", changes[i].path); + } } } else { @@ -3801,10 +3921,12 @@ static int enable_unit(DBusConnection *bus, char **args) { goto finish; } - if (streq(type, "symlink")) - log_info("ln -s '%s' '%s'", source, path); - else - log_info("rm '%s'", path); + if (!arg_quiet) { + if (streq(type, "symlink")) + log_info("ln -s '%s' '%s'", source, path); + else + log_info("rm '%s'", path); + } dbus_message_iter_next(&sub); } @@ -3969,7 +4091,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" @@ -4120,7 +4246,8 @@ static int systemctl_parse_argv(int argc, char *argv[]) { ARG_KILL_WHO, ARG_NO_ASK_PASSWORD, ARG_FAILED, - ARG_RUNTIME + ARG_RUNTIME, + ARG_FOLLOW }; static const struct option options[] = { @@ -4153,6 +4280,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 } }; @@ -4164,7 +4294,7 @@ static int systemctl_parse_argv(int argc, char *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) { @@ -4263,7 +4393,7 @@ static int systemctl_parse_argv(int argc, char *argv[]) { break; case 'f': - arg_force = true; + arg_force ++; break; case ARG_NO_RELOAD: @@ -4302,6 +4432,25 @@ 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 ARG_FOLLOW: + arg_follow = true; + 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; @@ -5124,10 +5273,47 @@ done: return 0; } +static void halt_now(enum action a) { + + /* Make sure C-A-D is handled by the kernel from this + * point on... */ + reboot(RB_ENABLE_CAD); + + switch (a) { + + case ACTION_HALT: + log_info("Halting."); + reboot(RB_HALT_SYSTEM); + break; + + case ACTION_POWEROFF: + log_info("Powering off."); + reboot(RB_POWER_OFF); + break; + + case ACTION_REBOOT: + log_info("Rebooting."); + reboot(RB_AUTOBOOT); + break; + + default: + assert_not_reached("Unknown halt action."); + } + + assert_not_reached("Uh? This shouldn't happen."); +} + static int halt_main(DBusConnection *bus) { int r; if (geteuid() != 0) { + if (arg_action == ACTION_POWEROFF || + arg_action == ACTION_REBOOT) { + r = reboot_with_logind(bus, arg_action); + if (r >= 0) + return r; + } + log_error("Must be root."); return -EPERM; } @@ -5171,31 +5357,7 @@ static int halt_main(DBusConnection *bus) { if (arg_dry) return 0; - /* Make sure C-A-D is handled by the kernel from this - * point on... */ - reboot(RB_ENABLE_CAD); - - switch (arg_action) { - - case ACTION_HALT: - log_info("Halting."); - reboot(RB_HALT_SYSTEM); - break; - - case ACTION_POWEROFF: - log_info("Powering off."); - reboot(RB_POWER_OFF); - break; - - case ACTION_REBOOT: - log_info("Rebooting."); - reboot(RB_AUTOBOOT); - break; - - default: - assert_not_reached("Unknown halt action."); - } - + halt_now(arg_action); /* We should never reach this. */ return -ENOSYS; }