# TimekeeperEnforcement Local enforcement for the Timekeeper e107 plugin. Once a minute it looks for each monitored student's games, asks the server how many play minutes are left (`timer.php`), warns, and closes the game when there are none. Needs Java 11 or newer. Run it as root (it stops other users' processes and shows popups as them). ## Build ```bash javac -d out src/au/pfip/*.java jar --create --file TimekeeperEnforcement.jar --main-class au.pfip.Main -C out . ``` ## Settings The first of these that exists is used. Use `/etc/timekeeper.inf`, owned by root and not writable by the students: a settings file they can edit defeats the monitoring. 1. `/etc/timekeeper.inf` 2. `~/timekeeper.inf` 3. `~/.config/timekeeper/timekeeper.inf` 4. `./timekeeper.inf` If none exists, a new one is written to `~/.config/timekeeper/timekeeper.inf` with the defaults below. It is Java XML properties format. | Key | Default | Meaning | |---|---|---| | `UsersToMonitor` | *(empty, must be set)* | Comma separated accounts to watch, e.g. `alice,bob` | | `TimekeeperActive` | `true` | `false` makes the program wait 10 minutes and exit | | `ExemptNetwork` | *(empty = none)* | Network name where enforcement is off (checked at start-up only; anyone can name a hotspot after a network) | | `OfflineChecksBeforeKill` | `5` | Failed server checks in a row before a game is closed (`1` = close at once) | | `GamesRefreshMinutes` | `60` | How often the games list is fetched again | | `GamesURL`, `PlaytimeURL` | the overlord URLs | Server endpoints | | `LogFilePath` | home folder | Where `timekeeper.log` is written | | `MatchWholeWords` | `true` | A game name must not run on into more letters or digits in the command line (`technic` no longer matches `technical-report.odt`). `false` = plain "contains" | | `IgnoreCommandLines` | *(empty)* | Comma separated command line fragments that exempt a process, e.g. `--kiosk,-contentproc`. Anyone can add such a fragment to a command they start themselves | ## games.json A JSON array of strings, or an object whose values are the names (keys are ignored). Each name is looked for in the full command line (`ps -eo user,args`) of every process the student owns, case-sensitively. Check what a game really looks like first: ```bash ps -eo user,args | grep -i ``` - Use a name that appears in the command line, and is specific: `technic` also matched `technical-report.odt` before whole-word matching. - Wine/Proton games show up as their `.exe` name (`bf2.exe`). Native Steam games show up as the binary (`hl2_linux`). - Browsers listed here are closed like any other game, including a browser used for Timekeeper's own questions. Use `IgnoreCommandLines` or a separate browser for those. - It is a deny-list: a browser, launcher or game that isn't listed isn't enforced. The games list is cached in `games.cache` next to the settings file and used when the server can't be reached. ## systemd ```ini [Unit] Description=Timekeeper enforcement After=network-online.target Wants=network-online.target [Service] ExecStart=/usr/bin/java -jar /opt/timekeeper/TimekeeperEnforcement.jar Restart=always RestartSec=30 User=root [Install] WantedBy=multi-user.target ``` ## Tests `test/au/pfip/Harness.java` and `Harness2.java` run against a fake local server and start and stop throwaway `sleep` processes owned by the current user: ```bash javac -d out src/au/pfip/*.java test/au/pfip/*.java java -cp out au.pfip.Harness java -cp out au.pfip.Harness2 ```