Skip to content

Commit c4fbeec

Browse files
authored
Idle logout rework (#28)
Rework of the idling system. Instead of assuming the screen was locked right as the greeter was started and using xprintidle to get idletime, get the screen lock time from a file in /tmp
1 parent aedda01 commit c4fbeec

File tree

2 files changed

+66
-7
lines changed

2 files changed

+66
-7
lines changed

client/uis/screens/lockscreen.ts

Lines changed: 46 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
import { Authenticator, AuthenticatorEvents } from "../../auth";
2-
import { LightDMUser } from "nody-greeter-types";
2+
import { LightDMUser, ThemeUtils } from "nody-greeter-types";
33
import { UIScreen, UILockScreenElements } from "../screen";
44
import { UI } from "../../ui";
55

6+
const PATH_LOCK_TIMESTAMP_PREFIX = '/tmp/codam_web_greeter_lock_timestamp';
7+
68
export class LockScreenUI extends UIScreen {
79
public readonly _form: UILockScreenElements;
810
private readonly _activeSession: LightDMUser;
911
private _isExamMode: boolean = false;
10-
private _lockedTime: Date = new Date();
12+
private _lockedTime: Date | null = null;
1113
protected _events: AuthenticatorEvents = {
1214
authenticationStart: () => {
1315
this._disableForm();
@@ -43,6 +45,10 @@ export class LockScreenUI extends UIScreen {
4345
} as UILockScreenElements;
4446

4547
this._initForm();
48+
49+
// Check when the screen was locked every minute (delete the lock_timestamp file in /tmp to prevent the automated logout)
50+
setInterval(this._getAndSetLockedTimestamp.bind(this), 60000);
51+
this._getAndSetLockedTimestamp();
4652
}
4753

4854
protected _initForm(): void {
@@ -122,16 +128,53 @@ export class LockScreenUI extends UIScreen {
122128
return (this._form as UILockScreenElements).passwordInput;
123129
}
124130

131+
public get lockedTime(): Date | null {
132+
return this._lockedTime;
133+
}
134+
135+
private _getScreenLockedTimestamp(login: string): Promise<Date> {
136+
return new Promise((resolve, reject) => {
137+
fetch(`${PATH_LOCK_TIMESTAMP_PREFIX}_${login}`)
138+
.then(response => response.text())
139+
.then(text => {
140+
// Get the first word from the text file
141+
const timestamp = text.split(' ')[0];
142+
resolve(new Date(parseInt(timestamp) * 1000));
143+
})
144+
.catch(() => {
145+
reject();
146+
});
147+
});
148+
}
149+
150+
private _getAndSetLockedTimestamp(): void {
151+
this._getScreenLockedTimestamp(this._activeSession.username)
152+
.then((timestamp: Date) => {
153+
this._lockedTime = timestamp;
154+
this._lockedTimer(); // run once immediately, after this the interval will take care of updating the timer
155+
})
156+
.catch(() => {
157+
// Unable to get the screen locked timestamp, prevent automated logout by setting the locked time to null
158+
this._lockedTime = null;
159+
});
160+
}
161+
125162
private _lockedTimer(): void {
163+
if (!this._lockedTime) {
164+
// Unsure when the screen was locked, no automated logout possible
165+
return;
166+
}
167+
126168
const logoutAfter = 42; // minutes
127169
const lockedMinutesAgo = (Date.now() - this._lockedTime.getTime()) / 1000 / 60;
128170
const timeRemaining = logoutAfter - lockedMinutesAgo;
129171
if (timeRemaining <= 0.25) {
130172
this._disableForm();
131173
this._form.lockedTimeAgo.innerText = "Automated logout in progress...";
132-
if (timeRemaining < -5) {
174+
if (timeRemaining < -5) { // Give it a 5 minute grace period
133175
// Add debug text indicating the systemd service might have failed or was not installed
134176
window.ui.setDebugInfo("Automated logout appears to take a while. Is the systemd idling service from codam-web-greeter installed and enabled?");
177+
this._enableForm(); // Allow the user to just unlock the screen again
135178
}
136179
}
137180
else {

systemd/system/codam-web-greeter-idler.sh

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,29 @@ while IFS= read -r line; do
1717
if ! [[ "$DISPLAY" =~ ^: ]]; then
1818
continue
1919
fi
20+
2021
# Get idle time from X-session using sudo
22+
# This time is used to determine if the session has been idle for too long (possibly without locking the screen)
2123
IDLE_TIME=$(/usr/bin/sudo -u "$USERNAME" DISPLAY="$DISPLAY" /usr/bin/xprintidle)
22-
# Check if session has been idle for over 42 minutes
23-
if [ "$IDLE_TIME" -gt 2520000 ]; then
24-
/usr/bin/echo "Session for user $USERNAME has been idle for over 42 minutes (idletime $IDLE_TIME ms), forcing logout now by restarting lightdm"
24+
25+
# Check if lock_timestamp file exists, and if so read the locked_at_timestamp
26+
# This time is used to determine if the screen lock has been active for too long
27+
# Sometimes xprintidle doesn't work properly when the screen is locked due to programs running in the user session in the background
28+
TIME_SINCE_LOCK=$((0)) # Placeholder
29+
if [ -f "/tmp/codam_web_greeter_lock_timestamp_$USERNAME" ]; then
30+
# Get the locked_at_timestamp from the file
31+
LOCKED_AT_TIMESTAMP=$(/usr/bin/awk '{print $1}' "/tmp/codam_web_greeter_lock_timestamp_$USERNAME")
32+
# Calculate the time since the session was locked
33+
TIME_SINCE_LOCK=$((($(date +%s) - LOCKED_AT_TIMESTAMP) * 1000))
34+
fi
35+
36+
# Check if session has been idle for long enough
37+
MAX_IDLE_TIME_MINUTES=$((42))
38+
MAX_IDLE_TIME=$((MAX_IDLE_TIME_MINUTES * 60 * 1000))
39+
if [ "$IDLE_TIME" -gt "$MAX_IDLE_TIME" ] || [ "$TIME_SINCE_LOCK" -gt "$MAX_IDLE_TIME" ]; then
40+
/usr/bin/echo "Session for user $USERNAME has been idle for over 42 minutes (idletime $IDLE_TIME ms, time_since_lock $TIME_SINCE_LOCK ms), forcing logout now by restarting lightdm"
2541
/usr/bin/systemctl restart lightdm
2642
else
27-
/usr/bin/echo "Session for $USERNAME has been idle for $((IDLE_TIME / 1000)) seconds"
43+
/usr/bin/echo "Session for $USERNAME has been idle for $((IDLE_TIME / 1000)) seconds, screen locked for $((TIME_SINCE_LOCK / 1000)) seconds"
2844
fi
2945
done <<< "$WHO_OUTPUT"

0 commit comments

Comments
 (0)