X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?a=blobdiff_plain;f=src%2Flogin%2Flogind-dbus.c;h=0fcf2cc96000da38bd5342eef4fe46daee6ee9fe;hb=edda707302d7c5138291c0ff4e9c43db9fba51d8;hp=374e5983702e480c47ef0f2fe2fa1f76cc78be80;hpb=fbf087ea4df2b0b893376a02f8e4777f77c47feb;p=elogind.git diff --git a/src/login/logind-dbus.c b/src/login/logind-dbus.c index 374e59837..0fcf2cc96 100644 --- a/src/login/logind-dbus.c +++ b/src/login/logind-dbus.c @@ -213,6 +213,33 @@ static int property_get_preparing( return sd_bus_message_append(reply, "b", b); } +static int property_get_scheduled_shutdown( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + Manager *m = userdata; + int r; + + assert(bus); + assert(reply); + assert(m); + + r = sd_bus_message_open_container(reply, 'r', "st"); + if (r < 0) + return r; + + r = sd_bus_message_append(reply, "st", m->scheduled_shutdown_type, m->scheduled_shutdown_timeout); + if (r < 0) + return r; + + return sd_bus_message_close_container(reply); +} + static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_handle_action, handle_action, HandleAction); static int method_get_session(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) { @@ -1590,49 +1617,25 @@ int bus_manager_shutdown_or_sleep_now_or_later( return r; } -static int method_do_shutdown_or_sleep( +static int verify_shutdown_creds( Manager *m, sd_bus_message *message, - const char *unit_name, InhibitWhat w, + bool interactive, const char *action, const char *action_multiple_sessions, const char *action_ignore_inhibit, - const char *sleep_verb, - sd_bus_message_handler_t method, sd_bus_error *error) { _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL; bool multiple_sessions, blocked; - int interactive, r; uid_t uid; + int r; assert(m); assert(message); - assert(unit_name); assert(w >= 0); assert(w <= _INHIBIT_WHAT_MAX); - assert(action); - assert(action_multiple_sessions); - assert(action_ignore_inhibit); - assert(method); - - r = sd_bus_message_read(message, "b", &interactive); - if (r < 0) - return r; - - /* Don't allow multiple jobs being executed at the same time */ - if (m->action_what) - return sd_bus_error_setf(error, BUS_ERROR_OPERATION_IN_PROGRESS, "There's already a shutdown or sleep operation in progress"); - - if (sleep_verb) { - r = can_sleep(sleep_verb); - if (r < 0) - return r; - - if (r == 0) - return sd_bus_error_setf(error, BUS_ERROR_SLEEP_VERB_NOT_SUPPORTED, "Sleep verb not supported"); - } r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_EUID, &creds); if (r < 0) @@ -1649,7 +1652,7 @@ static int method_do_shutdown_or_sleep( multiple_sessions = r > 0; blocked = manager_is_inhibited(m, w, INHIBIT_BLOCK, NULL, false, true, uid, NULL); - if (multiple_sessions) { + if (multiple_sessions && action_multiple_sessions) { r = bus_verify_polkit_async(message, CAP_SYS_BOOT, action_multiple_sessions, interactive, UID_INVALID, &m->polkit_registry, error); if (r < 0) return r; @@ -1657,7 +1660,7 @@ static int method_do_shutdown_or_sleep( return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ } - if (blocked) { + if (blocked && action_ignore_inhibit) { r = bus_verify_polkit_async(message, CAP_SYS_BOOT, action_ignore_inhibit, interactive, UID_INVALID, &m->polkit_registry, error); if (r < 0) return r; @@ -1665,7 +1668,7 @@ static int method_do_shutdown_or_sleep( return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ } - if (!multiple_sessions && !blocked) { + if (!multiple_sessions && !blocked && action) { r = bus_verify_polkit_async(message, CAP_SYS_BOOT, action, interactive, UID_INVALID, &m->polkit_registry, error); if (r < 0) return r; @@ -1673,6 +1676,50 @@ static int method_do_shutdown_or_sleep( return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ } + return 0; +} + +static int method_do_shutdown_or_sleep( + Manager *m, + sd_bus_message *message, + const char *unit_name, + InhibitWhat w, + const char *action, + const char *action_multiple_sessions, + const char *action_ignore_inhibit, + const char *sleep_verb, + sd_bus_error *error) { + + int interactive, r; + + assert(m); + assert(message); + assert(unit_name); + assert(w >= 0); + assert(w <= _INHIBIT_WHAT_MAX); + + r = sd_bus_message_read(message, "b", &interactive); + if (r < 0) + return r; + + /* Don't allow multiple jobs being executed at the same time */ + if (m->action_what) + return sd_bus_error_setf(error, BUS_ERROR_OPERATION_IN_PROGRESS, "There's already a shutdown or sleep operation in progress"); + + if (sleep_verb) { + r = can_sleep(sleep_verb); + if (r < 0) + return r; + + if (r == 0) + return sd_bus_error_setf(error, BUS_ERROR_SLEEP_VERB_NOT_SUPPORTED, "Sleep verb not supported"); + } + + r = verify_shutdown_creds(m, message, w, interactive, action, action_multiple_sessions, + action_ignore_inhibit, error); + if (r != 0) + return r; + r = bus_manager_shutdown_or_sleep_now_or_later(m, unit_name, w, error); if (r < 0) return r; @@ -1691,7 +1738,6 @@ static int method_poweroff(sd_bus *bus, sd_bus_message *message, void *userdata, "org.freedesktop.login1.power-off-multiple-sessions", "org.freedesktop.login1.power-off-ignore-inhibit", NULL, - method_poweroff, error); } @@ -1706,7 +1752,6 @@ static int method_reboot(sd_bus *bus, sd_bus_message *message, void *userdata, s "org.freedesktop.login1.reboot-multiple-sessions", "org.freedesktop.login1.reboot-ignore-inhibit", NULL, - method_reboot, error); } @@ -1721,10 +1766,117 @@ static int method_suspend(sd_bus *bus, sd_bus_message *message, void *userdata, "org.freedesktop.login1.suspend-multiple-sessions", "org.freedesktop.login1.suspend-ignore-inhibit", "suspend", - method_suspend, error); } +static int manager_scheduled_shutdown_handler( + sd_event_source *s, + uint64_t usec, + void *userdata) { + + _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; + Manager *m = userdata; + const char *target; + int r; + + assert(m); + + if (isempty(m->scheduled_shutdown_type)) + return 0; + + if (streq(m->scheduled_shutdown_type, "halt")) + target = SPECIAL_HALT_TARGET; + else if (streq(m->scheduled_shutdown_type, "poweroff")) + target = SPECIAL_POWEROFF_TARGET; + else + target = SPECIAL_REBOOT_TARGET; + + r = execute_shutdown_or_sleep(m, 0, target, &error); + if (r < 0) + return log_error_errno(r, "Unable to execute transition to %s: %m\n", target); + + return 0; +} + +static int method_schedule_shutdown(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) { + Manager *m = userdata; + const char *action_multiple_sessions = NULL; + const char *action_ignore_inhibit = NULL; + const char *action = NULL; + uint64_t elapse; + char *type; + int r; + + assert(m); + assert(message); + + r = sd_bus_message_read(message, "st", &type, &elapse); + if (r < 0) + return r; + + if (streq(type, "reboot")) { + action = "org.freedesktop.login1.reboot"; + action_multiple_sessions = "org.freedesktop.login1.reboot-multiple-sessions"; + action_ignore_inhibit = "org.freedesktop.login1.reboot-ignore-inhibit"; + } else if (streq(type, "halt")) { + action = "org.freedesktop.login1.halt"; + action_multiple_sessions = "org.freedesktop.login1.halt-multiple-sessions"; + action_ignore_inhibit = "org.freedesktop.login1.halt-ignore-inhibit"; + } else if (streq(type, "poweroff")) { + action = "org.freedesktop.login1.poweroff"; + action_multiple_sessions = "org.freedesktop.login1.poweroff-multiple-sessions"; + action_ignore_inhibit = "org.freedesktop.login1.poweroff-ignore-inhibit"; + } else + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unsupported shutdown type"); + + r = verify_shutdown_creds(m, message, INHIBIT_SHUTDOWN, false, + action, action_multiple_sessions, action_ignore_inhibit, error); + if (r != 0) + return r; + + if (m->scheduled_shutdown_timeout_source) { + r = sd_event_source_set_time(m->scheduled_shutdown_timeout_source, elapse); + if (r < 0) + return log_error_errno(r, "sd_event_source_set_time() failed: %m\n"); + + r = sd_event_source_set_enabled(m->scheduled_shutdown_timeout_source, SD_EVENT_ONESHOT); + if (r < 0) + return log_error_errno(r, "sd_event_source_set_enabled() failed: %m\n"); + } else { + r = sd_event_add_time(m->event, &m->scheduled_shutdown_timeout_source, + CLOCK_REALTIME, elapse, 0, manager_scheduled_shutdown_handler, m); + if (r < 0) + return log_error_errno(r, "sd_event_add_time() failed: %m\n"); + } + + r = free_and_strdup(&m->scheduled_shutdown_type, type); + if (r < 0) { + m->scheduled_shutdown_timeout_source = sd_event_source_unref(m->scheduled_shutdown_timeout_source); + return log_oom(); + } + + m->scheduled_shutdown_timeout = elapse; + + return sd_bus_reply_method_return(message, NULL); +} + +static int method_cancel_scheduled_shutdown(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) { + Manager *m = userdata; + bool cancelled; + + assert(m); + assert(message); + + cancelled = m->scheduled_shutdown_type != NULL; + + m->scheduled_shutdown_timeout_source = sd_event_source_unref(m->scheduled_shutdown_timeout_source); + free(m->scheduled_shutdown_type); + m->scheduled_shutdown_type = NULL; + m->scheduled_shutdown_timeout = 0; + + return sd_bus_reply_method_return(message, "b", cancelled); +} + static int method_hibernate(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) { Manager *m = userdata; @@ -1736,7 +1888,6 @@ static int method_hibernate(sd_bus *bus, sd_bus_message *message, void *userdata "org.freedesktop.login1.hibernate-multiple-sessions", "org.freedesktop.login1.hibernate-ignore-inhibit", "hibernate", - method_hibernate, error); } @@ -1751,7 +1902,6 @@ static int method_hybrid_sleep(sd_bus *bus, sd_bus_message *message, void *userd "org.freedesktop.login1.hibernate-multiple-sessions", "org.freedesktop.login1.hibernate-ignore-inhibit", "hybrid-sleep", - method_hybrid_sleep, error); } @@ -2146,6 +2296,7 @@ const sd_bus_vtable manager_vtable[] = { SD_BUS_PROPERTY("IdleActionUSec", "t", NULL, offsetof(Manager, idle_action_usec), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("PreparingForShutdown", "b", property_get_preparing, 0, 0), SD_BUS_PROPERTY("PreparingForSleep", "b", property_get_preparing, 0, 0), + SD_BUS_PROPERTY("ScheduledShutdown", "(st)", property_get_scheduled_shutdown, 0, 0), SD_BUS_METHOD("GetSession", "s", "o", method_get_session, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("GetSessionByPID", "u", "o", method_get_session_by_pid, SD_BUS_VTABLE_UNPRIVILEGED), @@ -2175,6 +2326,8 @@ const sd_bus_vtable manager_vtable[] = { SD_BUS_METHOD("PowerOff", "b", NULL, method_poweroff, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("Reboot", "b", NULL, method_reboot, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("Suspend", "b", NULL, method_suspend, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("ScheduleShutdown", "st", NULL, method_schedule_shutdown, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("CancelScheduledShutdown", NULL, "b", method_cancel_scheduled_shutdown, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("Hibernate", "b", NULL, method_hibernate, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("HybridSleep", "b", NULL, method_hybrid_sleep, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("CanPowerOff", NULL, "s", method_can_poweroff, SD_BUS_VTABLE_UNPRIVILEGED),