#include <unistd.h>
+#include "sd-messages.h"
+
#include "alloc-util.h"
#include "bus-error.h"
#include "bus-util.h"
#include "logind-dbus.h"
#include "logind-session-dbus.h"
#include "process-util.h"
-#include "sleep-config.h"
#include "special.h"
#include "string-table.h"
#include "terminal-util.h"
#include "user-util.h"
+static const ActionTableItem action_table[_HANDLE_ACTION_MAX] = {
+ [HANDLE_POWEROFF] = {
+ SPECIAL_POWEROFF_TARGET,
+ INHIBIT_SHUTDOWN,
+ "org.freedesktop.login1.power-off",
+ "org.freedesktop.login1.power-off-multiple-sessions",
+ "org.freedesktop.login1.power-off-ignore-inhibit",
+ _SLEEP_OPERATION_INVALID,
+ SD_MESSAGE_SHUTDOWN_STR,
+ "System is powering down",
+ "power-off",
+ },
+ [HANDLE_REBOOT] = {
+ SPECIAL_REBOOT_TARGET,
+ INHIBIT_SHUTDOWN,
+ "org.freedesktop.login1.reboot",
+ "org.freedesktop.login1.reboot-multiple-sessions",
+ "org.freedesktop.login1.reboot-ignore-inhibit",
+ _SLEEP_OPERATION_INVALID,
+ SD_MESSAGE_SHUTDOWN_STR,
+ "System is rebooting",
+ "reboot",
+ },
+ [HANDLE_HALT] = {
+ SPECIAL_HALT_TARGET,
+ INHIBIT_SHUTDOWN,
+ "org.freedesktop.login1.halt",
+ "org.freedesktop.login1.halt-multiple-sessions",
+ "org.freedesktop.login1.halt-ignore-inhibit",
+ _SLEEP_OPERATION_INVALID,
+ SD_MESSAGE_SHUTDOWN_STR,
+ "System is halting",
+ "halt",
+ },
+ [HANDLE_KEXEC] = {
+ SPECIAL_KEXEC_TARGET,
+ INHIBIT_SHUTDOWN,
+ "org.freedesktop.login1.reboot",
+ "org.freedesktop.login1.reboot-multiple-sessions",
+ "org.freedesktop.login1.reboot-ignore-inhibit",
+ _SLEEP_OPERATION_INVALID,
+ SD_MESSAGE_SHUTDOWN_STR,
+ "System is rebooting with kexec",
+ "kexec",
+ },
+ [HANDLE_SUSPEND] = {
+ SPECIAL_SUSPEND_TARGET,
+ INHIBIT_SLEEP,
+ "org.freedesktop.login1.suspend",
+ "org.freedesktop.login1.suspend-multiple-sessions",
+ "org.freedesktop.login1.suspend-ignore-inhibit",
+ SLEEP_SUSPEND,
+ },
+ [HANDLE_HIBERNATE] = {
+ SPECIAL_HIBERNATE_TARGET,
+ INHIBIT_SLEEP,
+ "org.freedesktop.login1.hibernate",
+ "org.freedesktop.login1.hibernate-multiple-sessions",
+ "org.freedesktop.login1.hibernate-ignore-inhibit",
+ SLEEP_HIBERNATE,
+ },
+ [HANDLE_HYBRID_SLEEP] = {
+ SPECIAL_HYBRID_SLEEP_TARGET,
+ INHIBIT_SLEEP,
+ "org.freedesktop.login1.hibernate",
+ "org.freedesktop.login1.hibernate-multiple-sessions",
+ "org.freedesktop.login1.hibernate-ignore-inhibit",
+ SLEEP_HYBRID_SLEEP,
+ },
+ [HANDLE_SUSPEND_THEN_HIBERNATE] = {
+ SPECIAL_SUSPEND_THEN_HIBERNATE_TARGET,
+ INHIBIT_SLEEP,
+ "org.freedesktop.login1.hibernate",
+ "org.freedesktop.login1.hibernate-multiple-sessions",
+ "org.freedesktop.login1.hibernate-ignore-inhibit",
+ SLEEP_SUSPEND_THEN_HIBERNATE,
+ },
+ [HANDLE_FACTORY_RESET] = {
+ SPECIAL_FACTORY_RESET_TARGET,
+ _INHIBIT_WHAT_INVALID,
+ NULL,
+ NULL,
+ NULL,
+ _SLEEP_OPERATION_INVALID,
+ SD_MESSAGE_FACTORY_RESET_STR,
+ "System is performing factory reset",
+ NULL
+ },
+};
+
const char* manager_target_for_action(HandleAction handle) {
- static const char * const target_table[_HANDLE_ACTION_MAX] = {
- [HANDLE_POWEROFF] = SPECIAL_POWEROFF_TARGET,
- [HANDLE_REBOOT] = SPECIAL_REBOOT_TARGET,
- [HANDLE_HALT] = SPECIAL_HALT_TARGET,
- [HANDLE_KEXEC] = SPECIAL_KEXEC_TARGET,
- [HANDLE_SUSPEND] = SPECIAL_SUSPEND_TARGET,
- [HANDLE_HIBERNATE] = SPECIAL_HIBERNATE_TARGET,
- [HANDLE_HYBRID_SLEEP] = SPECIAL_HYBRID_SLEEP_TARGET,
- [HANDLE_SUSPEND_THEN_HIBERNATE] = SPECIAL_SUSPEND_THEN_HIBERNATE_TARGET,
- [HANDLE_FACTORY_RESET] = SPECIAL_FACTORY_RESET_TARGET,
- };
+ assert(handle >= 0);
+ assert(handle < (ssize_t) ELEMENTSOF(action_table));
+ return action_table[handle].target;
+}
+
+const ActionTableItem* manager_item_for_handle(HandleAction handle) {
assert(handle >= 0);
- if (handle < (ssize_t) ELEMENTSOF(target_table))
- return target_table[handle];
- return NULL;
+ assert(handle < (ssize_t) ELEMENTSOF(action_table));
+
+ return &action_table[handle];
+}
+
+HandleAction manager_handle_for_item(const ActionTableItem* a) {
+ if (a && a < action_table + ELEMENTSOF(action_table))
+ return a - action_table;
+ return _HANDLE_ACTION_INVALID;
}
int manager_handle_action(
InhibitWhat inhibit_operation;
Inhibitor *offending = NULL;
bool supported;
- const char *target;
int r;
assert(m);
return log_warning_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
"Requested %s operation not supported, ignoring.", handle_action_to_string(handle));
- if (m->action_what > 0)
+ if (m->delayed_action)
return log_debug_errno(SYNTHETIC_ERRNO(EALREADY),
"Action already in progress (%s), ignoring requested %s operation.",
- inhibit_what_to_string(m->action_what),
+ inhibit_what_to_string(m->delayed_action->inhibit_what),
handle_action_to_string(handle));
- assert_se(target = manager_target_for_action(handle));
-
- inhibit_operation = IN_SET(handle, HANDLE_SUSPEND, HANDLE_HIBERNATE,
- HANDLE_HYBRID_SLEEP,
- HANDLE_SUSPEND_THEN_HIBERNATE) ? INHIBIT_SLEEP : INHIBIT_SHUTDOWN;
+ inhibit_operation = manager_item_for_handle(handle)->inhibit_what;
/* If the actual operation is inhibited, warn and fail */
if (!ignore_inhibited &&
log_info("%s", message_table[handle]);
- r = bus_manager_shutdown_or_sleep_now_or_later(m, target, inhibit_operation, &error);
+ r = bus_manager_shutdown_or_sleep_now_or_later(m, manager_item_for_handle(handle), &error);
if (r < 0)
return log_error_errno(r, "Failed to execute %s operation: %s",
handle_action_to_string(handle),
#include "fileio.h"
#include "format-util.h"
#include "fs-util.h"
+#include "logind-action.h"
#include "logind-dbus.h"
#include "logind-polkit.h"
#include "logind-seat-dbus.h"
#include "utmp-wtmp.h"
#include "virt.h"
+static void reset_scheduled_shutdown(Manager *m);
+
static int get_sender_session(
Manager *m,
sd_bus_message *message,
sd_bus_error *error) {
Manager *m = userdata;
- bool b;
+ bool b = false;
assert(bus);
assert(reply);
assert(m);
- if (streq(property, "PreparingForShutdown"))
- b = m->action_what & INHIBIT_SHUTDOWN;
- else
- b = m->action_what & INHIBIT_SLEEP;
+ if (m->delayed_action) {
+ if (streq(property, "PreparingForShutdown"))
+ b = m->delayed_action->inhibit_what & INHIBIT_SHUTDOWN;
+ else
+ b = m->delayed_action->inhibit_what & INHIBIT_SLEEP;
+ }
return sd_bus_message_append(reply, "b", b);
}
if (r < 0)
return r;
- r = sd_bus_message_append(reply, "st", m->scheduled_shutdown_type, m->scheduled_shutdown_timeout);
+ r = sd_bus_message_append(reply, "st",
+ handle_action_to_string(manager_handle_for_item(m->scheduled_shutdown_type)),
+ m->scheduled_shutdown_timeout);
if (r < 0)
return r;
return false;
}
-_printf_(2, 0)
-static int log_with_wall_message(Manager *m, const char *d, const char *p, const char *q) {
+static int bus_manager_log_shutdown(
+ Manager *m,
+ const ActionTableItem *a) {
+
+ const char *message, *log_str;
+
assert(m);
+ assert(a);
+
+ message = a->message;
+ log_str = a->log_str;
+
+ if (message)
+ message = strjoina("MESSAGE=", message);
+ else
+ message = "MESSAGE=System is shutting down";
if (isempty(m->wall_message))
- p = strjoina(p, ".");
+ message = strjoina(message, ".");
else
- p = strjoina(p, " (", m->wall_message, ").");
+ message = strjoina(message, " (", m->wall_message, ").");
- return log_struct(LOG_NOTICE, d, p, q);
-}
+ if (log_str)
+ log_str = strjoina("SHUTDOWN=", log_str);
-static int bus_manager_log_shutdown(
- Manager *m,
- const char *unit_name) {
-
- assert(m);
- assert(unit_name);
-
- if (streq(unit_name, SPECIAL_POWEROFF_TARGET))
- return log_with_wall_message(m,
- "MESSAGE_ID=" SD_MESSAGE_SHUTDOWN_STR,
- "MESSAGE=System is powering down",
- "SHUTDOWN=power-off");
-
- if (streq(unit_name, SPECIAL_REBOOT_TARGET))
- return log_with_wall_message(m,
- "MESSAGE_ID=" SD_MESSAGE_SHUTDOWN_STR,
- "MESSAGE=System is rebooting",
- "SHUTDOWN=reboot");
-
- if (streq(unit_name, SPECIAL_HALT_TARGET))
- return log_with_wall_message(m,
- "MESSAGE_ID=" SD_MESSAGE_SHUTDOWN_STR,
- "MESSAGE=System is halting",
- "SHUTDOWN=halt");
-
- if (streq(unit_name, SPECIAL_KEXEC_TARGET))
- return log_with_wall_message(m,
- "MESSAGE_ID=" SD_MESSAGE_SHUTDOWN_STR,
- "MESSAGE=System is rebooting with kexec",
- "SHUTDOWN=kexec");
-
- if (streq(unit_name, SPECIAL_FACTORY_RESET_TARGET))
- return log_with_wall_message(m,
- "MESSAGE_ID=" SD_MESSAGE_FACTORY_RESET_STR,
- "MESSAGE=System is performing factory reset",
- NULL);
-
- return log_with_wall_message(m,
- "MESSAGE_ID=" SD_MESSAGE_SHUTDOWN_STR,
- "MESSAGE=System is shutting down",
- NULL);
+ return log_struct(LOG_NOTICE,
+ "MESSAGE_ID=%s", a->message_id ? a->message_id : SD_MESSAGE_SHUTDOWN_STR,
+ message,
+ log_str);
}
static int lid_switch_ignore_handler(sd_event_source *e, uint64_t usec, void *userdata) {
static int execute_shutdown_or_sleep(
Manager *m,
- InhibitWhat w,
- const char *unit_name,
+ const ActionTableItem *a,
sd_bus_error *error) {
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
int r;
assert(m);
- assert(w > 0);
- assert(w < _INHIBIT_WHAT_MAX);
- assert(unit_name);
+ assert(a);
- if (w == INHIBIT_SHUTDOWN)
- bus_manager_log_shutdown(m, unit_name);
+ if (a->inhibit_what == INHIBIT_SHUTDOWN)
+ bus_manager_log_shutdown(m, a);
r = bus_call_method(
m->bus,
"StartUnit",
error,
&reply,
- "ss", unit_name, "replace-irreversibly");
+ "ss", a->target, "replace-irreversibly");
if (r < 0)
goto error;
if (r < 0)
goto error;
- m->action_unit = unit_name;
- m->action_what = w;
+ m->delayed_action = a;
/* 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));
error:
/* Tell people that they now may take a lock again */
- (void) send_prepare_for(m, w, false);
+ (void) send_prepare_for(m, a->inhibit_what, false);
return r;
}
assert(manager);
- if (manager->action_what == 0 || manager->action_job)
+ if (!manager->delayed_action || manager->action_job)
return 0;
- if (manager_is_inhibited(manager, manager->action_what, INHIBIT_DELAY, NULL, false, false, 0, &offending)) {
+ if (manager_is_inhibited(manager, manager->delayed_action->inhibit_what, INHIBIT_DELAY, NULL, false, false, 0, &offending)) {
_cleanup_free_ char *comm = NULL, *u = NULL;
if (!timeout)
}
/* Actually do the operation */
- r = execute_shutdown_or_sleep(manager, manager->action_what, manager->action_unit, &error);
+ r = execute_shutdown_or_sleep(manager, manager->delayed_action, &error);
if (r < 0) {
log_warning("Error during inhibitor-delayed operation (already returned success to client): %s",
bus_error_message(&error, r));
- manager->action_unit = NULL;
- manager->action_what = 0;
+ manager->delayed_action = NULL;
}
return 1; /* We did some work. */
static int delay_shutdown_or_sleep(
Manager *m,
- InhibitWhat w,
- const char *unit_name) {
+ const ActionTableItem *a) {
int r;
assert(m);
- assert(w >= 0);
- assert(w < _INHIBIT_WHAT_MAX);
- assert(unit_name);
+ assert(a);
if (m->inhibit_timeout_source) {
r = sd_event_source_set_time_relative(m->inhibit_timeout_source, m->inhibit_delay_max);
return r;
}
- m->action_unit = unit_name;
- m->action_what = w;
+ m->delayed_action = a;
return 0;
}
int bus_manager_shutdown_or_sleep_now_or_later(
Manager *m,
- const char *unit_name,
- InhibitWhat w,
+ const ActionTableItem *a,
sd_bus_error *error) {
_cleanup_free_ char *load_state = NULL;
int r;
assert(m);
- assert(unit_name);
- assert(w > 0);
- assert(w < _INHIBIT_WHAT_MAX);
+ assert(a);
assert(!m->action_job);
- r = unit_load_state(m->bus, unit_name, &load_state);
+ r = unit_load_state(m->bus, a->target, &load_state);
if (r < 0)
return r;
if (!streq(load_state, "loaded"))
return log_notice_errno(SYNTHETIC_ERRNO(EACCES),
"Unit %s is %s, refusing operation.",
- unit_name, load_state);
+ a->target, load_state);
/* Tell everybody to prepare for shutdown/sleep */
- (void) send_prepare_for(m, w, true);
+ (void) send_prepare_for(m, a->inhibit_what, true);
delayed =
m->inhibit_delay_max > 0 &&
- manager_is_inhibited(m, w, INHIBIT_DELAY, NULL, false, false, 0, NULL);
+ manager_is_inhibited(m, a->inhibit_what, INHIBIT_DELAY, NULL, false, false, 0, NULL);
if (delayed)
/* Shutdown is delayed, keep in mind what we
* want to do, and start a timeout */
- r = delay_shutdown_or_sleep(m, w, unit_name);
+ r = delay_shutdown_or_sleep(m, a);
else
/* Shutdown is not delayed, execute it
* immediately */
- r = execute_shutdown_or_sleep(m, w, unit_name, error);
+ r = execute_shutdown_or_sleep(m, a, error);
return r;
}
static int verify_shutdown_creds(
Manager *m,
sd_bus_message *message,
- InhibitWhat w,
- const char *action,
- const char *action_multiple_sessions,
- const char *action_ignore_inhibit,
+ const ActionTableItem *a,
uint64_t flags,
sd_bus_error *error) {
int r;
assert(m);
+ assert(a);
assert(message);
- assert(w >= 0);
- assert(w <= _INHIBIT_WHAT_MAX);
- assert(action);
- assert(action_multiple_sessions);
- assert(action_ignore_inhibit);
r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_EUID, &creds);
if (r < 0)
return r;
multiple_sessions = r > 0;
- blocked = manager_is_inhibited(m, w, INHIBIT_BLOCK, NULL, false, true, uid, NULL);
+ blocked = manager_is_inhibited(m, a->inhibit_what, INHIBIT_BLOCK, NULL, false, true, uid, NULL);
interactive = flags & SD_LOGIND_INTERACTIVE;
if (multiple_sessions) {
- r = bus_verify_polkit_async(message, CAP_SYS_BOOT, action_multiple_sessions, NULL, interactive, UID_INVALID, &m->polkit_registry, error);
+ r = bus_verify_polkit_async(
+ message,
+ CAP_SYS_BOOT,
+ a->polkit_action_multiple_sessions,
+ NULL,
+ interactive,
+ UID_INVALID,
+ &m->polkit_registry,
+ error);
if (r < 0)
return r;
if (r == 0)
return sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED,
"Access denied to root due to active block inhibitor");
- r = bus_verify_polkit_async(message, CAP_SYS_BOOT, action_ignore_inhibit, NULL, interactive, UID_INVALID, &m->polkit_registry, error);
+ r = bus_verify_polkit_async(message,
+ CAP_SYS_BOOT,
+ a->polkit_action_ignore_inhibit,
+ NULL,
+ interactive,
+ UID_INVALID,
+ &m->polkit_registry,
+ error);
if (r < 0)
return r;
if (r == 0)
}
if (!multiple_sessions && !blocked) {
- r = bus_verify_polkit_async(message, CAP_SYS_BOOT, action, NULL, interactive, UID_INVALID, &m->polkit_registry, error);
+ r = bus_verify_polkit_async(message,
+ CAP_SYS_BOOT,
+ a->polkit_action,
+ NULL,
+ interactive,
+ UID_INVALID,
+ &m->polkit_registry,
+ error);
if (r < 0)
return r;
if (r == 0)
return 0;
}
+static int setup_wall_message_timer(Manager *m, sd_bus_message* message) {
+ _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
+ int r;
+
+ r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_AUGMENT|SD_BUS_CREDS_TTY|SD_BUS_CREDS_UID, &creds);
+ if (r >= 0) {
+ const char *tty = NULL;
+
+ (void) sd_bus_creds_get_uid(creds, &m->scheduled_shutdown_uid);
+ (void) sd_bus_creds_get_tty(creds, &tty);
+
+ r = free_and_strdup(&m->scheduled_shutdown_tty, tty);
+ if (r < 0)
+ return log_oom();
+ }
+
+ r = manager_setup_wall_message_timer(m);
+ if (r < 0)
+ return r;
+
+ 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,
- SleepOperation sleep_operation,
+ const ActionTableItem *a,
bool with_flags,
sd_bus_error *error) {
assert(m);
assert(message);
- assert(unit_name);
- assert(w >= 0);
- assert(w <= _INHIBIT_WHAT_MAX);
+ assert(a);
if (with_flags) {
/* New style method: with flags parameter (and interactive bool in the bus message header) */
return r;
if ((flags & ~SD_LOGIND_SHUTDOWN_AND_SLEEP_FLAGS_PUBLIC) != 0)
return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid flags parameter");
- if (!streq(unit_name, SPECIAL_REBOOT_TARGET) && (flags & SD_LOGIND_REBOOT_VIA_KEXEC))
+ if (manager_handle_for_item(a) != HANDLE_REBOOT && (flags & SD_LOGIND_REBOOT_VIA_KEXEC))
return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Reboot via kexec is only applicable with reboot operations");
} else {
/* Old style method: no flags parameter, but interactive bool passed as boolean in
}
if ((flags & SD_LOGIND_REBOOT_VIA_KEXEC) && kexec_loaded())
- unit_name = SPECIAL_KEXEC_TARGET;
+ a = manager_item_for_handle(HANDLE_KEXEC);
/* Don't allow multiple jobs being executed at the same time */
- if (m->action_what > 0)
+ if (m->delayed_action)
return sd_bus_error_setf(error, BUS_ERROR_OPERATION_IN_PROGRESS,
"There's already a shutdown or sleep operation in progress");
- if (sleep_operation >= 0) {
- r = can_sleep(sleep_operation);
+ if (a->sleep_operation >= 0) {
+ r = can_sleep(a->sleep_operation);
if (r == -ENOSPC)
return sd_bus_error_set(error, BUS_ERROR_SLEEP_VERB_NOT_SUPPORTED,
"Not enough swap space for hibernation");
if (r == 0)
return sd_bus_error_setf(error, BUS_ERROR_SLEEP_VERB_NOT_SUPPORTED,
- "Sleep verb \"%s\" not supported", sleep_operation_to_string(sleep_operation));
+ "Sleep verb \"%s\" not supported", sleep_operation_to_string(a->sleep_operation));
if (r < 0)
return r;
}
- r = verify_shutdown_creds(m, message, w, action, action_multiple_sessions,
- action_ignore_inhibit, flags, error);
+ r = verify_shutdown_creds(m, message, a, flags, error);
if (r != 0)
return r;
- r = bus_manager_shutdown_or_sleep_now_or_later(m, unit_name, w, error);
+ /* reset case we're shorting a scheduled shutdown */
+ m->unlink_nologin = false;
+ reset_scheduled_shutdown(m);
+
+ m->scheduled_shutdown_timeout = 0;
+ m->scheduled_shutdown_type = a;
+
+ (void) setup_wall_message_timer(m, message);
+
+ r = bus_manager_shutdown_or_sleep_now_or_later(m, a, error);
if (r < 0)
return r;
return method_do_shutdown_or_sleep(
m, message,
- SPECIAL_POWEROFF_TARGET,
- INHIBIT_SHUTDOWN,
- "org.freedesktop.login1.power-off",
- "org.freedesktop.login1.power-off-multiple-sessions",
- "org.freedesktop.login1.power-off-ignore-inhibit",
- _SLEEP_OPERATION_INVALID,
+ manager_item_for_handle(HANDLE_POWEROFF),
sd_bus_message_is_method_call(message, NULL, "PowerOffWithFlags"),
error);
}
return method_do_shutdown_or_sleep(
m, message,
- SPECIAL_REBOOT_TARGET,
- INHIBIT_SHUTDOWN,
- "org.freedesktop.login1.reboot",
- "org.freedesktop.login1.reboot-multiple-sessions",
- "org.freedesktop.login1.reboot-ignore-inhibit",
- _SLEEP_OPERATION_INVALID,
+ manager_item_for_handle(HANDLE_REBOOT),
sd_bus_message_is_method_call(message, NULL, "RebootWithFlags"),
error);
}
return method_do_shutdown_or_sleep(
m, message,
- SPECIAL_HALT_TARGET,
- INHIBIT_SHUTDOWN,
- "org.freedesktop.login1.halt",
- "org.freedesktop.login1.halt-multiple-sessions",
- "org.freedesktop.login1.halt-ignore-inhibit",
- _SLEEP_OPERATION_INVALID,
+ manager_item_for_handle(HANDLE_HALT),
sd_bus_message_is_method_call(message, NULL, "HaltWithFlags"),
error);
}
return method_do_shutdown_or_sleep(
m, message,
- SPECIAL_SUSPEND_TARGET,
- INHIBIT_SLEEP,
- "org.freedesktop.login1.suspend",
- "org.freedesktop.login1.suspend-multiple-sessions",
- "org.freedesktop.login1.suspend-ignore-inhibit",
- SLEEP_SUSPEND,
+ manager_item_for_handle(HANDLE_SUSPEND),
sd_bus_message_is_method_call(message, NULL, "SuspendWithFlags"),
error);
}
return method_do_shutdown_or_sleep(
m, message,
- SPECIAL_HIBERNATE_TARGET,
- INHIBIT_SLEEP,
- "org.freedesktop.login1.hibernate",
- "org.freedesktop.login1.hibernate-multiple-sessions",
- "org.freedesktop.login1.hibernate-ignore-inhibit",
- SLEEP_HIBERNATE,
+ manager_item_for_handle(HANDLE_HIBERNATE),
sd_bus_message_is_method_call(message, NULL, "HibernateWithFlags"),
error);
}
return method_do_shutdown_or_sleep(
m, message,
- SPECIAL_HYBRID_SLEEP_TARGET,
- INHIBIT_SLEEP,
- "org.freedesktop.login1.hibernate",
- "org.freedesktop.login1.hibernate-multiple-sessions",
- "org.freedesktop.login1.hibernate-ignore-inhibit",
- SLEEP_HYBRID_SLEEP,
+ manager_item_for_handle(HANDLE_HYBRID_SLEEP),
sd_bus_message_is_method_call(message, NULL, "HybridSleepWithFlags"),
error);
}
return method_do_shutdown_or_sleep(
m, message,
- SPECIAL_SUSPEND_THEN_HIBERNATE_TARGET,
- INHIBIT_SLEEP,
- "org.freedesktop.login1.hibernate",
- "org.freedesktop.login1.hibernate-multiple-sessions",
- "org.freedesktop.login1.hibernate-ignore-inhibit",
- SLEEP_SUSPEND_THEN_HIBERNATE,
+ manager_item_for_handle(HANDLE_SUSPEND_THEN_HIBERNATE),
sd_bus_message_is_method_call(message, NULL, "SuspendThenHibernateWithFlags"),
error);
}
"MODE=%s\n",
m->scheduled_shutdown_timeout,
m->enable_wall_messages,
- m->scheduled_shutdown_type);
+ handle_action_to_string(manager_handle_for_item(m->scheduled_shutdown_type)));
if (!isempty(m->wall_message)) {
_cleanup_free_ char *t = NULL;
m->wall_message_timeout_source = sd_event_source_unref(m->wall_message_timeout_source);
m->nologin_timeout_source = sd_event_source_unref(m->nologin_timeout_source);
- m->scheduled_shutdown_type = mfree(m->scheduled_shutdown_type);
- m->scheduled_shutdown_timeout = 0;
+ m->scheduled_shutdown_type = NULL;
m->shutdown_dry_run = false;
if (m->unlink_nologin) {
uint64_t usec,
void *userdata) {
+ const ActionTableItem *a = NULL;
_cleanup_(sd_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, "poweroff"))
- target = SPECIAL_POWEROFF_TARGET;
- else if (streq(m->scheduled_shutdown_type, "reboot"))
- target = SPECIAL_REBOOT_TARGET;
- else if (streq(m->scheduled_shutdown_type, "kexec"))
- target = SPECIAL_KEXEC_TARGET;
- else if (streq(m->scheduled_shutdown_type, "halt"))
- target = SPECIAL_HALT_TARGET;
- else
- assert_not_reached();
+ a = m->scheduled_shutdown_type;
+ assert(a);
/* Don't allow multiple jobs being executed at the same time */
- if (m->action_what > 0) {
+ if (m->delayed_action) {
r = -EALREADY;
- log_error("Scheduled shutdown to %s failed: shutdown or sleep operation already in progress", target);
+ log_error("Scheduled shutdown to %s failed: shutdown or sleep operation already in progress", a->target);
goto error;
}
* above) for some seconds after our admin has seen the final
* wall message. */
- bus_manager_log_shutdown(m, target);
+ bus_manager_log_shutdown(m, a);
log_info("Running in dry run, suppressing action.");
reset_scheduled_shutdown(m);
return 0;
}
- r = bus_manager_shutdown_or_sleep_now_or_later(m, target, INHIBIT_SHUTDOWN, &error);
+ r = bus_manager_shutdown_or_sleep_now_or_later(m, m->scheduled_shutdown_type, &error);
if (r < 0) {
- log_error_errno(r, "Scheduled shutdown to %s failed: %m", target);
+ log_error_errno(r, "Scheduled shutdown to %s failed: %m", a->target);
goto error;
}
static int method_schedule_shutdown(sd_bus_message *message, void *userdata, sd_bus_error *error) {
Manager *m = userdata;
- _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
- const char *action_multiple_sessions = NULL;
- const char *action_ignore_inhibit = NULL;
- const char *action = NULL;
+ HandleAction handle;
+ const ActionTableItem *a;
uint64_t elapse;
char *type;
int r;
dry_run = true;
}
- if (streq(type, "poweroff")) {
- action = "org.freedesktop.login1.power-off";
- action_multiple_sessions = "org.freedesktop.login1.power-off-multiple-sessions";
- action_ignore_inhibit = "org.freedesktop.login1.power-off-ignore-inhibit";
- } else if (STR_IN_SET(type, "reboot", "kexec")) {
- 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
+ handle = handle_action_from_string(type);
+ if (!IN_SET(handle, HANDLE_POWEROFF, HANDLE_REBOOT, HANDLE_HALT, HANDLE_KEXEC))
return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Unsupported shutdown type");
- r = verify_shutdown_creds(m, message, INHIBIT_SHUTDOWN, action, action_multiple_sessions,
- action_ignore_inhibit, 0, error);
+ a = manager_item_for_handle(handle);
+ assert(a);
+ assert(a->polkit_action);
+
+ r = verify_shutdown_creds(m, message, a, 0, error);
if (r != 0)
return r;
return log_error_errno(r, "sd_event_add_time() failed: %m");
}
- 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_type = a;
m->shutdown_dry_run = dry_run;
if (m->nologin_timeout_source) {
m->scheduled_shutdown_timeout = elapse;
- r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_AUGMENT|SD_BUS_CREDS_TTY|SD_BUS_CREDS_UID, &creds);
- if (r >= 0) {
- const char *tty = NULL;
-
- (void) sd_bus_creds_get_uid(creds, &m->scheduled_shutdown_uid);
- (void) sd_bus_creds_get_tty(creds, &tty);
-
- r = free_and_strdup(&m->scheduled_shutdown_tty, tty);
- if (r < 0) {
- m->scheduled_shutdown_timeout_source = sd_event_source_unref(m->scheduled_shutdown_timeout_source);
- return log_oom();
- }
- }
-
- r = manager_setup_wall_message_timer(m);
- if (r < 0)
+ r = setup_wall_message_timer(m, message);
+ if (r < 0) {
+ m->scheduled_shutdown_timeout_source = sd_event_source_unref(m->scheduled_shutdown_timeout_source);
return r;
+ }
r = update_schedule_file(m);
if (r < 0)
static int method_cancel_scheduled_shutdown(sd_bus_message *message, void *userdata, sd_bus_error *error) {
Manager *m = userdata;
+ const ActionTableItem *a;
bool cancelled;
+ int r;
assert(m);
assert(message);
- cancelled = m->scheduled_shutdown_type != NULL;
+ cancelled = !IN_SET(manager_handle_for_item(m->scheduled_shutdown_type), HANDLE_IGNORE, _HANDLE_ACTION_INVALID);
+ if (!cancelled)
+ goto done;
+
+ a = m->scheduled_shutdown_type;
+ if (!a->polkit_action)
+ return sd_bus_error_set(error, SD_BUS_ERROR_AUTH_FAILED, "Unsupported shutdown type");
+
+ r = bus_verify_polkit_async(
+ message,
+ CAP_SYS_BOOT,
+ a->polkit_action,
+ NULL,
+ false,
+ UID_INVALID,
+ &m->polkit_registry,
+ error);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
+
reset_scheduled_shutdown(m);
- if (cancelled && m->enable_wall_messages) {
+ if (m->enable_wall_messages) {
_cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
_cleanup_free_ char *username = NULL;
const char *tty = NULL;
uid_t uid = 0;
- int r;
r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_AUGMENT|SD_BUS_CREDS_TTY|SD_BUS_CREDS_UID, &creds);
if (r >= 0) {
username, tty, logind_wall_tty_filter, m);
}
+done:
return sd_bus_reply_method_return(message, "b", cancelled);
}
static int method_can_shutdown_or_sleep(
Manager *m,
sd_bus_message *message,
- InhibitWhat w,
- const char *action,
- const char *action_multiple_sessions,
- const char *action_ignore_inhibit,
- SleepOperation sleep_operation,
+ const ActionTableItem *a,
sd_bus_error *error) {
_cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
- HandleAction handle;
bool multiple_sessions, challenge, blocked;
const char *result = NULL;
uid_t uid;
assert(m);
assert(message);
- assert(w >= 0);
- assert(w <= _INHIBIT_WHAT_MAX);
- assert(action);
- assert(action_multiple_sessions);
- assert(action_ignore_inhibit);
-
- if (sleep_operation >= 0) {
- r = can_sleep(sleep_operation);
+ assert(a);
+
+ if (a->sleep_operation >= 0) {
+ r = can_sleep(a->sleep_operation);
if (IN_SET(r, 0, -ENOSPC))
return sd_bus_reply_method_return(message, "s", "na");
if (r < 0)
return r;
multiple_sessions = r > 0;
- blocked = manager_is_inhibited(m, w, INHIBIT_BLOCK, NULL, false, true, uid, NULL);
+ blocked = manager_is_inhibited(m, a->inhibit_what, INHIBIT_BLOCK, NULL, false, true, uid, NULL);
- handle = handle_action_from_string(sleep_operation_to_string(sleep_operation));
+ HandleAction handle = handle_action_from_string(sleep_operation_to_string(a->sleep_operation));
if (handle >= 0) {
const char *target;
}
if (multiple_sessions) {
- r = bus_test_polkit(message, CAP_SYS_BOOT, action_multiple_sessions, NULL, UID_INVALID, &challenge, error);
+ r = bus_test_polkit(message, CAP_SYS_BOOT, a->polkit_action_multiple_sessions, NULL, UID_INVALID, &challenge, error);
if (r < 0)
return r;
}
if (blocked) {
- r = bus_test_polkit(message, CAP_SYS_BOOT, action_ignore_inhibit, NULL, UID_INVALID, &challenge, error);
+ r = bus_test_polkit(message, CAP_SYS_BOOT, a->polkit_action_ignore_inhibit, NULL, UID_INVALID, &challenge, error);
if (r < 0)
return r;
/* If neither inhibit nor multiple sessions
* apply then just check the normal policy */
- r = bus_test_polkit(message, CAP_SYS_BOOT, action, NULL, UID_INVALID, &challenge, error);
+ r = bus_test_polkit(message, CAP_SYS_BOOT, a->polkit_action, NULL, UID_INVALID, &challenge, error);
if (r < 0)
return r;
Manager *m = userdata;
return method_can_shutdown_or_sleep(
- m, message,
- INHIBIT_SHUTDOWN,
- "org.freedesktop.login1.power-off",
- "org.freedesktop.login1.power-off-multiple-sessions",
- "org.freedesktop.login1.power-off-ignore-inhibit",
- _SLEEP_OPERATION_INVALID,
+ m, message, manager_item_for_handle(HANDLE_POWEROFF),
error);
}
Manager *m = userdata;
return method_can_shutdown_or_sleep(
- m, message,
- INHIBIT_SHUTDOWN,
- "org.freedesktop.login1.reboot",
- "org.freedesktop.login1.reboot-multiple-sessions",
- "org.freedesktop.login1.reboot-ignore-inhibit",
- _SLEEP_OPERATION_INVALID,
+ m, message, manager_item_for_handle(HANDLE_REBOOT),
error);
}
Manager *m = userdata;
return method_can_shutdown_or_sleep(
- m, message,
- INHIBIT_SHUTDOWN,
- "org.freedesktop.login1.halt",
- "org.freedesktop.login1.halt-multiple-sessions",
- "org.freedesktop.login1.halt-ignore-inhibit",
- _SLEEP_OPERATION_INVALID,
+ m, message, manager_item_for_handle(HANDLE_HALT),
error);
}
Manager *m = userdata;
return method_can_shutdown_or_sleep(
- m, message,
- INHIBIT_SLEEP,
- "org.freedesktop.login1.suspend",
- "org.freedesktop.login1.suspend-multiple-sessions",
- "org.freedesktop.login1.suspend-ignore-inhibit",
- SLEEP_SUSPEND,
+ m, message, manager_item_for_handle(HANDLE_SUSPEND),
error);
}
Manager *m = userdata;
return method_can_shutdown_or_sleep(
- m, message,
- INHIBIT_SLEEP,
- "org.freedesktop.login1.hibernate",
- "org.freedesktop.login1.hibernate-multiple-sessions",
- "org.freedesktop.login1.hibernate-ignore-inhibit",
- SLEEP_HIBERNATE,
+ m, message, manager_item_for_handle(HANDLE_HIBERNATE),
error);
}
Manager *m = userdata;
return method_can_shutdown_or_sleep(
- m, message,
- INHIBIT_SLEEP,
- "org.freedesktop.login1.hibernate",
- "org.freedesktop.login1.hibernate-multiple-sessions",
- "org.freedesktop.login1.hibernate-ignore-inhibit",
- SLEEP_HYBRID_SLEEP,
+ m, message, manager_item_for_handle(HANDLE_HYBRID_SLEEP),
error);
}
Manager *m = userdata;
return method_can_shutdown_or_sleep(
- m, message,
- INHIBIT_SLEEP,
- "org.freedesktop.login1.hibernate",
- "org.freedesktop.login1.hibernate-multiple-sessions",
- "org.freedesktop.login1.hibernate-ignore-inhibit",
- SLEEP_SUSPEND_THEN_HIBERNATE,
+ m, message, manager_item_for_handle(HANDLE_SUSPEND_THEN_HIBERNATE),
error);
}
if (r < 0)
return r;
+ /* sysvinit has a 252 (256-(strlen(" \r\n")+1)) character
+ * limit for the wall message. There is no real technical
+ * need for that but doesn't make sense to store arbitrary
+ * armounts either.
+ * https://git.savannah.nongnu.org/cgit/sysvinit.git/tree/src/shutdown.c#n72)
+ */
+ if (strlen(wall_message) > 252)
+ return -EMSGSIZE;
+
/* Short-circuit the operation if the desired state is already in place, to
* avoid an unnecessary polkit permission check. */
if (streq_ptr(m->wall_message, empty_to_null(wall_message)) &&
* executing the operation. We shouldn't create the impression
* that the lock was successful if the machine is about to go
* down/suspend any moment. */
- if (m->action_what & w)
+ if (m->delayed_action && m->delayed_action->inhibit_what & w)
return sd_bus_error_setf(error, BUS_ERROR_OPERATION_IN_PROGRESS,
"The operation inhibition has been requested for is already running");
}
if (m->action_job && streq(m->action_job, path)) {
- log_info("Operation '%s' finished.", inhibit_what_to_string(m->action_what));
+ assert(m->delayed_action);
+ log_info("Operation '%s' finished.", inhibit_what_to_string(m->delayed_action->inhibit_what));
/* Tell people that they now may take a lock again */
- (void) send_prepare_for(m, m->action_what, false);
+ (void) send_prepare_for(m, m->delayed_action->inhibit_what, false);
m->action_job = mfree(m->action_job);
- m->action_unit = NULL;
- m->action_what = 0;
+ m->delayed_action = NULL;
return 0;
}