mirror of
https://github.com/systemd/systemd-stable.git
synced 2025-01-25 06:03:40 +03:00
Merge pull request #21838 from lnussel/logind-refactor
Logind shutdown refactor
This commit is contained in:
commit
256ce2e889
@ -2,6 +2,8 @@
|
|||||||
|
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "sd-messages.h"
|
||||||
|
|
||||||
#include "alloc-util.h"
|
#include "alloc-util.h"
|
||||||
#include "bus-error.h"
|
#include "bus-error.h"
|
||||||
#include "bus-util.h"
|
#include "bus-util.h"
|
||||||
@ -11,29 +13,119 @@
|
|||||||
#include "logind-dbus.h"
|
#include "logind-dbus.h"
|
||||||
#include "logind-session-dbus.h"
|
#include "logind-session-dbus.h"
|
||||||
#include "process-util.h"
|
#include "process-util.h"
|
||||||
#include "sleep-config.h"
|
|
||||||
#include "special.h"
|
#include "special.h"
|
||||||
#include "string-table.h"
|
#include "string-table.h"
|
||||||
#include "terminal-util.h"
|
#include "terminal-util.h"
|
||||||
#include "user-util.h"
|
#include "user-util.h"
|
||||||
|
|
||||||
const char* manager_target_for_action(HandleAction handle) {
|
static const ActionTableItem action_table[_HANDLE_ACTION_MAX] = {
|
||||||
static const char * const target_table[_HANDLE_ACTION_MAX] = {
|
[HANDLE_POWEROFF] = {
|
||||||
[HANDLE_POWEROFF] = SPECIAL_POWEROFF_TARGET,
|
SPECIAL_POWEROFF_TARGET,
|
||||||
[HANDLE_REBOOT] = SPECIAL_REBOOT_TARGET,
|
INHIBIT_SHUTDOWN,
|
||||||
[HANDLE_HALT] = SPECIAL_HALT_TARGET,
|
"org.freedesktop.login1.power-off",
|
||||||
[HANDLE_KEXEC] = SPECIAL_KEXEC_TARGET,
|
"org.freedesktop.login1.power-off-multiple-sessions",
|
||||||
[HANDLE_SUSPEND] = SPECIAL_SUSPEND_TARGET,
|
"org.freedesktop.login1.power-off-ignore-inhibit",
|
||||||
[HANDLE_HIBERNATE] = SPECIAL_HIBERNATE_TARGET,
|
_SLEEP_OPERATION_INVALID,
|
||||||
[HANDLE_HYBRID_SLEEP] = SPECIAL_HYBRID_SLEEP_TARGET,
|
SD_MESSAGE_SHUTDOWN_STR,
|
||||||
[HANDLE_SUSPEND_THEN_HIBERNATE] = SPECIAL_SUSPEND_THEN_HIBERNATE_TARGET,
|
"System is powering down",
|
||||||
[HANDLE_FACTORY_RESET] = SPECIAL_FACTORY_RESET_TARGET,
|
"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) {
|
||||||
assert(handle >= 0);
|
assert(handle >= 0);
|
||||||
if (handle < (ssize_t) ELEMENTSOF(target_table))
|
assert(handle < (ssize_t) ELEMENTSOF(action_table));
|
||||||
return target_table[handle];
|
|
||||||
return NULL;
|
return action_table[handle].target;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ActionTableItem* manager_item_for_handle(HandleAction handle) {
|
||||||
|
assert(handle >= 0);
|
||||||
|
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(
|
int manager_handle_action(
|
||||||
@ -59,7 +151,6 @@ int manager_handle_action(
|
|||||||
InhibitWhat inhibit_operation;
|
InhibitWhat inhibit_operation;
|
||||||
Inhibitor *offending = NULL;
|
Inhibitor *offending = NULL;
|
||||||
bool supported;
|
bool supported;
|
||||||
const char *target;
|
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
assert(m);
|
assert(m);
|
||||||
@ -129,17 +220,13 @@ int manager_handle_action(
|
|||||||
return log_warning_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
|
return log_warning_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
|
||||||
"Requested %s operation not supported, ignoring.", handle_action_to_string(handle));
|
"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),
|
return log_debug_errno(SYNTHETIC_ERRNO(EALREADY),
|
||||||
"Action already in progress (%s), ignoring requested %s operation.",
|
"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));
|
handle_action_to_string(handle));
|
||||||
|
|
||||||
assert_se(target = manager_target_for_action(handle));
|
inhibit_operation = manager_item_for_handle(handle)->inhibit_what;
|
||||||
|
|
||||||
inhibit_operation = IN_SET(handle, HANDLE_SUSPEND, HANDLE_HIBERNATE,
|
|
||||||
HANDLE_HYBRID_SLEEP,
|
|
||||||
HANDLE_SUSPEND_THEN_HIBERNATE) ? INHIBIT_SLEEP : INHIBIT_SHUTDOWN;
|
|
||||||
|
|
||||||
/* If the actual operation is inhibited, warn and fail */
|
/* If the actual operation is inhibited, warn and fail */
|
||||||
if (!ignore_inhibited &&
|
if (!ignore_inhibited &&
|
||||||
@ -162,7 +249,7 @@ int manager_handle_action(
|
|||||||
|
|
||||||
log_info("%s", message_table[handle]);
|
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)
|
if (r < 0)
|
||||||
return log_error_errno(r, "Failed to execute %s operation: %s",
|
return log_error_errno(r, "Failed to execute %s operation: %s",
|
||||||
handle_action_to_string(handle),
|
handle_action_to_string(handle),
|
||||||
|
@ -19,8 +19,26 @@ typedef enum HandleAction {
|
|||||||
_HANDLE_ACTION_INVALID = -EINVAL,
|
_HANDLE_ACTION_INVALID = -EINVAL,
|
||||||
} HandleAction;
|
} HandleAction;
|
||||||
|
|
||||||
|
typedef struct ActionTableItem ActionTableItem;
|
||||||
|
|
||||||
|
#define handle_action_valid(x) (x && (x < _HANDLE_ACTION_MAX))
|
||||||
|
|
||||||
#include "logind-inhibit.h"
|
#include "logind-inhibit.h"
|
||||||
#include "logind.h"
|
#include "logind.h"
|
||||||
|
#include "sleep-config.h"
|
||||||
|
|
||||||
|
struct ActionTableItem {
|
||||||
|
const char *target;
|
||||||
|
InhibitWhat inhibit_what;
|
||||||
|
const char *polkit_action;
|
||||||
|
const char *polkit_action_multiple_sessions;
|
||||||
|
const char *polkit_action_ignore_inhibit;
|
||||||
|
SleepOperation sleep_operation;
|
||||||
|
const char* message_id;
|
||||||
|
const char* message;
|
||||||
|
const char* log_str;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
int manager_handle_action(
|
int manager_handle_action(
|
||||||
Manager *m,
|
Manager *m,
|
||||||
@ -33,5 +51,7 @@ const char* handle_action_to_string(HandleAction h) _const_;
|
|||||||
HandleAction handle_action_from_string(const char *s) _pure_;
|
HandleAction handle_action_from_string(const char *s) _pure_;
|
||||||
|
|
||||||
const char* manager_target_for_action(HandleAction handle);
|
const char* manager_target_for_action(HandleAction handle);
|
||||||
|
const ActionTableItem* manager_item_for_handle(HandleAction handle);
|
||||||
|
HandleAction manager_handle_for_item(const ActionTableItem* a);
|
||||||
|
|
||||||
CONFIG_PARSER_PROTOTYPE(config_parse_handle_action);
|
CONFIG_PARSER_PROTOTYPE(config_parse_handle_action);
|
||||||
|
@ -84,8 +84,7 @@ static void button_lid_switch_handle_action(Manager *manager, bool is_edge) {
|
|||||||
* differently */
|
* differently */
|
||||||
if (manager_is_docked_or_external_displays(manager))
|
if (manager_is_docked_or_external_displays(manager))
|
||||||
handle_action = manager->handle_lid_switch_docked;
|
handle_action = manager->handle_lid_switch_docked;
|
||||||
else if (manager->handle_lid_switch_ep != _HANDLE_ACTION_INVALID &&
|
else if (!handle_action_valid(manager->handle_lid_switch_ep) && manager_is_on_external_power())
|
||||||
manager_is_on_external_power())
|
|
||||||
handle_action = manager->handle_lid_switch_ep;
|
handle_action = manager->handle_lid_switch_ep;
|
||||||
else
|
else
|
||||||
handle_action = manager->handle_lid_switch;
|
handle_action = manager->handle_lid_switch;
|
||||||
|
@ -29,6 +29,7 @@
|
|||||||
#include "fileio.h"
|
#include "fileio.h"
|
||||||
#include "format-util.h"
|
#include "format-util.h"
|
||||||
#include "fs-util.h"
|
#include "fs-util.h"
|
||||||
|
#include "logind-action.h"
|
||||||
#include "logind-dbus.h"
|
#include "logind-dbus.h"
|
||||||
#include "logind-polkit.h"
|
#include "logind-polkit.h"
|
||||||
#include "logind-seat-dbus.h"
|
#include "logind-seat-dbus.h"
|
||||||
@ -53,6 +54,8 @@
|
|||||||
#include "utmp-wtmp.h"
|
#include "utmp-wtmp.h"
|
||||||
#include "virt.h"
|
#include "virt.h"
|
||||||
|
|
||||||
|
static void reset_scheduled_shutdown(Manager *m);
|
||||||
|
|
||||||
static int get_sender_session(
|
static int get_sender_session(
|
||||||
Manager *m,
|
Manager *m,
|
||||||
sd_bus_message *message,
|
sd_bus_message *message,
|
||||||
@ -309,16 +312,18 @@ static int property_get_preparing(
|
|||||||
sd_bus_error *error) {
|
sd_bus_error *error) {
|
||||||
|
|
||||||
Manager *m = userdata;
|
Manager *m = userdata;
|
||||||
bool b;
|
bool b = false;
|
||||||
|
|
||||||
assert(bus);
|
assert(bus);
|
||||||
assert(reply);
|
assert(reply);
|
||||||
assert(m);
|
assert(m);
|
||||||
|
|
||||||
if (streq(property, "PreparingForShutdown"))
|
if (m->delayed_action) {
|
||||||
b = m->action_what & INHIBIT_SHUTDOWN;
|
if (streq(property, "PreparingForShutdown"))
|
||||||
else
|
b = m->delayed_action->inhibit_what & INHIBIT_SHUTDOWN;
|
||||||
b = m->action_what & INHIBIT_SLEEP;
|
else
|
||||||
|
b = m->delayed_action->inhibit_what & INHIBIT_SLEEP;
|
||||||
|
}
|
||||||
|
|
||||||
return sd_bus_message_append(reply, "b", b);
|
return sd_bus_message_append(reply, "b", b);
|
||||||
}
|
}
|
||||||
@ -343,7 +348,9 @@ static int property_get_scheduled_shutdown(
|
|||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
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)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
@ -1488,59 +1495,35 @@ static int have_multiple_sessions(
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
_printf_(2, 0)
|
|
||||||
static int log_with_wall_message(Manager *m, const char *d, const char *p, const char *q) {
|
|
||||||
assert(m);
|
|
||||||
|
|
||||||
if (isempty(m->wall_message))
|
|
||||||
p = strjoina(p, ".");
|
|
||||||
else
|
|
||||||
p = strjoina(p, " (", m->wall_message, ").");
|
|
||||||
|
|
||||||
return log_struct(LOG_NOTICE, d, p, q);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int bus_manager_log_shutdown(
|
static int bus_manager_log_shutdown(
|
||||||
Manager *m,
|
Manager *m,
|
||||||
const char *unit_name) {
|
const ActionTableItem *a) {
|
||||||
|
|
||||||
|
const char *message, *log_str;
|
||||||
|
|
||||||
assert(m);
|
assert(m);
|
||||||
assert(unit_name);
|
assert(a);
|
||||||
|
|
||||||
if (streq(unit_name, SPECIAL_POWEROFF_TARGET))
|
message = a->message;
|
||||||
return log_with_wall_message(m,
|
log_str = a->log_str;
|
||||||
"MESSAGE_ID=" SD_MESSAGE_SHUTDOWN_STR,
|
|
||||||
"MESSAGE=System is powering down",
|
|
||||||
"SHUTDOWN=power-off");
|
|
||||||
|
|
||||||
if (streq(unit_name, SPECIAL_REBOOT_TARGET))
|
if (message)
|
||||||
return log_with_wall_message(m,
|
message = strjoina("MESSAGE=", message);
|
||||||
"MESSAGE_ID=" SD_MESSAGE_SHUTDOWN_STR,
|
else
|
||||||
"MESSAGE=System is rebooting",
|
message = "MESSAGE=System is shutting down";
|
||||||
"SHUTDOWN=reboot");
|
|
||||||
|
|
||||||
if (streq(unit_name, SPECIAL_HALT_TARGET))
|
if (isempty(m->wall_message))
|
||||||
return log_with_wall_message(m,
|
message = strjoina(message, ".");
|
||||||
"MESSAGE_ID=" SD_MESSAGE_SHUTDOWN_STR,
|
else
|
||||||
"MESSAGE=System is halting",
|
message = strjoina(message, " (", m->wall_message, ").");
|
||||||
"SHUTDOWN=halt");
|
|
||||||
|
|
||||||
if (streq(unit_name, SPECIAL_KEXEC_TARGET))
|
if (log_str)
|
||||||
return log_with_wall_message(m,
|
log_str = strjoina("SHUTDOWN=", log_str);
|
||||||
"MESSAGE_ID=" SD_MESSAGE_SHUTDOWN_STR,
|
|
||||||
"MESSAGE=System is rebooting with kexec",
|
|
||||||
"SHUTDOWN=kexec");
|
|
||||||
|
|
||||||
if (streq(unit_name, SPECIAL_FACTORY_RESET_TARGET))
|
return log_struct(LOG_NOTICE,
|
||||||
return log_with_wall_message(m,
|
"MESSAGE_ID=%s", a->message_id ? a->message_id : SD_MESSAGE_SHUTDOWN_STR,
|
||||||
"MESSAGE_ID=" SD_MESSAGE_FACTORY_RESET_STR,
|
message,
|
||||||
"MESSAGE=System is performing factory reset",
|
log_str);
|
||||||
NULL);
|
|
||||||
|
|
||||||
return log_with_wall_message(m,
|
|
||||||
"MESSAGE_ID=" SD_MESSAGE_SHUTDOWN_STR,
|
|
||||||
"MESSAGE=System is shutting down",
|
|
||||||
NULL);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int lid_switch_ignore_handler(sd_event_source *e, uint64_t usec, void *userdata) {
|
static int lid_switch_ignore_handler(sd_event_source *e, uint64_t usec, void *userdata) {
|
||||||
@ -1604,8 +1587,7 @@ static int send_prepare_for(Manager *m, InhibitWhat w, bool _active) {
|
|||||||
|
|
||||||
static int execute_shutdown_or_sleep(
|
static int execute_shutdown_or_sleep(
|
||||||
Manager *m,
|
Manager *m,
|
||||||
InhibitWhat w,
|
const ActionTableItem *a,
|
||||||
const char *unit_name,
|
|
||||||
sd_bus_error *error) {
|
sd_bus_error *error) {
|
||||||
|
|
||||||
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
|
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
|
||||||
@ -1613,12 +1595,10 @@ static int execute_shutdown_or_sleep(
|
|||||||
int r;
|
int r;
|
||||||
|
|
||||||
assert(m);
|
assert(m);
|
||||||
assert(w > 0);
|
assert(a);
|
||||||
assert(w < _INHIBIT_WHAT_MAX);
|
|
||||||
assert(unit_name);
|
|
||||||
|
|
||||||
if (w == INHIBIT_SHUTDOWN)
|
if (a->inhibit_what == INHIBIT_SHUTDOWN)
|
||||||
bus_manager_log_shutdown(m, unit_name);
|
bus_manager_log_shutdown(m, a);
|
||||||
|
|
||||||
r = bus_call_method(
|
r = bus_call_method(
|
||||||
m->bus,
|
m->bus,
|
||||||
@ -1626,7 +1606,7 @@ static int execute_shutdown_or_sleep(
|
|||||||
"StartUnit",
|
"StartUnit",
|
||||||
error,
|
error,
|
||||||
&reply,
|
&reply,
|
||||||
"ss", unit_name, "replace-irreversibly");
|
"ss", a->target, "replace-irreversibly");
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
@ -1638,8 +1618,7 @@ static int execute_shutdown_or_sleep(
|
|||||||
if (r < 0)
|
if (r < 0)
|
||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
m->action_unit = unit_name;
|
m->delayed_action = a;
|
||||||
m->action_what = w;
|
|
||||||
|
|
||||||
/* 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));
|
manager_set_lid_switch_ignore(m, usec_add(now(CLOCK_MONOTONIC), m->holdoff_timeout_usec));
|
||||||
@ -1648,7 +1627,7 @@ static int execute_shutdown_or_sleep(
|
|||||||
|
|
||||||
error:
|
error:
|
||||||
/* 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, w, false);
|
(void) send_prepare_for(m, a->inhibit_what, false);
|
||||||
|
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
@ -1660,10 +1639,10 @@ int manager_dispatch_delayed(Manager *manager, bool timeout) {
|
|||||||
|
|
||||||
assert(manager);
|
assert(manager);
|
||||||
|
|
||||||
if (manager->action_what == 0 || manager->action_job)
|
if (!manager->delayed_action || manager->action_job)
|
||||||
return 0;
|
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;
|
_cleanup_free_ char *comm = NULL, *u = NULL;
|
||||||
|
|
||||||
if (!timeout)
|
if (!timeout)
|
||||||
@ -1678,13 +1657,12 @@ int manager_dispatch_delayed(Manager *manager, bool timeout) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Actually do the operation */
|
/* 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) {
|
if (r < 0) {
|
||||||
log_warning("Error during inhibitor-delayed operation (already returned success to client): %s",
|
log_warning("Error during inhibitor-delayed operation (already returned success to client): %s",
|
||||||
bus_error_message(&error, r));
|
bus_error_message(&error, r));
|
||||||
|
|
||||||
manager->action_unit = NULL;
|
manager->delayed_action = NULL;
|
||||||
manager->action_what = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return 1; /* We did some work. */
|
return 1; /* We did some work. */
|
||||||
@ -1705,15 +1683,12 @@ static int manager_inhibit_timeout_handler(
|
|||||||
|
|
||||||
static int delay_shutdown_or_sleep(
|
static int delay_shutdown_or_sleep(
|
||||||
Manager *m,
|
Manager *m,
|
||||||
InhibitWhat w,
|
const ActionTableItem *a) {
|
||||||
const char *unit_name) {
|
|
||||||
|
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
assert(m);
|
assert(m);
|
||||||
assert(w >= 0);
|
assert(a);
|
||||||
assert(w < _INHIBIT_WHAT_MAX);
|
|
||||||
assert(unit_name);
|
|
||||||
|
|
||||||
if (m->inhibit_timeout_source) {
|
if (m->inhibit_timeout_source) {
|
||||||
r = sd_event_source_set_time_relative(m->inhibit_timeout_source, m->inhibit_delay_max);
|
r = sd_event_source_set_time_relative(m->inhibit_timeout_source, m->inhibit_delay_max);
|
||||||
@ -1733,16 +1708,14 @@ static int delay_shutdown_or_sleep(
|
|||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
m->action_unit = unit_name;
|
m->delayed_action = a;
|
||||||
m->action_what = w;
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int bus_manager_shutdown_or_sleep_now_or_later(
|
int bus_manager_shutdown_or_sleep_now_or_later(
|
||||||
Manager *m,
|
Manager *m,
|
||||||
const char *unit_name,
|
const ActionTableItem *a,
|
||||||
InhibitWhat w,
|
|
||||||
sd_bus_error *error) {
|
sd_bus_error *error) {
|
||||||
|
|
||||||
_cleanup_free_ char *load_state = NULL;
|
_cleanup_free_ char *load_state = NULL;
|
||||||
@ -1750,35 +1723,33 @@ int bus_manager_shutdown_or_sleep_now_or_later(
|
|||||||
int r;
|
int r;
|
||||||
|
|
||||||
assert(m);
|
assert(m);
|
||||||
assert(unit_name);
|
assert(a);
|
||||||
assert(w > 0);
|
|
||||||
assert(w < _INHIBIT_WHAT_MAX);
|
|
||||||
assert(!m->action_job);
|
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)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
if (!streq(load_state, "loaded"))
|
if (!streq(load_state, "loaded"))
|
||||||
return log_notice_errno(SYNTHETIC_ERRNO(EACCES),
|
return log_notice_errno(SYNTHETIC_ERRNO(EACCES),
|
||||||
"Unit %s is %s, refusing operation.",
|
"Unit %s is %s, refusing operation.",
|
||||||
unit_name, load_state);
|
a->target, load_state);
|
||||||
|
|
||||||
/* Tell everybody to prepare for shutdown/sleep */
|
/* Tell everybody to prepare for shutdown/sleep */
|
||||||
(void) send_prepare_for(m, w, true);
|
(void) send_prepare_for(m, a->inhibit_what, true);
|
||||||
|
|
||||||
delayed =
|
delayed =
|
||||||
m->inhibit_delay_max > 0 &&
|
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)
|
if (delayed)
|
||||||
/* Shutdown is delayed, keep in mind what we
|
/* Shutdown is delayed, keep in mind what we
|
||||||
* want to do, and start a timeout */
|
* want to do, and start a timeout */
|
||||||
r = delay_shutdown_or_sleep(m, w, unit_name);
|
r = delay_shutdown_or_sleep(m, a);
|
||||||
else
|
else
|
||||||
/* Shutdown is not delayed, execute it
|
/* Shutdown is not delayed, execute it
|
||||||
* immediately */
|
* immediately */
|
||||||
r = execute_shutdown_or_sleep(m, w, unit_name, error);
|
r = execute_shutdown_or_sleep(m, a, error);
|
||||||
|
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
@ -1786,10 +1757,7 @@ int bus_manager_shutdown_or_sleep_now_or_later(
|
|||||||
static int verify_shutdown_creds(
|
static int verify_shutdown_creds(
|
||||||
Manager *m,
|
Manager *m,
|
||||||
sd_bus_message *message,
|
sd_bus_message *message,
|
||||||
InhibitWhat w,
|
const ActionTableItem *a,
|
||||||
const char *action,
|
|
||||||
const char *action_multiple_sessions,
|
|
||||||
const char *action_ignore_inhibit,
|
|
||||||
uint64_t flags,
|
uint64_t flags,
|
||||||
sd_bus_error *error) {
|
sd_bus_error *error) {
|
||||||
|
|
||||||
@ -1799,12 +1767,8 @@ static int verify_shutdown_creds(
|
|||||||
int r;
|
int r;
|
||||||
|
|
||||||
assert(m);
|
assert(m);
|
||||||
|
assert(a);
|
||||||
assert(message);
|
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);
|
r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_EUID, &creds);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
@ -1819,11 +1783,19 @@ static int verify_shutdown_creds(
|
|||||||
return r;
|
return r;
|
||||||
|
|
||||||
multiple_sessions = r > 0;
|
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;
|
interactive = flags & SD_LOGIND_INTERACTIVE;
|
||||||
|
|
||||||
if (multiple_sessions) {
|
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)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
if (r == 0)
|
if (r == 0)
|
||||||
@ -1836,7 +1808,14 @@ static int verify_shutdown_creds(
|
|||||||
return sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED,
|
return sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED,
|
||||||
"Access denied to root due to active block inhibitor");
|
"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)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
if (r == 0)
|
if (r == 0)
|
||||||
@ -1844,7 +1823,14 @@ static int verify_shutdown_creds(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!multiple_sessions && !blocked) {
|
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)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
if (r == 0)
|
if (r == 0)
|
||||||
@ -1854,15 +1840,33 @@ static int verify_shutdown_creds(
|
|||||||
return 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(
|
static int method_do_shutdown_or_sleep(
|
||||||
Manager *m,
|
Manager *m,
|
||||||
sd_bus_message *message,
|
sd_bus_message *message,
|
||||||
const char *unit_name,
|
const ActionTableItem *a,
|
||||||
InhibitWhat w,
|
|
||||||
const char *action,
|
|
||||||
const char *action_multiple_sessions,
|
|
||||||
const char *action_ignore_inhibit,
|
|
||||||
SleepOperation sleep_operation,
|
|
||||||
bool with_flags,
|
bool with_flags,
|
||||||
sd_bus_error *error) {
|
sd_bus_error *error) {
|
||||||
|
|
||||||
@ -1871,9 +1875,7 @@ static int method_do_shutdown_or_sleep(
|
|||||||
|
|
||||||
assert(m);
|
assert(m);
|
||||||
assert(message);
|
assert(message);
|
||||||
assert(unit_name);
|
assert(a);
|
||||||
assert(w >= 0);
|
|
||||||
assert(w <= _INHIBIT_WHAT_MAX);
|
|
||||||
|
|
||||||
if (with_flags) {
|
if (with_flags) {
|
||||||
/* New style method: with flags parameter (and interactive bool in the bus message header) */
|
/* New style method: with flags parameter (and interactive bool in the bus message header) */
|
||||||
@ -1882,7 +1884,7 @@ static int method_do_shutdown_or_sleep(
|
|||||||
return r;
|
return r;
|
||||||
if ((flags & ~SD_LOGIND_SHUTDOWN_AND_SLEEP_FLAGS_PUBLIC) != 0)
|
if ((flags & ~SD_LOGIND_SHUTDOWN_AND_SLEEP_FLAGS_PUBLIC) != 0)
|
||||||
return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid flags parameter");
|
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");
|
return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Reboot via kexec is only applicable with reboot operations");
|
||||||
} else {
|
} else {
|
||||||
/* Old style method: no flags parameter, but interactive bool passed as boolean in
|
/* Old style method: no flags parameter, but interactive bool passed as boolean in
|
||||||
@ -1898,31 +1900,39 @@ static int method_do_shutdown_or_sleep(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ((flags & SD_LOGIND_REBOOT_VIA_KEXEC) && kexec_loaded())
|
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 */
|
/* 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,
|
return sd_bus_error_setf(error, BUS_ERROR_OPERATION_IN_PROGRESS,
|
||||||
"There's already a shutdown or sleep operation in progress");
|
"There's already a shutdown or sleep operation in progress");
|
||||||
|
|
||||||
if (sleep_operation >= 0) {
|
if (a->sleep_operation >= 0) {
|
||||||
r = can_sleep(sleep_operation);
|
r = can_sleep(a->sleep_operation);
|
||||||
if (r == -ENOSPC)
|
if (r == -ENOSPC)
|
||||||
return sd_bus_error_set(error, BUS_ERROR_SLEEP_VERB_NOT_SUPPORTED,
|
return sd_bus_error_set(error, BUS_ERROR_SLEEP_VERB_NOT_SUPPORTED,
|
||||||
"Not enough swap space for hibernation");
|
"Not enough swap space for hibernation");
|
||||||
if (r == 0)
|
if (r == 0)
|
||||||
return sd_bus_error_setf(error, BUS_ERROR_SLEEP_VERB_NOT_SUPPORTED,
|
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)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
r = verify_shutdown_creds(m, message, w, action, action_multiple_sessions,
|
r = verify_shutdown_creds(m, message, a, flags, error);
|
||||||
action_ignore_inhibit, flags, error);
|
|
||||||
if (r != 0)
|
if (r != 0)
|
||||||
return r;
|
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)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
@ -1934,12 +1944,7 @@ static int method_poweroff(sd_bus_message *message, void *userdata, sd_bus_error
|
|||||||
|
|
||||||
return method_do_shutdown_or_sleep(
|
return method_do_shutdown_or_sleep(
|
||||||
m, message,
|
m, message,
|
||||||
SPECIAL_POWEROFF_TARGET,
|
manager_item_for_handle(HANDLE_POWEROFF),
|
||||||
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_bus_message_is_method_call(message, NULL, "PowerOffWithFlags"),
|
sd_bus_message_is_method_call(message, NULL, "PowerOffWithFlags"),
|
||||||
error);
|
error);
|
||||||
}
|
}
|
||||||
@ -1949,12 +1954,7 @@ static int method_reboot(sd_bus_message *message, void *userdata, sd_bus_error *
|
|||||||
|
|
||||||
return method_do_shutdown_or_sleep(
|
return method_do_shutdown_or_sleep(
|
||||||
m, message,
|
m, message,
|
||||||
SPECIAL_REBOOT_TARGET,
|
manager_item_for_handle(HANDLE_REBOOT),
|
||||||
INHIBIT_SHUTDOWN,
|
|
||||||
"org.freedesktop.login1.reboot",
|
|
||||||
"org.freedesktop.login1.reboot-multiple-sessions",
|
|
||||||
"org.freedesktop.login1.reboot-ignore-inhibit",
|
|
||||||
_SLEEP_OPERATION_INVALID,
|
|
||||||
sd_bus_message_is_method_call(message, NULL, "RebootWithFlags"),
|
sd_bus_message_is_method_call(message, NULL, "RebootWithFlags"),
|
||||||
error);
|
error);
|
||||||
}
|
}
|
||||||
@ -1964,12 +1964,7 @@ static int method_halt(sd_bus_message *message, void *userdata, sd_bus_error *er
|
|||||||
|
|
||||||
return method_do_shutdown_or_sleep(
|
return method_do_shutdown_or_sleep(
|
||||||
m, message,
|
m, message,
|
||||||
SPECIAL_HALT_TARGET,
|
manager_item_for_handle(HANDLE_HALT),
|
||||||
INHIBIT_SHUTDOWN,
|
|
||||||
"org.freedesktop.login1.halt",
|
|
||||||
"org.freedesktop.login1.halt-multiple-sessions",
|
|
||||||
"org.freedesktop.login1.halt-ignore-inhibit",
|
|
||||||
_SLEEP_OPERATION_INVALID,
|
|
||||||
sd_bus_message_is_method_call(message, NULL, "HaltWithFlags"),
|
sd_bus_message_is_method_call(message, NULL, "HaltWithFlags"),
|
||||||
error);
|
error);
|
||||||
}
|
}
|
||||||
@ -1979,12 +1974,7 @@ static int method_suspend(sd_bus_message *message, void *userdata, sd_bus_error
|
|||||||
|
|
||||||
return method_do_shutdown_or_sleep(
|
return method_do_shutdown_or_sleep(
|
||||||
m, message,
|
m, message,
|
||||||
SPECIAL_SUSPEND_TARGET,
|
manager_item_for_handle(HANDLE_SUSPEND),
|
||||||
INHIBIT_SLEEP,
|
|
||||||
"org.freedesktop.login1.suspend",
|
|
||||||
"org.freedesktop.login1.suspend-multiple-sessions",
|
|
||||||
"org.freedesktop.login1.suspend-ignore-inhibit",
|
|
||||||
SLEEP_SUSPEND,
|
|
||||||
sd_bus_message_is_method_call(message, NULL, "SuspendWithFlags"),
|
sd_bus_message_is_method_call(message, NULL, "SuspendWithFlags"),
|
||||||
error);
|
error);
|
||||||
}
|
}
|
||||||
@ -1994,12 +1984,7 @@ static int method_hibernate(sd_bus_message *message, void *userdata, sd_bus_erro
|
|||||||
|
|
||||||
return method_do_shutdown_or_sleep(
|
return method_do_shutdown_or_sleep(
|
||||||
m, message,
|
m, message,
|
||||||
SPECIAL_HIBERNATE_TARGET,
|
manager_item_for_handle(HANDLE_HIBERNATE),
|
||||||
INHIBIT_SLEEP,
|
|
||||||
"org.freedesktop.login1.hibernate",
|
|
||||||
"org.freedesktop.login1.hibernate-multiple-sessions",
|
|
||||||
"org.freedesktop.login1.hibernate-ignore-inhibit",
|
|
||||||
SLEEP_HIBERNATE,
|
|
||||||
sd_bus_message_is_method_call(message, NULL, "HibernateWithFlags"),
|
sd_bus_message_is_method_call(message, NULL, "HibernateWithFlags"),
|
||||||
error);
|
error);
|
||||||
}
|
}
|
||||||
@ -2009,12 +1994,7 @@ static int method_hybrid_sleep(sd_bus_message *message, void *userdata, sd_bus_e
|
|||||||
|
|
||||||
return method_do_shutdown_or_sleep(
|
return method_do_shutdown_or_sleep(
|
||||||
m, message,
|
m, message,
|
||||||
SPECIAL_HYBRID_SLEEP_TARGET,
|
manager_item_for_handle(HANDLE_HYBRID_SLEEP),
|
||||||
INHIBIT_SLEEP,
|
|
||||||
"org.freedesktop.login1.hibernate",
|
|
||||||
"org.freedesktop.login1.hibernate-multiple-sessions",
|
|
||||||
"org.freedesktop.login1.hibernate-ignore-inhibit",
|
|
||||||
SLEEP_HYBRID_SLEEP,
|
|
||||||
sd_bus_message_is_method_call(message, NULL, "HybridSleepWithFlags"),
|
sd_bus_message_is_method_call(message, NULL, "HybridSleepWithFlags"),
|
||||||
error);
|
error);
|
||||||
}
|
}
|
||||||
@ -2024,12 +2004,7 @@ static int method_suspend_then_hibernate(sd_bus_message *message, void *userdata
|
|||||||
|
|
||||||
return method_do_shutdown_or_sleep(
|
return method_do_shutdown_or_sleep(
|
||||||
m, message,
|
m, message,
|
||||||
SPECIAL_SUSPEND_THEN_HIBERNATE_TARGET,
|
manager_item_for_handle(HANDLE_SUSPEND_THEN_HIBERNATE),
|
||||||
INHIBIT_SLEEP,
|
|
||||||
"org.freedesktop.login1.hibernate",
|
|
||||||
"org.freedesktop.login1.hibernate-multiple-sessions",
|
|
||||||
"org.freedesktop.login1.hibernate-ignore-inhibit",
|
|
||||||
SLEEP_SUSPEND_THEN_HIBERNATE,
|
|
||||||
sd_bus_message_is_method_call(message, NULL, "SuspendThenHibernateWithFlags"),
|
sd_bus_message_is_method_call(message, NULL, "SuspendThenHibernateWithFlags"),
|
||||||
error);
|
error);
|
||||||
}
|
}
|
||||||
@ -2077,7 +2052,7 @@ static int update_schedule_file(Manager *m) {
|
|||||||
"MODE=%s\n",
|
"MODE=%s\n",
|
||||||
m->scheduled_shutdown_timeout,
|
m->scheduled_shutdown_timeout,
|
||||||
m->enable_wall_messages,
|
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)) {
|
if (!isempty(m->wall_message)) {
|
||||||
_cleanup_free_ char *t = NULL;
|
_cleanup_free_ char *t = NULL;
|
||||||
@ -2116,8 +2091,7 @@ static void reset_scheduled_shutdown(Manager *m) {
|
|||||||
m->wall_message_timeout_source = sd_event_source_unref(m->wall_message_timeout_source);
|
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->nologin_timeout_source = sd_event_source_unref(m->nologin_timeout_source);
|
||||||
|
|
||||||
m->scheduled_shutdown_type = mfree(m->scheduled_shutdown_type);
|
m->scheduled_shutdown_type = NULL;
|
||||||
m->scheduled_shutdown_timeout = 0;
|
|
||||||
m->shutdown_dry_run = false;
|
m->shutdown_dry_run = false;
|
||||||
|
|
||||||
if (m->unlink_nologin) {
|
if (m->unlink_nologin) {
|
||||||
@ -2133,31 +2107,20 @@ static int manager_scheduled_shutdown_handler(
|
|||||||
uint64_t usec,
|
uint64_t usec,
|
||||||
void *userdata) {
|
void *userdata) {
|
||||||
|
|
||||||
|
const ActionTableItem *a = NULL;
|
||||||
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||||
Manager *m = userdata;
|
Manager *m = userdata;
|
||||||
const char *target;
|
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
assert(m);
|
assert(m);
|
||||||
|
|
||||||
if (isempty(m->scheduled_shutdown_type))
|
a = m->scheduled_shutdown_type;
|
||||||
return 0;
|
assert(a);
|
||||||
|
|
||||||
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();
|
|
||||||
|
|
||||||
/* Don't allow multiple jobs being executed at the same time */
|
/* Don't allow multiple jobs being executed at the same time */
|
||||||
if (m->action_what > 0) {
|
if (m->delayed_action) {
|
||||||
r = -EALREADY;
|
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;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2167,16 +2130,16 @@ static int manager_scheduled_shutdown_handler(
|
|||||||
* above) for some seconds after our admin has seen the final
|
* above) for some seconds after our admin has seen the final
|
||||||
* wall message. */
|
* wall message. */
|
||||||
|
|
||||||
bus_manager_log_shutdown(m, target);
|
bus_manager_log_shutdown(m, a);
|
||||||
log_info("Running in dry run, suppressing action.");
|
log_info("Running in dry run, suppressing action.");
|
||||||
reset_scheduled_shutdown(m);
|
reset_scheduled_shutdown(m);
|
||||||
|
|
||||||
return 0;
|
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) {
|
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;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2189,10 +2152,8 @@ error:
|
|||||||
|
|
||||||
static int method_schedule_shutdown(sd_bus_message *message, void *userdata, sd_bus_error *error) {
|
static int method_schedule_shutdown(sd_bus_message *message, void *userdata, sd_bus_error *error) {
|
||||||
Manager *m = userdata;
|
Manager *m = userdata;
|
||||||
_cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
|
HandleAction handle;
|
||||||
const char *action_multiple_sessions = NULL;
|
const ActionTableItem *a;
|
||||||
const char *action_ignore_inhibit = NULL;
|
|
||||||
const char *action = NULL;
|
|
||||||
uint64_t elapse;
|
uint64_t elapse;
|
||||||
char *type;
|
char *type;
|
||||||
int r;
|
int r;
|
||||||
@ -2210,23 +2171,15 @@ static int method_schedule_shutdown(sd_bus_message *message, void *userdata, sd_
|
|||||||
dry_run = true;
|
dry_run = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (streq(type, "poweroff")) {
|
handle = handle_action_from_string(type);
|
||||||
action = "org.freedesktop.login1.power-off";
|
if (!IN_SET(handle, HANDLE_POWEROFF, HANDLE_REBOOT, HANDLE_HALT, HANDLE_KEXEC))
|
||||||
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
|
|
||||||
return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Unsupported shutdown type");
|
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,
|
a = manager_item_for_handle(handle);
|
||||||
action_ignore_inhibit, 0, error);
|
assert(a);
|
||||||
|
assert(a->polkit_action);
|
||||||
|
|
||||||
|
r = verify_shutdown_creds(m, message, a, 0, error);
|
||||||
if (r != 0)
|
if (r != 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
@ -2245,12 +2198,7 @@ static int method_schedule_shutdown(sd_bus_message *message, void *userdata, sd_
|
|||||||
return log_error_errno(r, "sd_event_add_time() failed: %m");
|
return log_error_errno(r, "sd_event_add_time() failed: %m");
|
||||||
}
|
}
|
||||||
|
|
||||||
r = free_and_strdup(&m->scheduled_shutdown_type, type);
|
m->scheduled_shutdown_type = a;
|
||||||
if (r < 0) {
|
|
||||||
m->scheduled_shutdown_timeout_source = sd_event_source_unref(m->scheduled_shutdown_timeout_source);
|
|
||||||
return log_oom();
|
|
||||||
}
|
|
||||||
|
|
||||||
m->shutdown_dry_run = dry_run;
|
m->shutdown_dry_run = dry_run;
|
||||||
|
|
||||||
if (m->nologin_timeout_source) {
|
if (m->nologin_timeout_source) {
|
||||||
@ -2270,23 +2218,11 @@ static int method_schedule_shutdown(sd_bus_message *message, void *userdata, sd_
|
|||||||
|
|
||||||
m->scheduled_shutdown_timeout = elapse;
|
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);
|
r = setup_wall_message_timer(m, message);
|
||||||
if (r >= 0) {
|
if (r < 0) {
|
||||||
const char *tty = NULL;
|
m->scheduled_shutdown_timeout_source = sd_event_source_unref(m->scheduled_shutdown_timeout_source);
|
||||||
|
|
||||||
(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)
|
|
||||||
return r;
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
r = update_schedule_file(m);
|
r = update_schedule_file(m);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
@ -2297,20 +2233,42 @@ static int method_schedule_shutdown(sd_bus_message *message, void *userdata, sd_
|
|||||||
|
|
||||||
static int method_cancel_scheduled_shutdown(sd_bus_message *message, void *userdata, sd_bus_error *error) {
|
static int method_cancel_scheduled_shutdown(sd_bus_message *message, void *userdata, sd_bus_error *error) {
|
||||||
Manager *m = userdata;
|
Manager *m = userdata;
|
||||||
|
const ActionTableItem *a;
|
||||||
bool cancelled;
|
bool cancelled;
|
||||||
|
int r;
|
||||||
|
|
||||||
assert(m);
|
assert(m);
|
||||||
assert(message);
|
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);
|
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_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
|
||||||
_cleanup_free_ char *username = NULL;
|
_cleanup_free_ char *username = NULL;
|
||||||
const char *tty = NULL;
|
const char *tty = NULL;
|
||||||
uid_t uid = 0;
|
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);
|
r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_AUGMENT|SD_BUS_CREDS_TTY|SD_BUS_CREDS_UID, &creds);
|
||||||
if (r >= 0) {
|
if (r >= 0) {
|
||||||
@ -2323,21 +2281,17 @@ static int method_cancel_scheduled_shutdown(sd_bus_message *message, void *userd
|
|||||||
username, tty, logind_wall_tty_filter, m);
|
username, tty, logind_wall_tty_filter, m);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
done:
|
||||||
return sd_bus_reply_method_return(message, "b", cancelled);
|
return sd_bus_reply_method_return(message, "b", cancelled);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int method_can_shutdown_or_sleep(
|
static int method_can_shutdown_or_sleep(
|
||||||
Manager *m,
|
Manager *m,
|
||||||
sd_bus_message *message,
|
sd_bus_message *message,
|
||||||
InhibitWhat w,
|
const ActionTableItem *a,
|
||||||
const char *action,
|
|
||||||
const char *action_multiple_sessions,
|
|
||||||
const char *action_ignore_inhibit,
|
|
||||||
SleepOperation sleep_operation,
|
|
||||||
sd_bus_error *error) {
|
sd_bus_error *error) {
|
||||||
|
|
||||||
_cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
|
_cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
|
||||||
HandleAction handle;
|
|
||||||
bool multiple_sessions, challenge, blocked;
|
bool multiple_sessions, challenge, blocked;
|
||||||
const char *result = NULL;
|
const char *result = NULL;
|
||||||
uid_t uid;
|
uid_t uid;
|
||||||
@ -2345,14 +2299,10 @@ static int method_can_shutdown_or_sleep(
|
|||||||
|
|
||||||
assert(m);
|
assert(m);
|
||||||
assert(message);
|
assert(message);
|
||||||
assert(w >= 0);
|
assert(a);
|
||||||
assert(w <= _INHIBIT_WHAT_MAX);
|
|
||||||
assert(action);
|
|
||||||
assert(action_multiple_sessions);
|
|
||||||
assert(action_ignore_inhibit);
|
|
||||||
|
|
||||||
if (sleep_operation >= 0) {
|
if (a->sleep_operation >= 0) {
|
||||||
r = can_sleep(sleep_operation);
|
r = can_sleep(a->sleep_operation);
|
||||||
if (IN_SET(r, 0, -ENOSPC))
|
if (IN_SET(r, 0, -ENOSPC))
|
||||||
return sd_bus_reply_method_return(message, "s", "na");
|
return sd_bus_reply_method_return(message, "s", "na");
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
@ -2372,9 +2322,9 @@ static int method_can_shutdown_or_sleep(
|
|||||||
return r;
|
return r;
|
||||||
|
|
||||||
multiple_sessions = r > 0;
|
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) {
|
if (handle >= 0) {
|
||||||
const char *target;
|
const char *target;
|
||||||
|
|
||||||
@ -2394,7 +2344,7 @@ static int method_can_shutdown_or_sleep(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (multiple_sessions) {
|
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)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
@ -2407,7 +2357,7 @@ static int method_can_shutdown_or_sleep(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (blocked) {
|
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)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
@ -2425,7 +2375,7 @@ static int method_can_shutdown_or_sleep(
|
|||||||
/* If neither inhibit nor multiple sessions
|
/* If neither inhibit nor multiple sessions
|
||||||
* apply then just check the normal policy */
|
* 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)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
@ -2445,12 +2395,7 @@ static int method_can_poweroff(sd_bus_message *message, void *userdata, sd_bus_e
|
|||||||
Manager *m = userdata;
|
Manager *m = userdata;
|
||||||
|
|
||||||
return method_can_shutdown_or_sleep(
|
return method_can_shutdown_or_sleep(
|
||||||
m, message,
|
m, message, manager_item_for_handle(HANDLE_POWEROFF),
|
||||||
INHIBIT_SHUTDOWN,
|
|
||||||
"org.freedesktop.login1.power-off",
|
|
||||||
"org.freedesktop.login1.power-off-multiple-sessions",
|
|
||||||
"org.freedesktop.login1.power-off-ignore-inhibit",
|
|
||||||
_SLEEP_OPERATION_INVALID,
|
|
||||||
error);
|
error);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2458,12 +2403,7 @@ static int method_can_reboot(sd_bus_message *message, void *userdata, sd_bus_err
|
|||||||
Manager *m = userdata;
|
Manager *m = userdata;
|
||||||
|
|
||||||
return method_can_shutdown_or_sleep(
|
return method_can_shutdown_or_sleep(
|
||||||
m, message,
|
m, message, manager_item_for_handle(HANDLE_REBOOT),
|
||||||
INHIBIT_SHUTDOWN,
|
|
||||||
"org.freedesktop.login1.reboot",
|
|
||||||
"org.freedesktop.login1.reboot-multiple-sessions",
|
|
||||||
"org.freedesktop.login1.reboot-ignore-inhibit",
|
|
||||||
_SLEEP_OPERATION_INVALID,
|
|
||||||
error);
|
error);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2471,12 +2411,7 @@ static int method_can_halt(sd_bus_message *message, void *userdata, sd_bus_error
|
|||||||
Manager *m = userdata;
|
Manager *m = userdata;
|
||||||
|
|
||||||
return method_can_shutdown_or_sleep(
|
return method_can_shutdown_or_sleep(
|
||||||
m, message,
|
m, message, manager_item_for_handle(HANDLE_HALT),
|
||||||
INHIBIT_SHUTDOWN,
|
|
||||||
"org.freedesktop.login1.halt",
|
|
||||||
"org.freedesktop.login1.halt-multiple-sessions",
|
|
||||||
"org.freedesktop.login1.halt-ignore-inhibit",
|
|
||||||
_SLEEP_OPERATION_INVALID,
|
|
||||||
error);
|
error);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2484,12 +2419,7 @@ static int method_can_suspend(sd_bus_message *message, void *userdata, sd_bus_er
|
|||||||
Manager *m = userdata;
|
Manager *m = userdata;
|
||||||
|
|
||||||
return method_can_shutdown_or_sleep(
|
return method_can_shutdown_or_sleep(
|
||||||
m, message,
|
m, message, manager_item_for_handle(HANDLE_SUSPEND),
|
||||||
INHIBIT_SLEEP,
|
|
||||||
"org.freedesktop.login1.suspend",
|
|
||||||
"org.freedesktop.login1.suspend-multiple-sessions",
|
|
||||||
"org.freedesktop.login1.suspend-ignore-inhibit",
|
|
||||||
SLEEP_SUSPEND,
|
|
||||||
error);
|
error);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2497,12 +2427,7 @@ static int method_can_hibernate(sd_bus_message *message, void *userdata, sd_bus_
|
|||||||
Manager *m = userdata;
|
Manager *m = userdata;
|
||||||
|
|
||||||
return method_can_shutdown_or_sleep(
|
return method_can_shutdown_or_sleep(
|
||||||
m, message,
|
m, message, manager_item_for_handle(HANDLE_HIBERNATE),
|
||||||
INHIBIT_SLEEP,
|
|
||||||
"org.freedesktop.login1.hibernate",
|
|
||||||
"org.freedesktop.login1.hibernate-multiple-sessions",
|
|
||||||
"org.freedesktop.login1.hibernate-ignore-inhibit",
|
|
||||||
SLEEP_HIBERNATE,
|
|
||||||
error);
|
error);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2510,12 +2435,7 @@ static int method_can_hybrid_sleep(sd_bus_message *message, void *userdata, sd_b
|
|||||||
Manager *m = userdata;
|
Manager *m = userdata;
|
||||||
|
|
||||||
return method_can_shutdown_or_sleep(
|
return method_can_shutdown_or_sleep(
|
||||||
m, message,
|
m, message, manager_item_for_handle(HANDLE_HYBRID_SLEEP),
|
||||||
INHIBIT_SLEEP,
|
|
||||||
"org.freedesktop.login1.hibernate",
|
|
||||||
"org.freedesktop.login1.hibernate-multiple-sessions",
|
|
||||||
"org.freedesktop.login1.hibernate-ignore-inhibit",
|
|
||||||
SLEEP_HYBRID_SLEEP,
|
|
||||||
error);
|
error);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2523,12 +2443,7 @@ static int method_can_suspend_then_hibernate(sd_bus_message *message, void *user
|
|||||||
Manager *m = userdata;
|
Manager *m = userdata;
|
||||||
|
|
||||||
return method_can_shutdown_or_sleep(
|
return method_can_shutdown_or_sleep(
|
||||||
m, message,
|
m, message, manager_item_for_handle(HANDLE_SUSPEND_THEN_HIBERNATE),
|
||||||
INHIBIT_SLEEP,
|
|
||||||
"org.freedesktop.login1.hibernate",
|
|
||||||
"org.freedesktop.login1.hibernate-multiple-sessions",
|
|
||||||
"org.freedesktop.login1.hibernate-ignore-inhibit",
|
|
||||||
SLEEP_SUSPEND_THEN_HIBERNATE,
|
|
||||||
error);
|
error);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3200,6 +3115,15 @@ static int method_set_wall_message(
|
|||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
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
|
/* Short-circuit the operation if the desired state is already in place, to
|
||||||
* avoid an unnecessary polkit permission check. */
|
* avoid an unnecessary polkit permission check. */
|
||||||
if (streq_ptr(m->wall_message, empty_to_null(wall_message)) &&
|
if (streq_ptr(m->wall_message, empty_to_null(wall_message)) &&
|
||||||
@ -3267,7 +3191,7 @@ static int method_inhibit(sd_bus_message *message, void *userdata, sd_bus_error
|
|||||||
* executing the operation. We shouldn't create the impression
|
* executing the operation. We shouldn't create the impression
|
||||||
* that the lock was successful if the machine is about to go
|
* that the lock was successful if the machine is about to go
|
||||||
* down/suspend any moment. */
|
* 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,
|
return sd_bus_error_setf(error, BUS_ERROR_OPERATION_IN_PROGRESS,
|
||||||
"The operation inhibition has been requested for is already running");
|
"The operation inhibition has been requested for is already running");
|
||||||
|
|
||||||
@ -3774,14 +3698,14 @@ int match_job_removed(sd_bus_message *message, void *userdata, sd_bus_error *err
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (m->action_job && streq(m->action_job, path)) {
|
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 */
|
/* 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_job = mfree(m->action_job);
|
||||||
m->action_unit = NULL;
|
m->delayed_action = NULL;
|
||||||
m->action_what = 0;
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
#include "sd-bus.h"
|
#include "sd-bus.h"
|
||||||
|
|
||||||
#include "bus-object.h"
|
#include "bus-object.h"
|
||||||
|
#include "logind-action.h"
|
||||||
#include "logind-session.h"
|
#include "logind-session.h"
|
||||||
#include "logind-user.h"
|
#include "logind-user.h"
|
||||||
#include "logind.h"
|
#include "logind.h"
|
||||||
@ -14,7 +15,7 @@ int manager_get_seat_from_creds(Manager *m, sd_bus_message *message, const char
|
|||||||
|
|
||||||
int manager_dispatch_delayed(Manager *manager, bool timeout);
|
int manager_dispatch_delayed(Manager *manager, bool timeout);
|
||||||
|
|
||||||
int bus_manager_shutdown_or_sleep_now_or_later(Manager *m, const char *unit_name, InhibitWhat w, sd_bus_error *error);
|
int bus_manager_shutdown_or_sleep_now_or_later(Manager *m, const ActionTableItem *a, sd_bus_error *error);
|
||||||
|
|
||||||
int match_job_removed(sd_bus_message *message, void *userdata, sd_bus_error *error);
|
int match_job_removed(sd_bus_message *message, void *userdata, sd_bus_error *error);
|
||||||
int match_unit_removed(sd_bus_message *message, void *userdata, sd_bus_error *error);
|
int match_unit_removed(sd_bus_message *message, void *userdata, sd_bus_error *error);
|
||||||
|
@ -72,7 +72,7 @@ static int warn_wall(Manager *m, usec_t n) {
|
|||||||
r = asprintf(&l, "%s%sThe system is going down for %s %s%s!",
|
r = asprintf(&l, "%s%sThe system is going down for %s %s%s!",
|
||||||
strempty(m->wall_message),
|
strempty(m->wall_message),
|
||||||
isempty(m->wall_message) ? "" : "\n",
|
isempty(m->wall_message) ? "" : "\n",
|
||||||
m->scheduled_shutdown_type,
|
handle_action_to_string(manager_handle_for_item(m->scheduled_shutdown_type)),
|
||||||
left ? "at " : "NOW",
|
left ? "at " : "NOW",
|
||||||
left ? FORMAT_TIMESTAMP(m->scheduled_shutdown_timeout) : "");
|
left ? FORMAT_TIMESTAMP(m->scheduled_shutdown_timeout) : "");
|
||||||
if (r < 0) {
|
if (r < 0) {
|
||||||
@ -130,16 +130,14 @@ int manager_setup_wall_message_timer(Manager *m) {
|
|||||||
|
|
||||||
/* wall message handling */
|
/* wall message handling */
|
||||||
|
|
||||||
if (isempty(m->scheduled_shutdown_type)) {
|
if (!m->scheduled_shutdown_type)
|
||||||
warn_wall(m, n);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
|
||||||
|
|
||||||
if (elapse < n)
|
if (elapse > 0 && elapse < n)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
/* Warn immediately if less than 15 minutes are left */
|
/* Warn immediately if less than 15 minutes are left */
|
||||||
if (elapse - n < 15 * USEC_PER_MINUTE) {
|
if (elapse == 0 || elapse - n < 15 * USEC_PER_MINUTE) {
|
||||||
r = warn_wall(m, n);
|
r = warn_wall(m, n);
|
||||||
if (r == 0)
|
if (r == 0)
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -54,6 +54,7 @@ static int manager_new(Manager **ret) {
|
|||||||
*m = (Manager) {
|
*m = (Manager) {
|
||||||
.console_active_fd = -1,
|
.console_active_fd = -1,
|
||||||
.reserve_vt_fd = -1,
|
.reserve_vt_fd = -1,
|
||||||
|
.enable_wall_messages = true,
|
||||||
.idle_action_not_before_usec = now(CLOCK_MONOTONIC),
|
.idle_action_not_before_usec = now(CLOCK_MONOTONIC),
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -167,7 +168,6 @@ static Manager* manager_unref(Manager *m) {
|
|||||||
strv_free(m->kill_only_users);
|
strv_free(m->kill_only_users);
|
||||||
strv_free(m->kill_exclude_users);
|
strv_free(m->kill_exclude_users);
|
||||||
|
|
||||||
free(m->scheduled_shutdown_type);
|
|
||||||
free(m->scheduled_shutdown_tty);
|
free(m->scheduled_shutdown_tty);
|
||||||
free(m->wall_message);
|
free(m->wall_message);
|
||||||
free(m->action_job);
|
free(m->action_job);
|
||||||
|
@ -68,21 +68,17 @@ struct Manager {
|
|||||||
usec_t inhibit_delay_max;
|
usec_t inhibit_delay_max;
|
||||||
usec_t user_stop_delay;
|
usec_t user_stop_delay;
|
||||||
|
|
||||||
/* If an action is currently being executed or is delayed,
|
|
||||||
* this is != 0 and encodes what is being done */
|
|
||||||
InhibitWhat action_what;
|
|
||||||
|
|
||||||
/* If a shutdown/suspend was delayed due to an inhibitor this
|
/* If a shutdown/suspend was delayed due to an inhibitor this
|
||||||
contains the unit name we are supposed to start after the
|
contains the action we are supposed to start after the
|
||||||
delay is over */
|
delay is over */
|
||||||
const char *action_unit;
|
const ActionTableItem *delayed_action;
|
||||||
|
|
||||||
/* If a shutdown/suspend is currently executed, then this is
|
/* If a shutdown/suspend is currently executed, then this is
|
||||||
* the job of it */
|
* the job of it */
|
||||||
char *action_job;
|
char *action_job;
|
||||||
sd_event_source *inhibit_timeout_source;
|
sd_event_source *inhibit_timeout_source;
|
||||||
|
|
||||||
char *scheduled_shutdown_type;
|
const ActionTableItem *scheduled_shutdown_type;
|
||||||
usec_t scheduled_shutdown_timeout;
|
usec_t scheduled_shutdown_timeout;
|
||||||
sd_event_source *scheduled_shutdown_timeout_source;
|
sd_event_source *scheduled_shutdown_timeout_source;
|
||||||
uid_t scheduled_shutdown_uid;
|
uid_t scheduled_shutdown_uid;
|
||||||
|
@ -144,35 +144,23 @@ int halt_parse_argv(int argc, char *argv[]) {
|
|||||||
int halt_main(void) {
|
int halt_main(void) {
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
r = logind_check_inhibitors(arg_action);
|
/* always try logind first */
|
||||||
if (r < 0)
|
if (arg_when > 0)
|
||||||
return r;
|
r = logind_schedule_shutdown();
|
||||||
|
else {
|
||||||
|
r = logind_check_inhibitors(arg_action);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
/* Delayed shutdown requested, and was successful */
|
r = logind_reboot(arg_action);
|
||||||
if (arg_when > 0 && logind_schedule_shutdown() == 0)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
/* No delay, or logind failed or is not at all available */
|
|
||||||
if (geteuid() != 0) {
|
|
||||||
if (arg_dry_run || arg_force > 0) {
|
|
||||||
(void) must_be_root();
|
|
||||||
return -EPERM;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Try logind if we are a normal user and no special mode applies. Maybe polkit allows us to
|
|
||||||
* shutdown the machine. */
|
|
||||||
if (IN_SET(arg_action, ACTION_POWEROFF, ACTION_REBOOT, ACTION_KEXEC, ACTION_HALT)) {
|
|
||||||
r = logind_reboot(arg_action);
|
|
||||||
if (r >= 0)
|
|
||||||
return r;
|
|
||||||
if (IN_SET(r, -EOPNOTSUPP, -EINPROGRESS))
|
|
||||||
/* Requested operation is not supported on the local system or already in
|
|
||||||
* progress */
|
|
||||||
return r;
|
|
||||||
|
|
||||||
/* on all other errors, try low-level operation */
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
if (r >= 0)
|
||||||
|
return r;
|
||||||
|
if (IN_SET(r, -EACCES, -EOPNOTSUPP, -EINPROGRESS))
|
||||||
|
/* Requested operation requires auth, is not supported on the local system or already in
|
||||||
|
* progress */
|
||||||
|
return r;
|
||||||
|
/* on all other errors, try low-level operation */
|
||||||
|
|
||||||
/* In order to minimize the difference between operation with and without logind, we explicitly
|
/* In order to minimize the difference between operation with and without logind, we explicitly
|
||||||
* enable non-blocking mode for this, as logind's shutdown operations are always non-blocking. */
|
* enable non-blocking mode for this, as logind's shutdown operations are always non-blocking. */
|
||||||
@ -181,7 +169,10 @@ int halt_main(void) {
|
|||||||
if (!arg_dry_run && !arg_force)
|
if (!arg_dry_run && !arg_force)
|
||||||
return start_with_fallback();
|
return start_with_fallback();
|
||||||
|
|
||||||
assert(geteuid() == 0);
|
if (geteuid() != 0) {
|
||||||
|
(void) must_be_root();
|
||||||
|
return -EPERM;
|
||||||
|
}
|
||||||
|
|
||||||
if (!arg_no_wtmp) {
|
if (!arg_no_wtmp) {
|
||||||
if (sd_booted() > 0)
|
if (sd_booted() > 0)
|
||||||
|
@ -330,7 +330,7 @@ int logind_schedule_shutdown(void) {
|
|||||||
|
|
||||||
r = bus_call_method(bus, bus_login_mgr, "ScheduleShutdown", &error, NULL, "st", action, arg_when);
|
r = bus_call_method(bus, bus_login_mgr, "ScheduleShutdown", &error, NULL, "st", action, arg_when);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return log_warning_errno(r, "Failed to call ScheduleShutdown in logind, proceeding with immediate shutdown: %s", bus_error_message(&error, r));
|
return log_warning_errno(r, "Failed to schedule shutdown: %s", bus_error_message(&error, r));
|
||||||
|
|
||||||
if (!arg_quiet)
|
if (!arg_quiet)
|
||||||
logind_show_shutdown();
|
logind_show_shutdown();
|
||||||
|
@ -213,8 +213,8 @@ int start_special(int argc, char *argv[], void *userdata) {
|
|||||||
r = logind_reboot(a);
|
r = logind_reboot(a);
|
||||||
if (r >= 0)
|
if (r >= 0)
|
||||||
return r;
|
return r;
|
||||||
if (IN_SET(r, -EOPNOTSUPP, -EINPROGRESS))
|
if (IN_SET(r, -EACCES, -EOPNOTSUPP, -EINPROGRESS))
|
||||||
/* Requested operation is not supported or already in progress */
|
/* Requested operation requires auth, is not supported or already in progress */
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
/* On all other errors, try low-level operation. In order to minimize the difference
|
/* On all other errors, try low-level operation. In order to minimize the difference
|
||||||
|
1
test/TEST-69-SHUTDOWN/Makefile
Symbolic link
1
test/TEST-69-SHUTDOWN/Makefile
Symbolic link
@ -0,0 +1 @@
|
|||||||
|
../TEST-01-BASIC/Makefile
|
33
test/TEST-69-SHUTDOWN/test.sh
Executable file
33
test/TEST-69-SHUTDOWN/test.sh
Executable file
@ -0,0 +1,33 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||||
|
set -e
|
||||||
|
|
||||||
|
TEST_DESCRIPTION="shutdown testing"
|
||||||
|
IMAGE_NAME="shutdown"
|
||||||
|
TEST_NO_QEMU=1
|
||||||
|
|
||||||
|
# shellcheck source=test/test-functions
|
||||||
|
. "${TEST_BASE_DIR:?}/test-functions"
|
||||||
|
|
||||||
|
_ORIG_NSPAWN="$SYSTEMD_NSPAWN"
|
||||||
|
SYSTEMD_NSPAWN="$STATEDIR/run-nspawn"
|
||||||
|
|
||||||
|
setup_nspawn_root_hook() {
|
||||||
|
cat > "$STATEDIR"/run-nspawn <<-EOF
|
||||||
|
#!/bin/bash
|
||||||
|
exec "$TEST_BASE_DIR"/test-shutdown.py -- "$_ORIG_NSPAWN" "\$@"
|
||||||
|
exit 1
|
||||||
|
EOF
|
||||||
|
chmod 755 "$STATEDIR"/run-nspawn
|
||||||
|
}
|
||||||
|
|
||||||
|
test_append_files() {
|
||||||
|
# prevent shutdown in test suite, the expect script does that manually.
|
||||||
|
rm "$1"/usr/lib/systemd/tests/testdata/units/end.service
|
||||||
|
inst /usr/bin/screen
|
||||||
|
echo "PS1='screen\$WINDOW # '" > "$1"/etc/bash.bashrc
|
||||||
|
echo 'startup_message off' > "$1"/etc/screenrc
|
||||||
|
echo 'bell_msg ""' >> "$1"/etc/screenrc
|
||||||
|
}
|
||||||
|
|
||||||
|
do_test "$@"
|
@ -1896,6 +1896,8 @@ has_user_dbus_socket() {
|
|||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setup_nspawn_root_hook() { :;}
|
||||||
|
|
||||||
setup_nspawn_root() {
|
setup_nspawn_root() {
|
||||||
if [ -z "${initdir}" ]; then
|
if [ -z "${initdir}" ]; then
|
||||||
dfatal "\$initdir not defined"
|
dfatal "\$initdir not defined"
|
||||||
@ -1908,6 +1910,8 @@ setup_nspawn_root() {
|
|||||||
ddebug "cp -ar $initdir $TESTDIR/unprivileged-nspawn-root"
|
ddebug "cp -ar $initdir $TESTDIR/unprivileged-nspawn-root"
|
||||||
cp -ar "$initdir" "$TESTDIR/unprivileged-nspawn-root"
|
cp -ar "$initdir" "$TESTDIR/unprivileged-nspawn-root"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
setup_nspawn_root_hook
|
||||||
}
|
}
|
||||||
|
|
||||||
setup_basic_dirs() {
|
setup_basic_dirs() {
|
||||||
|
114
test/test-shutdown.py
Executable file
114
test/test-shutdown.py
Executable file
@ -0,0 +1,114 @@
|
|||||||
|
#!/usr/bin/python3
|
||||||
|
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||||
|
#
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import logging
|
||||||
|
import pexpect
|
||||||
|
import sys
|
||||||
|
|
||||||
|
|
||||||
|
def run(args):
|
||||||
|
|
||||||
|
ret = 1
|
||||||
|
logger = logging.getLogger("test-shutdown")
|
||||||
|
|
||||||
|
logger.info("spawning test")
|
||||||
|
console = pexpect.spawn(args.command, args.arg, env={
|
||||||
|
"TERM": "linux",
|
||||||
|
}, encoding='utf-8', timeout=30)
|
||||||
|
|
||||||
|
if args.verbose:
|
||||||
|
console.logfile = sys.stdout
|
||||||
|
|
||||||
|
logger.debug("child pid %d" % console.pid)
|
||||||
|
|
||||||
|
try:
|
||||||
|
logger.info("waiting for login prompt")
|
||||||
|
console.expect('H login: ', 10)
|
||||||
|
|
||||||
|
logger.info("log in and start screen")
|
||||||
|
console.sendline('root')
|
||||||
|
console.expect('bash.*# ', 10)
|
||||||
|
console.sendline('screen')
|
||||||
|
console.expect('screen0 ', 10)
|
||||||
|
console.sendcontrol('a')
|
||||||
|
console.send('c')
|
||||||
|
console.expect('screen1 ', 10)
|
||||||
|
|
||||||
|
# console.interact()
|
||||||
|
|
||||||
|
console.sendline('tty')
|
||||||
|
console.expect(r'/dev/(pts/\d+)')
|
||||||
|
pty = console.match.group(1)
|
||||||
|
logger.info("window 1 at line %s", pty)
|
||||||
|
|
||||||
|
logger.info("schedule reboot")
|
||||||
|
console.sendline('shutdown -r')
|
||||||
|
console.expect("Reboot scheduled for (?P<date>.*), use 'shutdown -c' to cancel", 2)
|
||||||
|
date = console.match.group('date')
|
||||||
|
logger.info("reboot scheduled for %s", date)
|
||||||
|
|
||||||
|
console.sendcontrol('a')
|
||||||
|
console.send('0')
|
||||||
|
logger.info("verify broadcast message")
|
||||||
|
console.expect('Broadcast message from root@H on %s' % pty, 2)
|
||||||
|
console.expect('The system is going down for reboot at %s' % date, 2)
|
||||||
|
|
||||||
|
logger.info("check show output")
|
||||||
|
console.sendline('shutdown --show')
|
||||||
|
console.expect("Reboot scheduled for %s, use 'shutdown -c' to cancel" % date, 2)
|
||||||
|
|
||||||
|
logger.info("cancel shutdown")
|
||||||
|
console.sendline('shutdown -c')
|
||||||
|
console.sendcontrol('a')
|
||||||
|
console.send('1')
|
||||||
|
console.expect('The system shutdown has been cancelled', 2)
|
||||||
|
|
||||||
|
logger.info("call for reboot")
|
||||||
|
console.sendline('sleep 10; shutdown -r now')
|
||||||
|
console.sendcontrol('a')
|
||||||
|
console.send('0')
|
||||||
|
console.expect("The system is going down for reboot NOW!", 12)
|
||||||
|
|
||||||
|
logger.info("waiting for reboot")
|
||||||
|
|
||||||
|
console.expect('H login: ', 10)
|
||||||
|
console.sendline('root')
|
||||||
|
console.expect('bash.*# ', 10)
|
||||||
|
|
||||||
|
console.sendline('> /testok')
|
||||||
|
|
||||||
|
logger.info("power off")
|
||||||
|
console.sendline('poweroff')
|
||||||
|
|
||||||
|
logger.info("expect termination now")
|
||||||
|
console.expect(pexpect.EOF)
|
||||||
|
|
||||||
|
ret = 0
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(e)
|
||||||
|
logger.info("killing child pid %d" % console.pid)
|
||||||
|
console.terminate()
|
||||||
|
|
||||||
|
return ret
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
parser = argparse.ArgumentParser(description='test logind shutdown feature')
|
||||||
|
parser.add_argument("-v", "--verbose", action="store_true", help="verbose")
|
||||||
|
parser.add_argument("command", help="command to run")
|
||||||
|
parser.add_argument("arg", nargs='*', help="args for command")
|
||||||
|
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
if args.verbose:
|
||||||
|
level = logging.DEBUG
|
||||||
|
else:
|
||||||
|
level = logging.INFO
|
||||||
|
|
||||||
|
logging.basicConfig(level=level)
|
||||||
|
|
||||||
|
sys.exit(run(args))
|
||||||
|
|
||||||
|
# vim: sw=4 et
|
7
test/units/testsuite-69.service
Normal file
7
test/units/testsuite-69.service
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||||
|
[Unit]
|
||||||
|
Description=TEST-69-SHUTDOWN
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=oneshot
|
||||||
|
ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
|
Loading…
x
Reference in New Issue
Block a user