screentimectl

Schermtijdcontrole voor lokale accounts op Fedora/KDE Plasma (Wayland), vergelijkbaar met Google Family Link. Eén daemon kan meerdere accounts tegelijk bewaken (bv. meerdere kinderen op dezelfde machine, elk met hun eigen budget). Bestaat uit:

  • screentimectld — root-daemon die de schermtijd van elke geconfigureerde gebruiker ([[users]] in config.toml) bijhoudt, dagelijks reset, een waarschuwingspopup toont en bij het verstrijken van de tijd het scherm vergrendelt en het account vergrendelt (usermod -L).
  • screentimectl — CLI om via sudo voor een specifieke gebruiker (--user <naam>) tijd toe te voegen of af te trekken, het account direct te (ont)grendelen, of de status op te vragen.

Taal van popups, statuspagina's, CLI-uitvoer en de plasmoid-widget: standaard Engels, instelbaar op Nederlands via language = "nl" in config.toml (zie packaging/config.toml.example). Logregels (journalctl) en de installatiescripts blijven altijd in het Engels/Nederlands zoals ze in de broncode staan — dat is voor de beheerder, niet voor het gemonitorde account.

Werking

screentimectld telt elke seconde (app.rs::tick), voor elke gebruiker in [[users]] afzonderlijk, de resterende tijd terug — maar uitsluitend terwijl die gebruiker een actieve grafische sessie heeft: een sessie van Class=user en Type!=tty waarvan de Active-property van logind op true staat. Active betekent hier letterlijk "heeft op dit moment focus op het scherm/de VT" — bij meerdere gebruikers op één fysieke seat (bv. via VT-wisselen met Ctrl+Alt+Fx) loopt de teller van een gebruiker dus alleen terwijl die gebruiker ook echt in beeld is, niet terwijl de sessie op de achtergrond draait. De gebruikers staan volledig los van elkaar: het budget, de waarschuwing en het vergrendelen van de ene gebruiker raken de andere niet.

Bij het bereiken van warning_before_seconds resterend toont de daemon een passieve kdialog-popup in de Wayland-sessie van target_user (popup.rs, draait als die gebruiker via expliciete uid/gid/env-vars).

Bij 0 seconden resterend (app.rs::enforce):

  1. Nogmaals een popup ("Schermtijd is verstreken.").
  2. session::lock_screen — vergrendelt het scherm via loginctl lock-session <id>.
  3. session::lock_account — vergrendelt het account via usermod -L, zodat een nieuwe inlogpoging (en het ontgrendelen van het scherm met wachtwoord) faalt via PAM.

Elk CLI-commando werkt op precies één gebruiker, aangegeven met de verplichte --user <naam>-vlag (moet overeenkomen met een target_user uit [[users]]). Let op: --user moet vóór het subcommando staan (screentimectl --user <naam> <commando>), niet erna — dat is een beperking van de clap-argumentparser.

sudo screentimectl --user <naam> add <minuten> voegt tijd toe en ontgrendelt het account automatisch zodra de resterende tijd weer positief is (app.rs::handle_add).

sudo screentimectl --user <naam> remove <minuten> (alias subtract) trekt tijd af van het budget van vandaag (app.rs::handle_remove). Het ontgrendelt niets en vergrendelt ook niets rechtstreeks — komt de teller hierdoor op of onder 0, dan grendelt de daemon vanzelf af bij de eerstvolgende tick (binnen 1 seconde), op dezelfde manier als wanneer de tijd gewoon afloopt.

sudo screentimectl --user <naam> lock vergrendelt scherm en account direct, ongeacht hoeveel tijd er nog over is (app.rs::handle_lock). De remaining_seconds van het budget blijven ongewijzigd staan.

sudo screentimectl --user <naam> unlock ontgrendelt alleen het account (usermod -U, app.rs::handle_unlock) zonder de resterende tijd te wijzigen. Het scherm zelf blijft vergrendeld totdat er met het wachtwoord wordt ingelogd — dat kan nu weer, omdat het account niet langer geblokkeerd is via PAM.

Schermtijd bekijken in de browser

Optioneel start de daemon een kleine HTTP-server (status_server.rs) die de resterende tijd toont, als status_port is ingesteld in config.toml. Bewust zonder authenticatie: dit is juist bedoeld zodat een gecontroleerd account dit zelf kan opvragen. De server luistert alleen op 127.0.0.1, dus niet bereikbaar vanaf het LAN — alleen vanaf de machine zelf.

  • http://127.0.0.1:<status_port>/status/<naam> — leesbare pagina voor één gebruiker ("Nog 42m 0s schermtijd over."), bedoeld om aan dat kind te geven als link/bookmark. Puur informatief, geen instellingen — niets om aan te passen.
  • http://127.0.0.1:<status_port>/status/<naam>.json — JSON ({"remaining_seconds":...,"locked":...}) voor diezelfde gebruiker; dit is ook wat de KDE-plasmoid (packaging/plasmoid/) gebruikt.
  • http://127.0.0.1:<status_port>/ — overzichtspagina met de resterende tijd van alle geconfigureerde gebruikers samen (verversd elke 5 seconden) — bedoeld voor de ouder, niet om met een kind te delen als er broers/zussen meedoen.

Vanaf je eigen apparaat kun je dit nog steeds bekijken via een SSH-tunnel:

ssh -L 8765:127.0.0.1:8765 <gebruiker>@<machine-ip>
curl http://127.0.0.1:8765/

Waarom niet de sessie/processen killen?

Een eerdere implementatie gebruikte login1.Manager.KillUser(uid, SIGKILL) om de volledige desktopsessie (alle ~470 processen onder user-<uid>.slice, inclusief compositor, audio, Akonadi/MySQL) in één klap te beëindigen. Dat werkte, maar gaf een merkbare, korte systeemhang door de massale gelijktijdige SIGKILL en risico op corrupte state in apps die niet netjes konden afsluiten. De huidige aanpak (alleen vergrendelen, niets killen) is zachter en voldoende: het account is alsnog niet meer te gebruiken zodra het scherm ontgrendeld moet worden.

Een tussenstap die de rauwe D-Bus-aanroep org.freedesktop.login1.Session.Lock() direct via zbus deed, faalde onvoorspelbaar met NotSupported, ook met retries tot 15 seconden. Shellen naar loginctl lock-session <id> (dezelfde onderliggende aanroep, maar via de systemd-eigen CLI) werkte wel betrouwbaar — vandaar de keuze om hier net als bij usermod een bestaand CLI-commando te gebruiken in plaats van de D-Bus-aanroep zelf te reproduceren.

Testen

Voor handmatig testen van de volledige waarschuwing/lock-flow zonder een hele dag te wachten staat er een tweede, lokaal testaccount (bv. testkid) met een eigen ingelogde Plasma-sessie op een losse tty (bv. tty3/seat0), en twee hulpbestanden in packaging/:

  • test-config.toml — budget van 1 minuut, waarschuwing bij 30s, popup van 5s.
  • set-test-state.sh — zet /var/lib/screentimectl/state.json terug op 40s resterend, niet gewaarschuwd, niet vergrendeld.

Testcyclus (elke regel apart uitvoeren, niet als één geplakt blok — lange regels kunnen in de terminal op de visuele line-wrap afbreken en dan deels als los commando worden uitgevoerd):

sudo install -m 755 target/release/screentimectld /usr/local/bin/screentimectld
sudo usermod -U testkid
sudo bash packaging/set-test-state.sh
sudo /usr/local/bin/screentimectld

Wissel daarna direct naar de tty van het testaccount (Ctrl+Alt+Fx) en blijf daar, anders telt de tijd niet af (zie "Werking" hierboven over de Active-vereiste). Tijd toevoegen tijdens het testen kan vanuit een andere terminal met sudo screentimectl --user testkid add 30.

Build

cargo build --release --workspace

Deployen naar de machine van het kind

Voor het overzetten naar een andere machine (zelfde soort systeem: Fedora/KDE Plasma Wayland) hoef je niet op die machine te bouwen. packaging/package.sh bouwt hier de release-binaries en verzamelt ze samen met het install-script en de systemd-unit in één map dist/, zodat je daar één zip van kunt maken en versturen:

packaging/package.sh
cd .. && zip -r screentimectl-dist.zip dist   # of: cd screenctrl && zip -r screentimectl-dist.zip dist

Op de doelmachine: unzip, en draai als root (de accounts van de kinderen moeten daar al bestaan):

cd dist
sudo ./install.sh

Het script installeert de binaries naar /usr/local/bin en de systemd-service, en schrijft — alleen als er nog geen config bestaat — een voorbeeld /etc/screentimectl/config.toml (uit config.toml.example, met [[users]]-blokken om in te vullen). Vul daarna de echte gebruikers in en start de service:

sudo "$EDITOR" /etc/screentimectl/config.toml   # vul [[users]] in, 1 blok per kind
sudo systemctl enable --now screentimectld

Draai je install.sh nogmaals op een machine die al een ingevulde config heeft (bv. om nieuwe binaries te installeren), dan laat het script die config staan en herstart het de service automatisch zodat de nieuwe binaries meteen actief zijn. Vereist op de doelmachine: usermod (shadow-utils, standaard aanwezig), kdialog (voor de popup) en systemd-logind met D-Bus (standaard op Fedora/KDE) — het script waarschuwt als iets daarvan ontbreekt.

Installeren (handmatig, lokaal)

sudo install -m 755 target/release/screentimectld /usr/local/bin/screentimectld
sudo install -m 755 target/release/screentimectl /usr/local/bin/screentimectl
sudo mkdir -p /etc/screentimectl
sudo cp packaging/config.toml.example /etc/screentimectl/config.toml
sudo "$EDITOR" /etc/screentimectl/config.toml   # vul [[users]] in, 1 blok per kind
sudo chmod 600 /etc/screentimectl/config.toml
sudo cp packaging/screentimectld.service /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable --now screentimectld

Gebruik

Elk commando werkt op één gebruiker, aangegeven met --user <naam> vóór het subcommando (moet overeenkomen met een target_user uit [[users]]):

sudo screentimectl --user kind1 add 30      # voeg 30 minuten toe aan het budget van vandaag
sudo screentimectl --user kind1 remove 15   # trek 15 minuten af van het budget van vandaag (alias: subtract)
sudo screentimectl --user kind1 lock        # vergrendel scherm + account direct, los van resterende tijd
sudo screentimectl --user kind1 unlock      # ontgrendel het account weer (resterende tijd blijft gelijk)
sudo screentimectl --user kind1 status      # toon resterende tijd

Logs

journalctl -u screentimectld -f