X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?a=blobdiff_plain;f=src%2Flogin%2Floginctl.c;h=fd1e9b9c2898341ab78eda2865da51c3b8ff92c8;hb=a70be41f0832e4a36ff2fc99a2ac1f082b17b3be;hp=ee9816ad07628020b9d1b6d3140735f0ccbab91b;hpb=8331cb6e143dc261fd22af9a2a679196822326a9;p=elogind.git diff --git a/src/login/loginctl.c b/src/login/loginctl.c index ee9816ad0..fd1e9b9c2 100644 --- a/src/login/loginctl.c +++ b/src/login/loginctl.c @@ -318,7 +318,7 @@ typedef struct SessionStatusInfo { char *seat; char *tty; char *display; - bool remote; + int remote; char *remote_host; char *remote_user; char *service; @@ -332,7 +332,7 @@ typedef struct SessionStatusInfo { typedef struct UserStatusInfo { uid_t uid; - bool linger; + int linger; char *name; struct dual_timestamp timestamp; char *state; @@ -1402,16 +1402,81 @@ static int logind_set_wall_message(sd_bus* bus, const char* msg) { return 0; } -/* Ask elogind, which might grant access to unprivileged users - * through PolicyKit */ +static int elogind_cancel_shutdown(sd_bus *bus) { + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + int r; + + r = sd_bus_call_method( + bus, + "org.freedesktop.login1", + "/org/freedesktop/login1", + "org.freedesktop.login1.Manager", + "CancelScheduledShutdown", + &error, + NULL, NULL); + if (r < 0) + return log_warning_errno(r, "Failed to talk to elogind, shutdown hasn't been cancelled: %s", bus_error_message(&error, r)); + + return 0; +} + +static int elogind_schedule_shutdown(sd_bus *bus, enum action a) { + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + const char *method = NULL; + int r; + + if (!bus) + return -EIO; + + polkit_agent_open_if_enabled(); + + switch (a) { + + case ACTION_POWEROFF: + method = "poweroff"; + break; + + case ACTION_REBOOT: + method = "reboot"; + break; + + default: + return -EINVAL; + } + + r = logind_set_wall_message(bus, NULL); + + if (r < 0) { + log_warning_errno(r, "Failed to set wall message, ignoring: %s", + bus_error_message(&error, r)); + sd_bus_error_free(&error); + } + + r = sd_bus_call_method( + bus, + "org.freedesktop.login1", + "/org/freedesktop/login1", + "org.freedesktop.login1.Manager", + "ScheduleShutdown", + &error, + NULL, + "st", + method, + arg_when); + + if (r < 0) + log_error("Failed to execute operation: %s", bus_error_message(&error, r)); + + return r; +} + static int elogind_reboot(sd_bus *bus, enum action a) { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - const char *method; + const char *method = NULL; int r; static const char *table[_ACTION_MAX] = { [ACTION_REBOOT] = "The system is going down for reboot NOW!", - [ACTION_POWEROFF] = "The system is going down for power-off NOW!", - [ACTION_CANCEL_SHUTDOWN] = "The system shutdown has been cancelled NOW!" + [ACTION_POWEROFF] = "The system is going down for power-off NOW!" }; if (!bus) @@ -1441,21 +1506,19 @@ static int elogind_reboot(sd_bus *bus, enum action a) { method = "HybridSleep"; break; - case ACTION_CANCEL_SHUTDOWN: - method = "CancelScheduledShutdown"; - break; - default: return -EINVAL; } r = logind_set_wall_message(bus, table[a]); + if (r < 0) { log_warning_errno(r, "Failed to set wall message, ignoring: %s", bus_error_message(&error, r)); sd_bus_error_free(&error); } + /* Now call elogind itself to request the operation */ r = sd_bus_call_method( bus, "org.freedesktop.login1", @@ -1464,7 +1527,9 @@ static int elogind_reboot(sd_bus *bus, enum action a) { method, &error, NULL, - "b", arg_ask_password); + "b", + arg_ask_password); + if (r < 0) log_error("Failed to execute operation: %s", bus_error_message(&error, r)); @@ -1642,7 +1707,7 @@ static int check_inhibitors(sd_bus* bus, enum action a) { if (c <= 0) return 0; - log_error("Please retry operation after closing inhibitors and logging out other users.\nAlternatively, ignore inhibitors and users with 'systemctl %s -i'.", + log_error("Please retry operation after closing inhibitors and logging out other users.\nAlternatively, ignore inhibitors and users with 'loginctl -i %s'.", action_table[a].verb); return -EPERM; @@ -1652,16 +1717,23 @@ static int start_special(int argc, char *argv[], void *userdata) { sd_bus *bus = userdata; enum action a; int r; + char** wall = NULL; assert(argv); a = verb_to_action(argv[0]); + /* Switch to cancel shutdown, if a shutdown action was requested, + and the option to cancel it was set: */ + if ( IN_SET(a, ACTION_POWEROFF, ACTION_REBOOT) + && (arg_action == ACTION_CANCEL_SHUTDOWN) ) + return elogind_cancel_shutdown(bus); + r = check_inhibitors(bus, a); if (r < 0) return r; - /* Now power off actions in chroot environments */ + /* No power off actions in chroot environments */ if ((a == ACTION_POWEROFF || a == ACTION_REBOOT) && (running_in_chroot() > 0) ) { @@ -1669,20 +1741,44 @@ static int start_special(int argc, char *argv[], void *userdata) { return 0; } - /* Switch to cancel shutdown, if a shutdown action was requested, - and the option to cancel it was set: */ - if ((a == ACTION_POWEROFF || - a == ACTION_REBOOT) && - (arg_action == ACTION_CANCEL_SHUTDOWN)) - return elogind_reboot(bus, arg_action); + /* Check time arguments */ + if ( IN_SET(a, ACTION_POWEROFF, ACTION_REBOOT) + && (argc > 1) + && (arg_action != ACTION_CANCEL_SHUTDOWN) ) { + r = parse_shutdown_time_spec(argv[1], &arg_when); + if (r < 0) { + log_error("Failed to parse time specification: %s", argv[optind]); + return r; + } + } else + arg_when = now(CLOCK_REALTIME) + USEC_PER_MINUTE; - /* Otherwise perform requested action */ - if (a == ACTION_POWEROFF || - a == ACTION_REBOOT || - a == ACTION_SUSPEND || - a == ACTION_HIBERNATE || - a == ACTION_HYBRID_SLEEP) - return elogind_reboot(bus, a); + /* The optional user wall message must be set */ + if ((argc > 1) && (arg_action == ACTION_CANCEL_SHUTDOWN) ) + /* No time argument for shutdown cancel */ + wall = argv + 1; + else if (argc > 2) + /* We skip the time argument */ + wall = argv + 2; + + if (wall) { + arg_wall = strv_copy(wall); + if (!arg_wall) + return log_oom(); + } + + /* Perform requested action */ + if (IN_SET(a, + ACTION_POWEROFF, + ACTION_REBOOT, + ACTION_SUSPEND, + ACTION_HIBERNATE, + ACTION_HYBRID_SLEEP)) { + if (arg_when > 0) + return elogind_schedule_shutdown(bus, a); + else + return elogind_reboot(bus, a); + } return -EOPNOTSUPP; } @@ -1710,40 +1806,40 @@ static int help(int argc, char *argv[], void *userdata) { " -o --output=STRING Change journal output mode (short, short-monotonic,\n" " verbose, export, json, json-pretty, json-sse, cat)\n\n" #endif // 0 - " -c Cancel a pending shutdown\n" - " -i --ignore-inhibitors When shutting down or sleeping, ignore inhibitors\n" + " -c Cancel a pending shutdown or reboot\n" + " -i --ignore-inhibitors When shutting down or sleeping, ignore inhibitors\n" "Session Commands:\n" - " list-sessions List sessions\n" - " session-status [ID...] Show session status\n" - " show-session [ID...] Show properties of sessions or the manager\n" - " activate [ID] Activate a session\n" - " lock-session [ID...] Screen lock one or more sessions\n" - " unlock-session [ID...] Screen unlock one or more sessions\n" - " lock-sessions Screen lock all current sessions\n" - " unlock-sessions Screen unlock all current sessions\n" - " terminate-session ID... Terminate one or more sessions\n" - " kill-session ID... Send signal to processes of a session\n\n" + " list-sessions List sessions\n" + " session-status [ID...] Show session status\n" + " show-session [ID...] Show properties of sessions or the manager\n" + " activate [ID] Activate a session\n" + " lock-session [ID...] Screen lock one or more sessions\n" + " unlock-session [ID...] Screen unlock one or more sessions\n" + " lock-sessions Screen lock all current sessions\n" + " unlock-sessions Screen unlock all current sessions\n" + " terminate-session ID... Terminate one or more sessions\n" + " kill-session ID... Send signal to processes of a session\n\n" "User Commands:\n" - " list-users List users\n" - " user-status [USER...] Show user status\n" - " show-user [USER...] Show properties of users or the manager\n" - " enable-linger [USER...] Enable linger state of one or more users\n" - " disable-linger [USER...] Disable linger state of one or more users\n" - " terminate-user USER... Terminate all sessions of one or more users\n" - " kill-user USER... Send signal to processes of a user\n\n" + " list-users List users\n" + " user-status [USER...] Show user status\n" + " show-user [USER...] Show properties of users or the manager\n" + " enable-linger [USER...] Enable linger state of one or more users\n" + " disable-linger [USER...] Disable linger state of one or more users\n" + " terminate-user USER... Terminate all sessions of one or more users\n" + " kill-user USER... Send signal to processes of a user\n\n" "Seat Commands:\n" - " list-seats List seats\n" - " seat-status [NAME...] Show seat status\n" - " show-seat [NAME...] Show properties of seats or the manager\n" - " attach NAME DEVICE... Attach one or more devices to a seat\n" - " flush-devices Flush all device associations\n" - " terminate-seat NAME... Terminate all sessions on one or more seats\n" + " list-seats List seats\n" + " seat-status [NAME...] Show seat status\n" + " show-seat [NAME...] Show properties of seats or the manager\n" + " attach NAME DEVICE... Attach one or more devices to a seat\n" + " flush-devices Flush all device associations\n" + " terminate-seat NAME... Terminate all sessions on one or more seats\n" "System Commands:\n" - " poweroff Turn off the machine\n" - " reboot Reboot the machine\n" - " suspend Suspend the machine to memory\n" - " hibernate Suspend the machine to disk\n" - " hybrid-sleep Suspend the machine to memory and disk\n" + " poweroff [TIME] [WALL...] Turn off the machine\n" + " reboot [TIME] [WALL...] Reboot the machine\n" + " suspend Suspend the machine to memory\n" + " hibernate Suspend the machine to disk\n" + " hybrid-sleep Suspend the machine to memory and disk\n" , program_invocation_short_name); return 0; @@ -1784,13 +1880,12 @@ static int parse_argv(int argc, char *argv[]) { {} }; - char **wall = NULL; int c, r; assert(argc >= 0); assert(argv); - while ((c = getopt_long(argc, argv, "hp:als:H:M:n:o:ci", options, NULL)) >= 0) + while ((c = getopt_long(argc, argv, "hp:als:H:M:n:o:ci", options, NULL)) >= 0) { switch (c) { @@ -1894,31 +1989,8 @@ static int parse_argv(int argc, char *argv[]) { default: assert_not_reached("Unhandled option"); } - - if (argc > optind && arg_action != ACTION_CANCEL_SHUTDOWN) { - r = parse_shutdown_time_spec(argv[optind], &arg_when); - if (r < 0) { - log_error("Failed to parse time specification: %s", argv[optind]); - return r; - } - } else - arg_when = now(CLOCK_REALTIME) + USEC_PER_MINUTE; - - if (argc > optind && arg_action == ACTION_CANCEL_SHUTDOWN) - /* No time argument for shutdown cancel */ - wall = argv + optind; - else if (argc > optind + 1) - /* We skip the time argument */ - wall = argv + optind + 1; - - if (wall) { - arg_wall = strv_copy(wall); - if (!arg_wall) - return log_oom(); } - optind = argc; - return 1; } @@ -1949,8 +2021,8 @@ static int loginctl_main(int argc, char *argv[], sd_bus *bus) { { "attach", 3, VERB_ANY, 0, attach }, { "flush-devices", VERB_ANY, 1, 0, flush_devices }, { "terminate-seat", 2, VERB_ANY, 0, terminate_seat }, - { "poweroff", VERB_ANY, 1, 0, start_special }, - { "reboot", VERB_ANY, 1, 0, start_special }, + { "poweroff", VERB_ANY, VERB_ANY, 0, start_special }, + { "reboot", VERB_ANY, VERB_ANY, 0, start_special }, { "suspend", VERB_ANY, 1, 0, start_special }, { "hibernate", VERB_ANY, 1, 0, start_special }, { "hybrid-sleep", VERB_ANY, 1, 0, start_special }, @@ -1958,11 +2030,14 @@ static int loginctl_main(int argc, char *argv[], sd_bus *bus) { {} }; + if ((argc == optind) && (ACTION_CANCEL_SHUTDOWN == arg_action)) + return elogind_cancel_shutdown(bus); + return dispatch_verb(argc, argv, verbs, bus); } int main(int argc, char *argv[]) { - _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; + sd_bus *bus = NULL; int r; setlocale(LC_ALL, ""); @@ -1981,10 +2056,11 @@ int main(int argc, char *argv[]) { } sd_bus_set_allow_interactive_authorization(bus, arg_ask_password); - r = loginctl_main(argc, argv, bus); finish: + sd_bus_flush_close_unref(bus); + pager_close(); polkit_agent_close();