diff --git a/src/login/logind-dbus.c b/src/login/logind-dbus.c index 6dd375c164..35549e663a 100644 --- a/src/login/logind-dbus.c +++ b/src/login/logind-dbus.c @@ -412,10 +412,23 @@ static int property_get_scheduled_shutdown( if (r < 0) return r; - r = sd_bus_message_append( - reply, "st", - handle_action_to_string(m->scheduled_shutdown_action), - m->scheduled_shutdown_timeout); + if (m->delayed_action) { + usec_t t = m->scheduled_shutdown_timeout; /* fall back to the schedule time on failure below */ + + if (m->inhibit_timeout_source) { + r = sd_event_source_get_time(m->inhibit_timeout_source, &t); + if (r < 0) + log_debug_errno(r, "Failed to get time of inhibit timeout event source, ignoring: %m"); + } + + r = sd_bus_message_append( + reply, "st", + handle_action_to_string(m->delayed_action->handle), t); + } else + r = sd_bus_message_append( + reply, "st", + handle_action_to_string(m->scheduled_shutdown_action), + m->scheduled_shutdown_timeout); if (r < 0) return r; @@ -1886,15 +1899,18 @@ static int execute_shutdown_or_sleep( if (r < 0) goto fail; + /* Save the action to prevent another request of shutdown or friends before the current action being + * finished. See method_do_shutdown_or_sleep(). This is also used in match_job_removed() to log what + * kind of action is finished. */ m->delayed_action = a; - /* Make sure the lid switch is ignored for a while */ + /* Make sure the lid switch is ignored for a while. */ manager_set_lid_switch_ignore(m, usec_add(now(CLOCK_MONOTONIC), m->holdoff_timeout_usec)); return 0; fail: - /* Tell people that they now may take a lock again */ + /* Tell people that they now may take a lock again. */ (void) send_prepare_for(m, a, false); return r; @@ -1957,29 +1973,34 @@ static int delay_shutdown_or_sleep( assert(m); assert(a); - if (m->inhibit_timeout_source) { - r = sd_event_source_set_time_relative(m->inhibit_timeout_source, m->inhibit_delay_max); - if (r < 0) - return log_error_errno(r, "sd_event_source_set_time_relative() failed: %m"); - - r = sd_event_source_set_enabled(m->inhibit_timeout_source, SD_EVENT_ONESHOT); - if (r < 0) - return log_error_errno(r, "sd_event_source_set_enabled() failed: %m"); - } else { - r = sd_event_add_time_relative( - m->event, - &m->inhibit_timeout_source, - CLOCK_MONOTONIC, m->inhibit_delay_max, 0, - manager_inhibit_timeout_handler, m); - if (r < 0) - return r; - } + r = event_reset_time_relative( + m->event, &m->inhibit_timeout_source, + CLOCK_MONOTONIC, m->inhibit_delay_max, /* accuracy = */ 0, + manager_inhibit_timeout_handler, m, + /* priority = */ 0, "inhibit-timeout", /* force = */ true); + if (r < 0) + return log_error_errno(r, "Failed to reset timer event source for inhibit timeout: %m"); m->delayed_action = a; return 0; } +static void cancel_delayed_action(Manager *m) { + assert(m); + + (void) sd_event_source_set_enabled(m->inhibit_timeout_source, SD_EVENT_OFF); + + /* When m->action_job is NULL, the delayed action has not been triggered yet. Let's clear it to + * accept later shutdown and friends. + * + * When m->action_job is non-NULL, the delayed action has been already triggered, and now we are + * waiting for the job being finished. In match_job_removed(), the triggered action will be used. + * Hence, do not clear it. */ + if (!m->action_job) + m->delayed_action = NULL; +} + int bus_manager_shutdown_or_sleep_now_or_later( Manager *m, const HandleActionData *a, @@ -2722,6 +2743,7 @@ static int method_cancel_scheduled_shutdown(sd_bus_message *message, void *userd username, tty, logind_wall_tty_filter, m); } + cancel_delayed_action(m); reset_scheduled_shutdown(m); return sd_bus_reply_method_return(message, "b", true);