are excluded from the effect of this setting. Defaults to <literal>yes</literal>.</para></listitem>
</varlistentry>
+ <varlistentry>
+ <term><varname>StopIdleSessionSec=</varname></term>
+
+ <listitem><para>Specifies a timeout in seconds, or a time span value after which
+ <filename>systemd-logind</filename> checks the idle state of all sessions. Every session that is idle for
+ longer then the timeout will be stopped. Defaults to <literal>infinity</literal>
+ (<filename>systemd-logind</filename> is not checking the idle state of sessions). For details about the syntax
+ of time spans, see
+ <citerefentry><refentrytitle>systemd.time</refentrytitle><manvolnum>7</manvolnum></citerefentry>.
+ </para></listitem>
+ </varlistentry>
</variablelist>
</refsect1>
readonly t SessionsMax = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly t NCurrentSessions = ...;
+ @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+ readonly t StopIdleSessionUSec = ...;
};
interface org.freedesktop.DBus.Peer { ... };
interface org.freedesktop.DBus.Introspectable { ... };
<!--property HandleHibernateKeyLongPress is not documented!-->
+ <!--property StopIdleSessionUSec is not documented!-->
+
<!--Autogenerated cross-references for systemd.directives, do not edit-->
<variablelist class="dbus-interface" generated="True" extra-ref="org.freedesktop.login1.Manager"/>
<variablelist class="dbus-property" generated="True" extra-ref="NCurrentSessions"/>
+ <variablelist class="dbus-property" generated="True" extra-ref="StopIdleSessionUSec"/>
+
<!--End of Autogenerated section-->
<refsect2>
return 0;
}
-int config_parse_sec_fix_0(
- 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) {
-
- usec_t *usec = data;
- int r;
-
- assert(filename);
- assert(lvalue);
- assert(rvalue);
- assert(usec);
-
- /* This is pretty much like config_parse_sec(), except that this treats a time of 0 as infinity, for
- * compatibility with older versions of systemd where 0 instead of infinity was used as indicator to turn off a
- * timeout. */
-
- r = parse_sec_fix_0(rvalue, usec);
- if (r < 0)
- log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse %s= parameter, ignoring: %s", lvalue, rvalue);
-
- return 0;
-}
-
int config_parse_user_group_compat(
const char *unit,
const char *filename,
CONFIG_PARSER_PROTOTYPE(config_parse_exec_utmp_mode);
CONFIG_PARSER_PROTOTYPE(config_parse_working_directory);
CONFIG_PARSER_PROTOTYPE(config_parse_fdname);
-CONFIG_PARSER_PROTOTYPE(config_parse_sec_fix_0);
CONFIG_PARSER_PROTOTYPE(config_parse_user_group_compat);
CONFIG_PARSER_PROTOTYPE(config_parse_user_group_strv_compat);
CONFIG_PARSER_PROTOTYPE(config_parse_restrict_namespaces);
m->kill_only_users = strv_free(m->kill_only_users);
m->kill_exclude_users = strv_free(m->kill_exclude_users);
+
+ m->stop_idle_session_usec = USEC_INFINITY;
}
int manager_parse_config_file(Manager *m) {
SD_BUS_PROPERTY("SessionsMax", "t", NULL, offsetof(Manager, sessions_max), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("NCurrentSessions", "t", property_get_hashmap_size, offsetof(Manager, sessions), 0),
SD_BUS_PROPERTY("UserTasksMax", "t", property_get_compat_user_tasks_max, 0, SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN),
+ SD_BUS_PROPERTY("StopIdleSessionUSec", "t", NULL, offsetof(Manager, stop_idle_session_usec), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_METHOD_WITH_ARGS("GetSession",
SD_BUS_ARGS("s", session_id),
Login.InhibitorsMax, config_parse_uint64, 0, offsetof(Manager, inhibitors_max)
Login.SessionsMax, config_parse_uint64, 0, offsetof(Manager, sessions_max)
Login.UserTasksMax, config_parse_compat_user_tasks_max, 0, 0
+Login.StopIdleSessionSec, config_parse_sec_fix_0, 0, offsetof(Manager, stop_idle_session_usec)
free(s->state_file);
free(s->fifo_path);
+ sd_event_source_unref(s->stop_on_idle_event_source);
+
return mfree(s);
}
return 0;
}
+static int session_dispatch_stop_on_idle(sd_event_source *source, uint64_t t, void *userdata) {
+ Session *s = userdata;
+ dual_timestamp ts;
+ int r, idle;
+
+ assert(s);
+
+ if (s->stopping)
+ return 0;
+
+ idle = session_get_idle_hint(s, &ts);
+ if (idle) {
+ log_debug("Session \"%s\" of user \"%s\" is idle, stopping.", s->id, s->user->user_record->user_name);
+
+ return session_stop(s, /* force */ true);
+ }
+
+ r = sd_event_source_set_time(source, usec_add(ts.monotonic, s->manager->stop_idle_session_usec));
+ if (r < 0)
+ return log_error_errno(r, "Failed to configure stop on idle session event source: %m");
+
+ r = sd_event_source_set_enabled(source, SD_EVENT_ONESHOT);
+ if (r < 0)
+ return log_error_errno(r, "Failed to enable stop on idle session event source: %m");
+
+ return 1;
+}
+
+static int session_setup_stop_on_idle_timer(Session *s) {
+ int r;
+
+ assert(s);
+
+ if (s->manager->stop_idle_session_usec == USEC_INFINITY)
+ return 0;
+
+ r = sd_event_add_time_relative(
+ s->manager->event,
+ &s->stop_on_idle_event_source,
+ CLOCK_MONOTONIC,
+ s->manager->stop_idle_session_usec,
+ 0,
+ session_dispatch_stop_on_idle, s);
+ if (r < 0)
+ return log_error_errno(r, "Failed to add stop on idle session event source: %m");
+
+ return 0;
+}
+
int session_start(Session *s, sd_bus_message *properties, sd_bus_error *error) {
int r;
if (r < 0)
return r;
+ r = session_setup_stop_on_idle_timer(s);
+ if (r < 0)
+ return r;
+
log_struct(s->class == SESSION_BACKGROUND ? LOG_DEBUG : LOG_INFO,
"MESSAGE_ID=" SD_MESSAGE_SESSION_START_STR,
"SESSION_ID=%s", s->id,
}
int session_get_idle_hint(Session *s, dual_timestamp *t) {
- usec_t atime = 0;
+ usec_t atime = 0, dtime = 0;
int r;
assert(s);
if (t)
dual_timestamp_from_realtime(t, atime);
- if (s->manager->idle_action_usec <= 0)
+ if (s->manager->idle_action_usec > 0 && s->manager->stop_idle_session_usec != USEC_INFINITY)
+ dtime = MIN(s->manager->idle_action_usec, s->manager->stop_idle_session_usec);
+ else if (s->manager->idle_action_usec > 0)
+ dtime = s->manager->idle_action_usec;
+ else if (s->manager->stop_idle_session_usec != USEC_INFINITY)
+ dtime = s->manager->stop_idle_session_usec;
+ else
return false;
- return usec_add(atime, s->manager->idle_action_usec) <= now(CLOCK_REALTIME);
+ return usec_add(atime, dtime) <= now(CLOCK_REALTIME);
}
int session_set_idle_hint(Session *s, bool b) {
Hashmap *devices;
sd_bus_track *track;
+ sd_event_source *stop_on_idle_event_source;
+
LIST_FIELDS(Session, sessions_by_user);
LIST_FIELDS(Session, sessions_by_seat);
#RemoveIPC=yes
#InhibitorsMax=8192
#SessionsMax=8192
+#StopIdleSessionSec=infinity
HandleAction idle_action;
bool was_idle;
+ usec_t stop_idle_session_usec;
+
HandleAction handle_power_key;
HandleAction handle_power_key_long_press;
HandleAction handle_reboot_key;
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_percent);
CONFIG_PARSER_PROTOTYPE(config_parse_permyriad);
CONFIG_PARSER_PROTOTYPE(config_parse_pid);
+CONFIG_PARSER_PROTOTYPE(config_parse_sec_fix_0);
typedef enum Disabled {
DISABLED_CONFIGURATION,
assert_eq "$(loginctl list-users --no-legend | awk '$2 == "logind-test-user" { print $3 }')" yes
}
+
+teardown_stop_idle_session() (
+ set +eux
+
+ rm -f /run/systemd/logind.conf.d/stop-idle-session.conf
+ systemctl restart systemd-logind.service
+
+ cleanup_session
+)
+
+test_stop_idle_session() {
+ local id ts
+
+ if [[ ! -c /dev/tty2 ]]; then
+ echo "/dev/tty2 does not exist, skipping test ${FUNCNAME[0]}."
+ return
+ fi
+
+ create_session
+ trap teardown_stop_idle_session RETURN
+
+ id="$(loginctl --no-legend | awk '$3 == "logind-test-user" { print $1; }')"
+ ts="$(date '+%H:%M:%S')"
+
+ mkdir -p /run/systemd/logind.conf.d
+ cat >/run/systemd/logind.conf.d/stop-idle-session.conf <<EOF
+[Login]
+StopIdleSessionSec=2s
+EOF
+ systemctl restart systemd-logind.service
+ sleep 5
+
+ assert_eq "$(journalctl -b -u systemd-logind.service --since="$ts" --grep "Session \"$id\" of user \"logind-test-user\" is idle, stopping." | wc -l)" 1
+ assert_eq "$(loginctl --no-legend | grep -c "logind-test-user")" 0
+}
+
: >/failed
setup_test_user
test_lock_idle_action
test_session_properties
test_list_users
+test_stop_idle_session
touch /testok
rm /failed