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=2f03f6baa8a702e326ac7eb703773066557e94cd;hb=071fd8c21eb43f9e465309304c6072ba45ee8dd1;hpb=df50185b43916926a72246ab3a80875eda7ad2a3 diff --git a/src/systemctl.c b/src/systemctl.c index 2f03f6baa..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; @@ -125,6 +126,7 @@ static OutputMode arg_output = OUTPUT_SHORT; static bool private_bus = false; static int daemon_reload(DBusConnection *bus, char **args); +static void halt_now(enum action a); static bool on_tty(void) { static int t = -1; @@ -165,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) { @@ -224,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!", @@ -254,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) { @@ -382,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 = ""; @@ -592,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); @@ -1436,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")) @@ -1680,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; } @@ -2002,6 +2105,7 @@ typedef struct UnitStatusInfo { const char *default_control_group; const char *load_error; + const char *result; usec_t inactive_exit_timestamp; usec_t inactive_exit_timestamp_monotonic; @@ -2068,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 = ""; @@ -2085,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); @@ -2105,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") || @@ -2162,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 = ""; @@ -2262,7 +2369,7 @@ static void print_status_info(UnitStatusInfo *i) { else c = 0; - show_cgroup_by_path(i->default_control_group, "\t\t ", c); + show_cgroup_by_path(i->default_control_group, "\t\t ", c, false); } } @@ -2273,8 +2380,8 @@ static void print_status_info(UnitStatusInfo *i) { if (i->need_daemon_reload) printf("\n%sWarning:%s Unit file changed on disk, 'systemctl %s daemon-reload' recommended.\n", - ansi_highlight(true), - ansi_highlight(false), + ansi_highlight_red(true), + ansi_highlight_red(false), arg_scope == UNIT_FILE_SYSTEM ? "--system" : "--user"); } @@ -2324,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; @@ -3655,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); @@ -3687,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 { @@ -3807,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); } @@ -3978,7 +4094,8 @@ static int systemctl_help(void) { " --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, verbose, export, json)\n\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" @@ -4276,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: @@ -5156,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; } @@ -5203,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; }