<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>
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")
<!--property HandleHibernateKeyLongPress is not documented!-->
+ <!--property DesignatedMaintenanceTime is not documented!-->
+
<!--property StopIdleSessionUSec is not documented!-->
<!--Autogenerated cross-references for systemd.directives, do not edit-->
<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"/>
<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.)
<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>
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>
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),
#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"
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) {
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);
}
(void) unlink(SHUTDOWN_SCHEDULE_FILE);
+
+ manager_send_changed(m, "ScheduledShutdown", NULL);
}
static int update_schedule_file(Manager *m) {
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;
return r;
}
+ manager_send_changed(m, "ScheduledShutdown", NULL);
+
return sd_bus_reply_method_return(message, NULL);
}
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),
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)
#InhibitorsMax=8192
#SessionsMax=8192
#StopIdleSessionSec=infinity
+#DesignatedMaintenanceTime=
#include "sd-device.h"
#include "sd-event.h"
+#include "calendarspec.h"
#include "conf-parser.h"
#include "hashmap.h"
#include "list.h"
char *efi_loader_entry_one_shot;
struct stat efi_loader_entry_one_shot_stat;
+
+ CalendarSpec *maintenance_time;
};
void manager_reset_config(Manager *m);
#include "alloc-util.h"
#include "chase.h"
+#include "calendarspec.h"
#include "conf-files.h"
#include "conf-parser.h"
#include "constants.h"
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");
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,
#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"
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 */
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),
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. */
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;
}
break;
case ACTION_SHOW_SHUTDOWN:
+ case ACTION_SYSTEMCTL_SHOW_SHUTDOWN:
r = logind_show_shutdown();
break;
ACTION_RUNLEVEL,
ACTION_CANCEL_SHUTDOWN,
ACTION_SHOW_SHUTDOWN,
+ ACTION_SYSTEMCTL_SHOW_SHUTDOWN,
_ACTION_MAX,
_ACTION_INVALID = -EINVAL,
};