logind: implement maintenance time
Update frameworks that work automatically in the background occasionally need to schedule reboots. Systemd-logind already provides a nice mechanism to schedule shutdowns, send notfications and block logins short before the time. Systemd has a framework for calendar events, so we may conveniently use logind to define a maintenance time for reboots. The existing ScheduleShutdown DBus method in logind expects a usec_t with an absolute time. Passing USEC_INFINITY as magic value now tells logind to take the time from the configured maintenance time if set. "shutdown -r" leverages that and uses the maintenance time automatically if configured. The one minute default is still used if nothing was specified. Similarly the new 'auto' setting for the --when parameter of systemctl uses the maintenance time if configured or a one minute timer like the shutdown command.
This commit is contained in:
committed by
Lennart Poettering
parent
e262205eb7
commit
0e10c3d872
@@ -393,6 +393,20 @@
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v252"/></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>DesignatedMaintenanceTime=</varname></term>
|
||||
|
||||
<listitem>
|
||||
<para>
|
||||
Specifies a default calendar event for scheduled shutdowns. So when using e.g. the command
|
||||
<command>shutdown -r</command> to reboot the system without specifying a timeout, logind would
|
||||
use the configured calendar event instead. For details about the syntax of calendar events, see
|
||||
<citerefentry><refentrytitle>systemd.time</refentrytitle><manvolnum>7</manvolnum></citerefentry>.
|
||||
</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v257"/></listitem>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
</refsect1>
|
||||
|
||||
|
||||
@@ -253,9 +253,10 @@ node /org/freedesktop/login1 {
|
||||
readonly b PreparingForShutdown = ...;
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
|
||||
readonly b PreparingForSleep = ...;
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
|
||||
readonly (st) ScheduledShutdown = ...;
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
|
||||
readonly s DesignatedMaintenanceTime = '...';
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
|
||||
readonly b Docked = ...;
|
||||
readonly b LidClosed = ...;
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
|
||||
@@ -295,6 +296,8 @@ node /org/freedesktop/login1 {
|
||||
|
||||
<!--property HandleHibernateKeyLongPress is not documented!-->
|
||||
|
||||
<!--property DesignatedMaintenanceTime is not documented!-->
|
||||
|
||||
<!--property StopIdleSessionUSec is not documented!-->
|
||||
|
||||
<!--Autogenerated cross-references for systemd.directives, do not edit-->
|
||||
@@ -517,6 +520,8 @@ node /org/freedesktop/login1 {
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="ScheduledShutdown"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="DesignatedMaintenanceTime"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="Docked"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="LidClosed"/>
|
||||
@@ -688,7 +693,10 @@ node /org/freedesktop/login1 {
|
||||
<literal>challenge</literal> is returned, the operation is available but only after authorization.</para>
|
||||
|
||||
<para><function>ScheduleShutdown()</function> schedules a shutdown operation <varname>type</varname> at
|
||||
time <varname>usec</varname> in microseconds since the UNIX epoch. <varname>type</varname> can be one
|
||||
time <varname>usec</varname> in microseconds since the UNIX epoch. Alternatively, if
|
||||
<varname>usec</varname> <literal>UINT64_MAX</literal> and a maintenance window is
|
||||
configured, <filename>systemd-logind</filename> will use the next time of the maintenance window
|
||||
instead. <varname>type</varname> can be one
|
||||
of <literal>poweroff</literal>, <literal>dry-poweroff</literal>, <literal>reboot</literal>,
|
||||
<literal>dry-reboot</literal>, <literal>halt</literal>, and <literal>dry-halt</literal>. (The
|
||||
<literal>dry-</literal> variants do not actually execute the shutdown action.)
|
||||
@@ -1579,7 +1587,8 @@ node /org/freedesktop/login1/session/1 {
|
||||
<function>CreateSessionWithPIDFD()</function> were added in version 255.</para>
|
||||
<para><function>Sleep()</function>,
|
||||
<function>CanSleep()</function>,
|
||||
<varname>SleepOperation</varname>, and
|
||||
<varname>SleepOperation</varname>,
|
||||
<varname>DesignatedMaintenanceTime</varname>, and
|
||||
<function>ListSessionsEx()</function> were added in version 256.</para>
|
||||
</refsect2>
|
||||
<refsect2>
|
||||
|
||||
+3
-1
@@ -2862,7 +2862,9 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err
|
||||
which should adhere to the syntax documented in <citerefentry
|
||||
project='man-pages'><refentrytitle>systemd.time</refentrytitle><manvolnum>7</manvolnum></citerefentry>
|
||||
section "PARSING TIMESTAMPS". Specially, if <literal>show</literal> is given, the currently scheduled
|
||||
action will be shown, which can be canceled by passing an empty string or <literal>cancel</literal>.</para>
|
||||
action will be shown, which can be canceled by passing an empty string or <literal>cancel</literal>.
|
||||
<literal>auto</literal> will schedule the action according to maintenance window or one minute in
|
||||
the future.</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v254"/>
|
||||
</listitem>
|
||||
|
||||
@@ -59,6 +59,8 @@ BUS_ERROR_MAP_ELF_REGISTER const sd_bus_error_map bus_common_errors[] = {
|
||||
SD_BUS_ERROR_MAP(BUS_ERROR_SLEEP_VERB_NOT_SUPPORTED, EOPNOTSUPP),
|
||||
SD_BUS_ERROR_MAP(BUS_ERROR_SESSION_BUSY, EBUSY),
|
||||
SD_BUS_ERROR_MAP(BUS_ERROR_NOT_YOUR_DEVICE, EPERM),
|
||||
/* needs to be EOPNOTSUPP for proper handling in callers of logind_schedule_shutdown() */
|
||||
SD_BUS_ERROR_MAP(BUS_ERROR_DESIGNATED_MAINTENANCE_TIME_NOT_SCHEDULED, EOPNOTSUPP),
|
||||
|
||||
SD_BUS_ERROR_MAP(BUS_ERROR_AUTOMATIC_TIME_SYNC_ENABLED, EALREADY),
|
||||
SD_BUS_ERROR_MAP(BUS_ERROR_NO_NTP_SUPPORT, EOPNOTSUPP),
|
||||
|
||||
@@ -59,6 +59,8 @@
|
||||
#define BUS_ERROR_SLEEP_VERB_NOT_SUPPORTED "org.freedesktop.login1.SleepVerbNotSupported"
|
||||
#define BUS_ERROR_SESSION_BUSY "org.freedesktop.login1.SessionBusy"
|
||||
#define BUS_ERROR_NOT_YOUR_DEVICE "org.freedesktop.login1.NotYourDevice"
|
||||
#define BUS_ERROR_DESIGNATED_MAINTENANCE_TIME_NOT_SCHEDULED \
|
||||
"org.freedesktop.login1.DesignatedMaintenanceTimeNotScheduled"
|
||||
|
||||
#define BUS_ERROR_AUTOMATIC_TIME_SYNC_ENABLED "org.freedesktop.timedate1.AutomaticTimeSyncEnabled"
|
||||
#define BUS_ERROR_NO_NTP_SUPPORT "org.freedesktop.timedate1.NoNTPSupport"
|
||||
|
||||
@@ -77,6 +77,8 @@ void manager_reset_config(Manager *m) {
|
||||
m->kill_exclude_users = strv_free(m->kill_exclude_users);
|
||||
|
||||
m->stop_idle_session_usec = USEC_INFINITY;
|
||||
|
||||
m->maintenance_time = calendar_spec_free(m->maintenance_time);
|
||||
}
|
||||
|
||||
int manager_parse_config_file(Manager *m) {
|
||||
|
||||
+50
-1
@@ -392,6 +392,31 @@ static int property_get_scheduled_shutdown(
|
||||
return sd_bus_message_close_container(reply);
|
||||
}
|
||||
|
||||
static int property_get_maintenance_time(
|
||||
sd_bus *bus,
|
||||
const char *path,
|
||||
const char *interface,
|
||||
const char *property,
|
||||
sd_bus_message *reply,
|
||||
void *userdata,
|
||||
sd_bus_error *error) {
|
||||
|
||||
Manager *m = ASSERT_PTR(userdata);
|
||||
_cleanup_free_ char *s = NULL;
|
||||
int r;
|
||||
|
||||
assert(bus);
|
||||
assert(reply);
|
||||
|
||||
if (m->maintenance_time) {
|
||||
r = calendar_spec_to_string(m->maintenance_time, &s);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to format calendar specification: %m");
|
||||
}
|
||||
|
||||
return sd_bus_message_append(reply, "s", s);
|
||||
}
|
||||
|
||||
static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_handle_action, handle_action, HandleAction);
|
||||
static BUS_DEFINE_PROPERTY_GET(property_get_docked, "b", Manager, manager_is_docked_or_external_displays);
|
||||
static BUS_DEFINE_PROPERTY_GET(property_get_lid_closed, "b", Manager, manager_is_lid_closed);
|
||||
@@ -2331,6 +2356,8 @@ static void reset_scheduled_shutdown(Manager *m) {
|
||||
}
|
||||
|
||||
(void) unlink(SHUTDOWN_SCHEDULE_FILE);
|
||||
|
||||
manager_send_changed(m, "ScheduledShutdown", NULL);
|
||||
}
|
||||
|
||||
static int update_schedule_file(Manager *m) {
|
||||
@@ -2565,6 +2592,25 @@ static int method_schedule_shutdown(sd_bus_message *message, void *userdata, sd_
|
||||
if (r != 0)
|
||||
return r;
|
||||
|
||||
if (elapse == USEC_INFINITY) {
|
||||
if (m->maintenance_time) {
|
||||
r = calendar_spec_next_usec(m->maintenance_time, now(CLOCK_REALTIME), &elapse);
|
||||
if (r < 0) {
|
||||
if (r == -ENOENT)
|
||||
return sd_bus_error_set(error,
|
||||
BUS_ERROR_DESIGNATED_MAINTENANCE_TIME_NOT_SCHEDULED,
|
||||
"No upcoming maintenance window scheduled");
|
||||
return sd_bus_error_setf(error,
|
||||
BUS_ERROR_DESIGNATED_MAINTENANCE_TIME_NOT_SCHEDULED,
|
||||
"Failed to determine next maintenace window");
|
||||
}
|
||||
|
||||
log_info("Scheduled %s at maintenance window %s", type, FORMAT_TIMESTAMP(elapse));
|
||||
} else
|
||||
/* the good old shutdown command uses one minute by default */
|
||||
elapse = usec_add(now(CLOCK_REALTIME), USEC_PER_MINUTE);
|
||||
}
|
||||
|
||||
m->scheduled_shutdown_action = handle;
|
||||
m->shutdown_dry_run = dry_run;
|
||||
m->scheduled_shutdown_timeout = elapse;
|
||||
@@ -2582,6 +2628,8 @@ static int method_schedule_shutdown(sd_bus_message *message, void *userdata, sd_
|
||||
return r;
|
||||
}
|
||||
|
||||
manager_send_changed(m, "ScheduledShutdown", NULL);
|
||||
|
||||
return sd_bus_reply_method_return(message, NULL);
|
||||
}
|
||||
|
||||
@@ -3656,7 +3704,8 @@ static const sd_bus_vtable manager_vtable[] = {
|
||||
SD_BUS_PROPERTY("IdleActionUSec", "t", NULL, offsetof(Manager, idle_action_usec), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("PreparingForShutdown", "b", property_get_preparing, 0, 0),
|
||||
SD_BUS_PROPERTY("PreparingForSleep", "b", property_get_preparing, 0, 0),
|
||||
SD_BUS_PROPERTY("ScheduledShutdown", "(st)", property_get_scheduled_shutdown, 0, 0),
|
||||
SD_BUS_PROPERTY("ScheduledShutdown", "(st)", property_get_scheduled_shutdown, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
|
||||
SD_BUS_PROPERTY("DesignatedMaintenanceTime", "s", property_get_maintenance_time, 0, 0),
|
||||
SD_BUS_PROPERTY("Docked", "b", property_get_docked, 0, 0),
|
||||
SD_BUS_PROPERTY("LidClosed", "b", property_get_lid_closed, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
|
||||
SD_BUS_PROPERTY("OnExternalPower", "b", property_get_on_external_power, 0, 0),
|
||||
|
||||
@@ -50,5 +50,6 @@ Login.RuntimeDirectoryInodesMax, config_parse_iec_uint64, 0, offse
|
||||
Login.RemoveIPC, config_parse_bool, 0, offsetof(Manager, remove_ipc)
|
||||
Login.InhibitorsMax, config_parse_uint64, 0, offsetof(Manager, inhibitors_max)
|
||||
Login.SessionsMax, config_parse_uint64, 0, offsetof(Manager, sessions_max)
|
||||
Login.DesignatedMaintenanceTime, config_parse_calendar, 0, offsetof(Manager, maintenance_time)
|
||||
Login.UserTasksMax, config_parse_compat_user_tasks_max, 0, 0
|
||||
Login.StopIdleSessionSec, config_parse_sec_fix_0, 0, offsetof(Manager, stop_idle_session_usec)
|
||||
|
||||
@@ -50,3 +50,4 @@
|
||||
#InhibitorsMax=8192
|
||||
#SessionsMax=8192
|
||||
#StopIdleSessionSec=infinity
|
||||
#DesignatedMaintenanceTime=
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include "sd-device.h"
|
||||
#include "sd-event.h"
|
||||
|
||||
#include "calendarspec.h"
|
||||
#include "conf-parser.h"
|
||||
#include "hashmap.h"
|
||||
#include "list.h"
|
||||
@@ -141,6 +142,8 @@ struct Manager {
|
||||
|
||||
char *efi_loader_entry_one_shot;
|
||||
struct stat efi_loader_entry_one_shot_stat;
|
||||
|
||||
CalendarSpec *maintenance_time;
|
||||
};
|
||||
|
||||
void manager_reset_config(Manager *m);
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "chase.h"
|
||||
#include "calendarspec.h"
|
||||
#include "conf-files.h"
|
||||
#include "conf-parser.h"
|
||||
#include "constants.h"
|
||||
@@ -1980,6 +1981,41 @@ int config_parse_unsigned_bounded(
|
||||
return r;
|
||||
}
|
||||
|
||||
int config_parse_calendar(
|
||||
const char *unit,
|
||||
const char *filename,
|
||||
unsigned line,
|
||||
const char *section,
|
||||
unsigned section_line,
|
||||
const char *lvalue,
|
||||
int ltype,
|
||||
const char *rvalue,
|
||||
void *data,
|
||||
void *userdata) {
|
||||
|
||||
CalendarSpec **cr = data;
|
||||
_cleanup_(calendar_spec_freep) CalendarSpec *c = NULL;
|
||||
int r;
|
||||
|
||||
assert(filename);
|
||||
assert(lvalue);
|
||||
assert(rvalue);
|
||||
assert(data);
|
||||
|
||||
if (isempty(rvalue)) {
|
||||
*cr = calendar_spec_free(*cr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
r = calendar_spec_from_string(rvalue, &c);
|
||||
if (r < 0)
|
||||
log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse calendar specification, ignoring: %s", rvalue);
|
||||
else
|
||||
*cr = TAKE_PTR(c);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEFINE_CONFIG_PARSE(config_parse_percent, parse_percent, "Failed to parse percent value");
|
||||
DEFINE_CONFIG_PARSE(config_parse_permyriad, parse_permyriad, "Failed to parse permyriad value");
|
||||
DEFINE_CONFIG_PARSE_PTR(config_parse_sec_fix_0, parse_sec_fix_0, usec_t, "Failed to parse time value");
|
||||
|
||||
@@ -251,6 +251,7 @@ CONFIG_PARSER_PROTOTYPE(config_parse_permyriad);
|
||||
CONFIG_PARSER_PROTOTYPE(config_parse_pid);
|
||||
CONFIG_PARSER_PROTOTYPE(config_parse_sec_fix_0);
|
||||
CONFIG_PARSER_PROTOTYPE(config_parse_timezone);
|
||||
CONFIG_PARSER_PROTOTYPE(config_parse_calendar);
|
||||
|
||||
typedef enum Disabled {
|
||||
DISABLED_CONFIGURATION,
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "pretty-print.h"
|
||||
#include "reboot-util.h"
|
||||
#include "systemctl-compat-shutdown.h"
|
||||
#include "systemctl-logind.h"
|
||||
#include "systemctl-sysv-compat.h"
|
||||
#include "systemctl.h"
|
||||
#include "terminal-util.h"
|
||||
@@ -137,7 +138,7 @@ int shutdown_parse_argv(int argc, char *argv[]) {
|
||||
return r;
|
||||
}
|
||||
} else
|
||||
arg_when = now(CLOCK_REALTIME) + USEC_PER_MINUTE;
|
||||
arg_when = USEC_INFINITY; /* logind chooses on server side */
|
||||
|
||||
if (argc > optind && arg_action == ACTION_CANCEL_SHUTDOWN)
|
||||
/* No time argument for shutdown cancel */
|
||||
|
||||
@@ -408,7 +408,7 @@ int logind_show_shutdown(void) {
|
||||
else /* If we don't recognize the action string, we'll show it as-is */
|
||||
pretty_action = action;
|
||||
|
||||
if (arg_action == ACTION_SYSTEMCTL)
|
||||
if (IN_SET(arg_action, ACTION_SYSTEMCTL, ACTION_SYSTEMCTL_SHOW_SHUTDOWN))
|
||||
log_info("%s scheduled for %s, use 'systemctl %s --when=cancel' to cancel.",
|
||||
pretty_action,
|
||||
FORMAT_TIMESTAMP_STYLE(elapse, arg_timestamp_style),
|
||||
|
||||
@@ -203,10 +203,8 @@ int verb_start_special(int argc, char *argv[], void *userdata) {
|
||||
case ACTION_SOFT_REBOOT:
|
||||
if (arg_when == 0)
|
||||
r = logind_reboot(a);
|
||||
else if (arg_when != USEC_INFINITY)
|
||||
else
|
||||
r = logind_schedule_shutdown(a);
|
||||
else /* arg_when == USEC_INFINITY */
|
||||
r = logind_cancel_shutdown();
|
||||
if (r >= 0 || IN_SET(r, -EACCES, -EOPNOTSUPP, -EINPROGRESS))
|
||||
/* The latter indicates that the requested operation requires auth,
|
||||
* is not supported or already in progress, in which cases we ignore the error. */
|
||||
|
||||
@@ -1023,15 +1023,17 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
|
||||
|
||||
case ARG_WHEN:
|
||||
if (streq(optarg, "show")) {
|
||||
r = logind_show_shutdown();
|
||||
if (r < 0 && r != -ENODATA)
|
||||
return r;
|
||||
|
||||
return 0;
|
||||
arg_action = ACTION_SYSTEMCTL_SHOW_SHUTDOWN;
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (STR_IN_SET(optarg, "", "cancel")) {
|
||||
arg_when = USEC_INFINITY;
|
||||
arg_action = ACTION_CANCEL_SHUTDOWN;
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (streq(optarg, "auto")) {
|
||||
arg_when = USEC_INFINITY; /* logind chooses on server side */
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1339,6 +1341,7 @@ static int run(int argc, char *argv[]) {
|
||||
break;
|
||||
|
||||
case ACTION_SHOW_SHUTDOWN:
|
||||
case ACTION_SYSTEMCTL_SHOW_SHUTDOWN:
|
||||
r = logind_show_shutdown();
|
||||
break;
|
||||
|
||||
|
||||
@@ -35,6 +35,7 @@ enum action {
|
||||
ACTION_RUNLEVEL,
|
||||
ACTION_CANCEL_SHUTDOWN,
|
||||
ACTION_SHOW_SHUTDOWN,
|
||||
ACTION_SYSTEMCTL_SHOW_SHUTDOWN,
|
||||
_ACTION_MAX,
|
||||
_ACTION_INVALID = -EINVAL,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user