mirror of
https://github.com/systemd/systemd.git
synced 2025-02-23 13:57:33 +03:00
Merge pull request #24242 from msekletar/terminate-idle-sessions
Add option to stop idle sessions after specified timeout
This commit is contained in:
commit
5b9ae04c65
@ -344,6 +344,17 @@
|
||||
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>
|
||||
|
||||
|
@ -245,6 +245,8 @@ node /org/freedesktop/login1 {
|
||||
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 { ... };
|
||||
@ -262,6 +264,8 @@ node /org/freedesktop/login1 {
|
||||
|
||||
<!--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"/>
|
||||
@ -490,6 +494,8 @@ node /org/freedesktop/login1 {
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="NCurrentSessions"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="StopIdleSessionUSec"/>
|
||||
|
||||
<!--End of Autogenerated section-->
|
||||
|
||||
<refsect2>
|
||||
|
@ -2544,37 +2544,6 @@ int config_parse_service_timeout_abort(
|
||||
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,
|
||||
|
@ -115,7 +115,6 @@ CONFIG_PARSER_PROTOTYPE(config_parse_bus_name);
|
||||
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);
|
||||
|
@ -71,6 +71,8 @@ void manager_reset_config(Manager *m) {
|
||||
|
||||
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) {
|
||||
|
@ -3402,6 +3402,7 @@ static const sd_bus_vtable manager_vtable[] = {
|
||||
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),
|
||||
|
@ -50,3 +50,4 @@ Login.RemoveIPC, config_parse_bool, 0, offse
|
||||
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)
|
||||
|
@ -152,6 +152,8 @@ Session* session_free(Session *s) {
|
||||
free(s->state_file);
|
||||
free(s->fifo_path);
|
||||
|
||||
sd_event_source_unref(s->stop_on_idle_event_source);
|
||||
|
||||
return mfree(s);
|
||||
}
|
||||
|
||||
@ -697,6 +699,55 @@ static int session_start_scope(Session *s, sd_bus_message *properties, sd_bus_er
|
||||
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;
|
||||
|
||||
@ -719,6 +770,10 @@ int session_start(Session *s, sd_bus_message *properties, sd_bus_error *error) {
|
||||
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,
|
||||
@ -959,7 +1014,7 @@ static int get_process_ctty_atime(pid_t pid, usec_t *atime) {
|
||||
}
|
||||
|
||||
int session_get_idle_hint(Session *s, dual_timestamp *t) {
|
||||
usec_t atime = 0;
|
||||
usec_t atime = 0, dtime = 0;
|
||||
int r;
|
||||
|
||||
assert(s);
|
||||
@ -996,10 +1051,16 @@ found_atime:
|
||||
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) {
|
||||
|
@ -115,6 +115,8 @@ struct Session {
|
||||
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);
|
||||
|
||||
|
@ -46,3 +46,4 @@
|
||||
#RemoveIPC=yes
|
||||
#InhibitorsMax=8192
|
||||
#SessionsMax=8192
|
||||
#StopIdleSessionSec=infinity
|
||||
|
@ -96,6 +96,8 @@ struct Manager {
|
||||
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;
|
||||
|
@ -1885,3 +1885,4 @@ int config_parse_in_addr_non_null(
|
||||
|
||||
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");
|
||||
|
@ -209,6 +209,7 @@ CONFIG_PARSER_PROTOTYPE(config_parse_in_addr_non_null);
|
||||
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,
|
||||
|
@ -493,6 +493,42 @@ test_list_users() {
|
||||
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
|
||||
@ -505,6 +541,7 @@ test_session
|
||||
test_lock_idle_action
|
||||
test_session_properties
|
||||
test_list_users
|
||||
test_stop_idle_session
|
||||
|
||||
touch /testok
|
||||
rm /failed
|
||||
|
Loading…
x
Reference in New Issue
Block a user