Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
88e60f9bf9 | ||
|
|
eb0242c182 | ||
|
|
6733aacb6e |
No files matched your search
@@ -26,7 +26,7 @@ jobs:
|
||||
- name: Tests
|
||||
run: python3 -m pytest -p no:cacheprovider
|
||||
- name: Shellcheck
|
||||
run: shellcheck -S warning install.sh install-online.sh update.sh uninstall.sh tools/release-upload.sh image/build.sh image/stage-voxip/prerun.sh image/stage-voxip/00-voxip/00-run.sh bin/voxip-kiosk-x
|
||||
run: shellcheck -S warning install.sh install-online.sh update.sh uninstall.sh tools/release-upload.sh image/build.sh image/stage-daynivo/prerun.sh image/stage-daynivo/00-daynivo/00-run.sh bin/daynivo-kiosk-x
|
||||
|
||||
test-latest:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
PY ?= .venv/bin/python
|
||||
DEV := $(CURDIR)/.dev
|
||||
DEV_ENV := PYTHONPATH=src VOXIP_CONFIG_DIR=$(DEV)/etc VOXIP_STATE_DIR=$(DEV)/state VOXIP_LOG_DIR=$(DEV)/log \
|
||||
VOXIP_SHARE_DIR=$(DEV)/share VOXIP_AGENT_RUN_DIR=$(DEV)/run VOXIP_BROWSER_RUN_DIR=$(DEV)/run \
|
||||
VOXIP_NETWORK_STATE_DIR=$(DEV)/net VOXIP_HELPER=/nonexistent/voxip-kiosk-helper
|
||||
DEV_ENV := PYTHONPATH=src DAYNIVO_CONFIG_DIR=$(DEV)/etc DAYNIVO_STATE_DIR=$(DEV)/state DAYNIVO_LOG_DIR=$(DEV)/log \
|
||||
DAYNIVO_SHARE_DIR=$(DEV)/share DAYNIVO_AGENT_RUN_DIR=$(DEV)/run DAYNIVO_BROWSER_RUN_DIR=$(DEV)/run \
|
||||
DAYNIVO_NETWORK_STATE_DIR=$(DEV)/net DAYNIVO_HELPER=/nonexistent/daynivo-kiosk-helper
|
||||
|
||||
.PHONY: venv test lint test-bookworm dev image clean
|
||||
|
||||
@@ -13,8 +13,8 @@ test:
|
||||
$(PY) -m pytest
|
||||
|
||||
lint:
|
||||
shellcheck -S warning install.sh update.sh uninstall.sh image/build.sh image/stage-voxip/prerun.sh \
|
||||
image/stage-voxip/00-voxip/00-run.sh bin/voxip-kiosk-x
|
||||
shellcheck -S warning install.sh update.sh uninstall.sh image/build.sh image/stage-daynivo/prerun.sh \
|
||||
image/stage-daynivo/00-daynivo/00-run.sh bin/daynivo-kiosk-x
|
||||
|
||||
# The test suite against the exact Python/Flask/argon2 versions that Raspberry Pi OS Bookworm ships.
|
||||
test-bookworm:
|
||||
@@ -22,7 +22,7 @@ test-bookworm:
|
||||
apt-get update -qq && DEBIAN_FRONTEND=noninteractive apt-get install -y -qq --no-install-recommends \
|
||||
python3 python3-flask python3-argon2 python3-websocket python3-pytest shellcheck >/dev/null && \
|
||||
cp -r /src /work && cd /work && rm -rf .venv && python3 -m pytest -p no:cacheprovider && \
|
||||
shellcheck -S warning install.sh install-online.sh update.sh uninstall.sh tools/release-upload.sh image/build.sh bin/voxip-kiosk-x'
|
||||
shellcheck -S warning install.sh install-online.sh update.sh uninstall.sh tools/release-upload.sh image/build.sh bin/daynivo-kiosk-x'
|
||||
|
||||
# Web interface on http://127.0.0.1:8080 with throwaway state in .dev/ (system actions fail gracefully).
|
||||
dev:
|
||||
@@ -30,7 +30,7 @@ dev:
|
||||
test -f $(DEV)/etc/config.json || cp config/config.default.json $(DEV)/etc/config.json
|
||||
cp VERSION $(DEV)/share/VERSION
|
||||
@echo "Setupcode: zie $(DEV)/etc/setup-code (wordt aangemaakt bij de start)"
|
||||
$(DEV_ENV) $(PY) -m flask --app 'voxip_kiosk.web:create_app()' run --host 127.0.0.1 --port 8080
|
||||
$(DEV_ENV) $(PY) -m flask --app 'daynivo_kiosk.web:create_app()' run --host 127.0.0.1 --port 8080
|
||||
|
||||
image:
|
||||
./image/build.sh
|
||||
|
||||
@@ -12,7 +12,7 @@ webinterface **Daynivo Kiosk Manager** op `http://kiosk.local:8080`.
|
||||
een setupcode die op het scherm staat.
|
||||
- Netwerkwijzigingen met automatische rollback.
|
||||
- Geen klantspecifieke code: alles per klant staat in
|
||||
`/etc/voxip-kiosk/config.json` en kan bij uitrol via de bootpartitie
|
||||
`/etc/daynivo-kiosk/config.json` en kan bij uitrol via de bootpartitie
|
||||
worden meegegeven.
|
||||
|
||||
De architectuur en de veiligheidskeuzes staan in
|
||||
@@ -61,7 +61,7 @@ sudo ./install.sh --url "https://schermen-ijsselflat.voxip.nl" --name "IJsselfla
|
||||
```
|
||||
|
||||
Alle opties zijn optioneel. Het script installeert de benodigde pakketten,
|
||||
maakt de gebruikers `kiosk` en `voxip` aan, installeert de services en
|
||||
maakt de gebruikers `kiosk` en `daynivo` aan, installeert de services en
|
||||
start de kiosk. Aan het eind toont het de setupcode.
|
||||
|
||||
Draaide hier eerder de Daynivo-kiosk (`raspberry-pi/install-kiosk.sh` uit
|
||||
@@ -72,14 +72,14 @@ de Daynivo-repository)? Dan neemt `install.sh` de display-URL (inclusief
|
||||
|
||||
In de webinterface onder **Systeem → Software-updates**: *Nu controleren*,
|
||||
*Bijwerken naar …*, en het vinkje **Automatisch bijwerken**. Elke nacht
|
||||
tussen 03:00 en 05:00 (`voxip-kiosk-update.timer`) kijkt de kiosk of er een
|
||||
tussen 03:00 en 05:00 (`daynivo-kiosk-update.timer`) kijkt de kiosk of er een
|
||||
nieuwe release is; met het vinkje aan installeert hij die meteen. Dat werkt
|
||||
ook op een apparaat dat met de image of vanuit een clone is geïnstalleerd.
|
||||
Vanaf de commandoregel:
|
||||
|
||||
```bash
|
||||
sudo /usr/lib/voxip-kiosk/voxip-kiosk-updater --check # alleen kijken
|
||||
sudo /usr/lib/voxip-kiosk/voxip-kiosk-updater --install # nieuwste release installeren
|
||||
sudo /usr/lib/daynivo-kiosk/daynivo-kiosk-updater --check # alleen kijken
|
||||
sudo /usr/lib/daynivo-kiosk/daynivo-kiosk-updater --install # nieuwste release installeren
|
||||
```
|
||||
|
||||
De updater installeert alleen een nieuwere versie met een kloppende
|
||||
@@ -89,15 +89,40 @@ het ook met git:
|
||||
|
||||
```bash
|
||||
cd daynivo-kiosk-os
|
||||
sudo ./update.sh # git pull --ff-only + back-up van /etc/voxip-kiosk + install.sh --upgrade
|
||||
sudo ./update.sh # git pull --ff-only + back-up van /etc/daynivo-kiosk + install.sh --upgrade
|
||||
```
|
||||
|
||||
Een update overschrijft nooit bestaande configuratie of het
|
||||
beheerderswachtwoord. Back-ups staan in `/var/backups/voxip-kiosk-*.tar.gz`
|
||||
beheerderswachtwoord. Back-ups staan in `/var/backups/daynivo-kiosk-*.tar.gz`
|
||||
(de laatste vijf). Op een apparaat met de image staat de broncode in
|
||||
`/usr/src/voxip-kiosk`. Clone de repository voor updates naar een eigen map,
|
||||
`/usr/src/daynivo-kiosk`. Clone de repository voor updates naar een eigen map,
|
||||
of pak een release uit en draai daar `sudo ./update.sh --no-pull`.
|
||||
|
||||
### Van de VoxIP-namen naar Daynivo
|
||||
|
||||
Kiosks tot en met versie 1.2.x gebruiken nog de oude namen: services
|
||||
`voxip-kiosk-*`, mappen `/etc/voxip-kiosk`, `/var/lib/voxip-kiosk*` en
|
||||
`/usr/lib/voxip-kiosk`, en de gebruiker `voxip`. De eerste update naar een
|
||||
nieuwere versie zet zo'n kiosk eenmalig over, gewoon via de webinterface of
|
||||
's nachts automatisch:
|
||||
|
||||
- services, programma's en instellingen krijgen de `daynivo-`-namen; de oude
|
||||
services worden uitgeschakeld en verwijderd;
|
||||
- configuratie, beheerderswachtwoord, browserprofiel (offline-cache), logs en
|
||||
updatestatus verhuizen naar de `daynivo-`-mappen; de oude mapnamen blijven
|
||||
als verwijzing bestaan;
|
||||
- de gebruiker `voxip` wordt `daynivo`, met hetzelfde uid, zodat alle
|
||||
bestanden hun eigenaar houden;
|
||||
- de netwerkprofielen `voxip-eth0`/`voxip-wlan0` worden `daynivo-eth0`/
|
||||
`daynivo-wlan0` (niet zolang er een netwerkwijziging op bevestiging wacht;
|
||||
dan bij de volgende update);
|
||||
- wie in de Kiosk Manager was ingelogd, moet één keer opnieuw inloggen.
|
||||
|
||||
Op de bootpartitie blijven `voxip-config.json`, `voxip-authorized_keys` en
|
||||
`voxip-network-reset` werken, net als de opdracht `voxip-kiosk-ctl`. Bij het
|
||||
bouwen van een image worden `VOXIP_DEFAULT_URL` en `VOXIP_DEVICE_NAME` nog
|
||||
gelezen als de `DAYNIVO_`-varianten ontbreken.
|
||||
|
||||
### Verwijderen
|
||||
|
||||
```bash
|
||||
@@ -109,7 +134,7 @@ sudo ./uninstall.sh --purge # ook configuratie, status, logs, netwerkprofielen
|
||||
|
||||
Het image wordt gebouwd met [pi-gen](https://github.com/RPi-Distro/pi-gen),
|
||||
de officiële bouwtool van Raspberry Pi OS. De extra stage
|
||||
`image/stage-voxip` voert hetzelfde `install.sh` uit als een handmatige
|
||||
`image/stage-daynivo` voert hetzelfde `install.sh` uit als een handmatige
|
||||
installatie.
|
||||
|
||||
Vereist: een Linux-buildmachine (x86_64 of arm64) met Docker en git. Op
|
||||
@@ -131,14 +156,14 @@ Instelbaar via omgevingsvariabelen:
|
||||
|---|---|---|
|
||||
| `PI_GEN_REF` | `2024-11-19-raspios-bookworm-armhf` | pi-gen-tag (32-bit) |
|
||||
| `RELEASE` | `bookworm` | `bookworm` of `trixie` (moet bij `PI_GEN_REF` passen) |
|
||||
| `VOXIP_DEFAULT_URL` | `https://schermen-ijsselflat.voxip.nl` | standaard kiosk-URL |
|
||||
| `VOXIP_DEVICE_NAME` | `Daynivo Kiosk` | standaard apparaatnaam |
|
||||
| `DAYNIVO_DEFAULT_URL` | `https://schermen-ijsselflat.voxip.nl` | standaard kiosk-URL |
|
||||
| `DAYNIVO_DEVICE_NAME` | `Daynivo Kiosk` | standaard apparaatnaam |
|
||||
| `TARGET_HOSTNAME` | `kiosk` | hostname (`http://<hostname>.local:8080`) |
|
||||
| `ALLOW_DIRTY` | `0` | `1` = bouw de werkmap in plaats van de laatste commit |
|
||||
|
||||
**Reproduceerbaarheid.** pi-gen staat vast op een tag en de broncode
|
||||
gaat via `git archive` van één commit mee. Beide staan in `BUILD-INFO`
|
||||
(ook in de image: `/usr/share/voxip-kiosk/BUILD-INFO`). De Debian- en
|
||||
(ook in de image: `/usr/share/daynivo-kiosk/BUILD-INFO`). De Debian- en
|
||||
Raspberry Pi-pakketten komen uit de archieven zoals die op het moment van
|
||||
bouwen zijn. Er bestaat geen snapshot-archief voor Raspberry Pi OS, dus
|
||||
twee builds op verschillende dagen kunnen nieuwere pakketversies bevatten.
|
||||
@@ -199,10 +224,10 @@ sudo daynivo-kiosk-ctl reload # pagina herladen
|
||||
sudo daynivo-kiosk-ctl restart-browser # Chromium herstarten
|
||||
sudo daynivo-kiosk-ctl health # healthcheck-rapport
|
||||
sudo daynivo-kiosk-ctl maintenance on|off # healthcheck tijdelijk niets laten herstellen
|
||||
journalctl -u voxip-kiosk-browser -u voxip-kiosk-agent -f
|
||||
journalctl -u daynivo-kiosk-browser -u daynivo-kiosk-agent -f
|
||||
```
|
||||
|
||||
### Configuratie — `/etc/voxip-kiosk/config.json`
|
||||
### Configuratie — `/etc/daynivo-kiosk/config.json`
|
||||
|
||||
| Sleutel | Standaard | Betekenis |
|
||||
|---|---|---|
|
||||
@@ -270,8 +295,8 @@ make dev # http://127.0.0.1:8080 met tijdelijke mappen in .dev/
|
||||
|
||||
| Symptoom | Oplossing |
|
||||
|---|---|
|
||||
| Zwart scherm na opstarten | `sudo daynivo-kiosk-ctl status`. Kijk in de journal van `voxip-kiosk-x`/`voxip-kiosk-browser`. Staat de monitor aan vóór de Pi? |
|
||||
| Zwart scherm na opstarten | `sudo daynivo-kiosk-ctl status`. Kijk in de journal van `daynivo-kiosk-x`/`daynivo-kiosk-browser`. Staat de monitor aan vóór de Pi? |
|
||||
| Beheerinterface onbereikbaar na netwerkwijziging | Wacht 3 minuten (automatische rollback) of zet `daynivo-network-reset` op de bootpartitie en herstart. |
|
||||
| Wachtwoord vergeten | `sudo daynivo-kiosk-ctl reset-password` via SSH, of verwijder `/etc/voxip-kiosk/admin.json` met de SD-kaart in een Linux-pc. |
|
||||
| Wachtwoord vergeten | `sudo daynivo-kiosk-ctl reset-password` via SSH, of verwijder `/etc/daynivo-kiosk/admin.json` met de SD-kaart in een Linux-pc. |
|
||||
| `kiosk.local` werkt niet | Gebruik het IP-adres (staat op het setupscherm en in je router). Windows heeft soms Bonjour/mDNS nodig. Geef iedere kiosk een unieke hostname. |
|
||||
| Onderspanning in het dashboard | Gebruik de officiële 5,1 V/2,5 A-voeding. Onderspanning veroorzaakt crashes en SD-kaartcorruptie. |
|
||||
Executable
+9
@@ -0,0 +1,9 @@
|
||||
#!/usr/bin/python3 -I
|
||||
# Installed to /usr/lib/daynivo-kiosk/ by install.sh. -I: ignore PYTHON* env and user site-packages.
|
||||
import sys
|
||||
|
||||
sys.path.insert(0, "/usr/lib/daynivo-kiosk/python")
|
||||
|
||||
from daynivo_kiosk.agent import main # noqa: E402
|
||||
|
||||
sys.exit(main())
|
||||
Executable
+9
@@ -0,0 +1,9 @@
|
||||
#!/usr/bin/python3 -I
|
||||
# Installed to /usr/lib/daynivo-kiosk/ by install.sh. -I: ignore PYTHON* env and user site-packages.
|
||||
import sys
|
||||
|
||||
sys.path.insert(0, "/usr/lib/daynivo-kiosk/python")
|
||||
|
||||
from daynivo_kiosk.browser import main # noqa: E402
|
||||
|
||||
sys.exit(main())
|
||||
@@ -1,9 +1,9 @@
|
||||
#!/usr/bin/python3 -I
|
||||
# Installed to /usr/lib/voxip-kiosk/ by install.sh. -I: ignore PYTHON* env and user site-packages.
|
||||
# Installed to /usr/lib/daynivo-kiosk/ by install.sh. -I: ignore PYTHON* env and user site-packages.
|
||||
import sys
|
||||
|
||||
sys.path.insert(0, "/usr/lib/voxip-kiosk/python")
|
||||
sys.path.insert(0, "/usr/lib/daynivo-kiosk/python")
|
||||
|
||||
from voxip_kiosk.ctl import main # noqa: E402
|
||||
from daynivo_kiosk.ctl import main # noqa: E402
|
||||
|
||||
sys.exit(main())
|
||||
Executable
+9
@@ -0,0 +1,9 @@
|
||||
#!/usr/bin/python3 -I
|
||||
# Installed to /usr/lib/daynivo-kiosk/ by install.sh. -I: ignore PYTHON* env and user site-packages.
|
||||
import sys
|
||||
|
||||
sys.path.insert(0, "/usr/lib/daynivo-kiosk/python")
|
||||
|
||||
from daynivo_kiosk.healthcheck import main # noqa: E402
|
||||
|
||||
sys.exit(main())
|
||||
Executable
+9
@@ -0,0 +1,9 @@
|
||||
#!/usr/bin/python3 -I
|
||||
# Installed to /usr/lib/daynivo-kiosk/ by install.sh. -I: ignore PYTHON* env and user site-packages.
|
||||
import sys
|
||||
|
||||
sys.path.insert(0, "/usr/lib/daynivo-kiosk/python")
|
||||
|
||||
from daynivo_kiosk.helper import main # noqa: E402
|
||||
|
||||
sys.exit(main())
|
||||
Executable
+9
@@ -0,0 +1,9 @@
|
||||
#!/usr/bin/python3 -I
|
||||
# Installed to /usr/lib/daynivo-kiosk/ by install.sh. -I: ignore PYTHON* env and user site-packages.
|
||||
import sys
|
||||
|
||||
sys.path.insert(0, "/usr/lib/daynivo-kiosk/python")
|
||||
|
||||
from daynivo_kiosk.manager import main # noqa: E402
|
||||
|
||||
sys.exit(main())
|
||||
Executable
+9
@@ -0,0 +1,9 @@
|
||||
#!/usr/bin/python3 -I
|
||||
# Installed to /usr/lib/daynivo-kiosk/ by install.sh. -I: ignore PYTHON* env and user site-packages.
|
||||
import sys
|
||||
|
||||
sys.path.insert(0, "/usr/lib/daynivo-kiosk/python")
|
||||
|
||||
from daynivo_kiosk.updater import main # noqa: E402
|
||||
|
||||
sys.exit(main())
|
||||
@@ -2,7 +2,7 @@
|
||||
# Starts the kiosk X server on vt7 for user "kiosk" (via Xorg.wrap).
|
||||
# A fresh MIT cookie per start; Chromium reads it via XAUTHORITY.
|
||||
set -eu
|
||||
AUTH="${RUNTIME_DIRECTORY:-/run/voxip-kiosk-x}/Xauthority"
|
||||
AUTH="${RUNTIME_DIRECTORY:-/run/daynivo-kiosk-x}/Xauthority"
|
||||
umask 077
|
||||
: > "$AUTH"
|
||||
xauth -q -f "$AUTH" add :0 . "$(mcookie)"
|
||||
@@ -1,9 +0,0 @@
|
||||
#!/usr/bin/python3 -I
|
||||
# Installed to /usr/lib/voxip-kiosk/ by install.sh. -I: ignore PYTHON* env and user site-packages.
|
||||
import sys
|
||||
|
||||
sys.path.insert(0, "/usr/lib/voxip-kiosk/python")
|
||||
|
||||
from voxip_kiosk.agent import main # noqa: E402
|
||||
|
||||
sys.exit(main())
|
||||
@@ -1,9 +0,0 @@
|
||||
#!/usr/bin/python3 -I
|
||||
# Installed to /usr/lib/voxip-kiosk/ by install.sh. -I: ignore PYTHON* env and user site-packages.
|
||||
import sys
|
||||
|
||||
sys.path.insert(0, "/usr/lib/voxip-kiosk/python")
|
||||
|
||||
from voxip_kiosk.browser import main # noqa: E402
|
||||
|
||||
sys.exit(main())
|
||||
@@ -1,9 +0,0 @@
|
||||
#!/usr/bin/python3 -I
|
||||
# Installed to /usr/lib/voxip-kiosk/ by install.sh. -I: ignore PYTHON* env and user site-packages.
|
||||
import sys
|
||||
|
||||
sys.path.insert(0, "/usr/lib/voxip-kiosk/python")
|
||||
|
||||
from voxip_kiosk.healthcheck import main # noqa: E402
|
||||
|
||||
sys.exit(main())
|
||||
@@ -1,9 +0,0 @@
|
||||
#!/usr/bin/python3 -I
|
||||
# Installed to /usr/lib/voxip-kiosk/ by install.sh. -I: ignore PYTHON* env and user site-packages.
|
||||
import sys
|
||||
|
||||
sys.path.insert(0, "/usr/lib/voxip-kiosk/python")
|
||||
|
||||
from voxip_kiosk.helper import main # noqa: E402
|
||||
|
||||
sys.exit(main())
|
||||
@@ -1,9 +0,0 @@
|
||||
#!/usr/bin/python3 -I
|
||||
# Installed to /usr/lib/voxip-kiosk/ by install.sh. -I: ignore PYTHON* env and user site-packages.
|
||||
import sys
|
||||
|
||||
sys.path.insert(0, "/usr/lib/voxip-kiosk/python")
|
||||
|
||||
from voxip_kiosk.manager import main # noqa: E402
|
||||
|
||||
sys.exit(main())
|
||||
@@ -1,9 +0,0 @@
|
||||
#!/usr/bin/python3 -I
|
||||
# Installed to /usr/lib/voxip-kiosk/ by install.sh. -I: ignore PYTHON* env and user site-packages.
|
||||
import sys
|
||||
|
||||
sys.path.insert(0, "/usr/lib/voxip-kiosk/python")
|
||||
|
||||
from voxip_kiosk.updater import main # noqa: E402
|
||||
|
||||
sys.exit(main())
|
||||
File renamed without changes.
File renamed without changes.
+3
-3
@@ -1,4 +1,4 @@
|
||||
/var/log/voxip-kiosk/*.log {
|
||||
/var/log/daynivo-kiosk/*.log {
|
||||
weekly
|
||||
rotate 8
|
||||
maxsize 2M
|
||||
@@ -6,6 +6,6 @@
|
||||
delaycompress
|
||||
missingok
|
||||
notifempty
|
||||
create 0640 voxip voxip
|
||||
su voxip voxip
|
||||
create 0640 daynivo daynivo
|
||||
su daynivo daynivo
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
[Plymouth Theme]
|
||||
Name=daynivo-kiosk
|
||||
Description=Daynivo Kiosk boot splash (logo on white)
|
||||
ModuleName=script
|
||||
|
||||
[script]
|
||||
ImageDir=/usr/share/plymouth/themes/daynivo-kiosk
|
||||
ScriptFile=/usr/share/plymouth/themes/daynivo-kiosk/daynivo-kiosk.script
|
||||
File renamed without changes.
File renamed without changes.
|
Before Width: | Height: | Size: 111 KiB After Width: | Height: | Size: 111 KiB |
@@ -1,8 +0,0 @@
|
||||
[Plymouth Theme]
|
||||
Name=voxip-kiosk
|
||||
Description=Daynivo Kiosk boot splash (logo on white)
|
||||
ModuleName=script
|
||||
|
||||
[script]
|
||||
ImageDir=/usr/share/plymouth/themes/voxip-kiosk
|
||||
ScriptFile=/usr/share/plymouth/themes/voxip-kiosk/voxip-kiosk.script
|
||||
+3
-3
@@ -1,4 +1,4 @@
|
||||
# Daynivo Kiosk OS: the web interface (user voxip) may run exactly one program
|
||||
# Daynivo Kiosk OS: the web interface (user daynivo) may run exactly one program
|
||||
# as root: the restricted helper, which validates all of its input itself.
|
||||
Defaults:voxip !requiretty
|
||||
voxip ALL=(root) NOPASSWD: /usr/lib/voxip-kiosk/voxip-kiosk-helper
|
||||
Defaults:daynivo !requiretty
|
||||
daynivo ALL=(root) NOPASSWD: /usr/lib/daynivo-kiosk/daynivo-kiosk-helper
|
||||
+27
-27
@@ -11,7 +11,7 @@ samenwerken en waarom bepaalde keuzes zijn gemaakt.
|
||||
- Eén HTTPS-pagina fullscreen in Chromium, zonder desktopomgeving.
|
||||
- Lokale webbeheerinterface (*Daynivo Kiosk Manager*) op poort 8080.
|
||||
- Geen klantspecifieke code: alles wat per klant verschilt staat in
|
||||
`/etc/voxip-kiosk/config.json` en kan bij uitrol via de bootpartitie
|
||||
`/etc/daynivo-kiosk/config.json` en kan bij uitrol via de bootpartitie
|
||||
worden meegegeven (`daynivo-config.json`).
|
||||
|
||||
## Overzicht
|
||||
@@ -19,25 +19,25 @@ samenwerken en waarom bepaalde keuzes zijn gemaakt.
|
||||
```
|
||||
┌──────────────────────── Raspberry Pi ────────────────────────┐
|
||||
│ │
|
||||
HDMI ◄────────┤ voxip-kiosk-x.service Xorg :0 op vt7 (user kiosk) │
|
||||
HDMI ◄────────┤ daynivo-kiosk-x.service Xorg :0 op vt7 (user kiosk) │
|
||||
│ ▲ │
|
||||
│ │ DISPLAY=:0 │
|
||||
│ voxip-kiosk-browser.service Chromium --kiosk (user kiosk) │
|
||||
│ daynivo-kiosk-browser.service Chromium --kiosk (user kiosk) │
|
||||
│ ▲ CDP 127.0.0.1:9222 │
|
||||
│ │ ┌─────────────────────────────┐ │
|
||||
│ ├──┤ voxip-kiosk-agent.service │ navigatie, offline-│
|
||||
│ ├──┤ daynivo-kiosk-agent.service │ navigatie, offline-│
|
||||
│ │ │ (user kiosk) │ pagina, verversen, │
|
||||
│ │ └─────────────────────────────┘ hang-detectie │
|
||||
│ │ │
|
||||
LAN :8080 ───►│ voxip-kiosk-manager.service Flask + gunicorn (user voxip) │
|
||||
LAN :8080 ───►│ daynivo-kiosk-manager.service Flask + gunicorn (user daynivo) │
|
||||
│ │ sudo -n (één commando) │
|
||||
│ ▼ │
|
||||
│ voxip-kiosk-helper root, vaste werkwoorden, │
|
||||
│ daynivo-kiosk-helper root, vaste werkwoorden, │
|
||||
│ strikte invoervalidatie │
|
||||
│ │
|
||||
│ voxip-kiosk-healthcheck.timer elke minuut, root │
|
||||
│ voxip-network-guard.service netwerk-rollback bij boot │
|
||||
│ voxip-boot-import.service config/SSH-sleutels vanaf │
|
||||
│ daynivo-kiosk-healthcheck.timer elke minuut, root │
|
||||
│ daynivo-network-guard.service netwerk-rollback bij boot │
|
||||
│ daynivo-boot-import.service config/SSH-sleutels vanaf │
|
||||
│ de bootpartitie │
|
||||
└──────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
@@ -46,16 +46,16 @@ samenwerken en waarom bepaalde keuzes zijn gemaakt.
|
||||
|
||||
| Onderdeel | Gebruiker | Taak |
|
||||
|---|---|---|
|
||||
| `voxip-kiosk-x` | `kiosk` (via Xorg.wrap) | Minimale X-server op vt7, zonder cursor, zonder TCP. |
|
||||
| `voxip-kiosk-browser` | `kiosk` | Past rotatie/resolutie/DPMS toe (xrandr/xset), ruimt een vastgelopen profiel op en start Chromium. |
|
||||
| `voxip-kiosk-agent` | `kiosk` | Bestuurt Chromium via het DevTools Protocol op localhost: offline-pagina, URL-wissel zonder herstart, periodiek verversen, hang-detectie. |
|
||||
| `voxip-kiosk-manager` | `voxip` | Flask-webinterface (gunicorn, 1 worker, 4 threads). |
|
||||
| `voxip-kiosk-helper` | `root` (via sudo) | Enige geprivilegieerde ingang: reboot, uitschakelen, Chromium herstarten, hostname, wifi-land, wifi-scan, netwerkconfiguratie + rollback, updates controleren en starten. |
|
||||
| `voxip-kiosk-updater` | `root` (timer, of eigen transient unit via de helper) | Haalt de nieuwste release op (HTTPS, SHA-256), pakt hem uit in `/opt/daynivo-kiosk-os/<versie>` en draait `update.sh --no-pull`. Nooit een oudere of gelijke versie. |
|
||||
| `voxip-kiosk-healthcheck` | `root` | Bewaakt alle services en herstelt ze, met logging. |
|
||||
| `daynivo-kiosk-x` | `kiosk` (via Xorg.wrap) | Minimale X-server op vt7, zonder cursor, zonder TCP. |
|
||||
| `daynivo-kiosk-browser` | `kiosk` | Past rotatie/resolutie/DPMS toe (xrandr/xset), ruimt een vastgelopen profiel op en start Chromium. |
|
||||
| `daynivo-kiosk-agent` | `kiosk` | Bestuurt Chromium via het DevTools Protocol op localhost: offline-pagina, URL-wissel zonder herstart, periodiek verversen, hang-detectie. |
|
||||
| `daynivo-kiosk-manager` | `daynivo` | Flask-webinterface (gunicorn, 1 worker, 4 threads). |
|
||||
| `daynivo-kiosk-helper` | `root` (via sudo) | Enige geprivilegieerde ingang: reboot, uitschakelen, Chromium herstarten, hostname, wifi-land, wifi-scan, netwerkconfiguratie + rollback, updates controleren en starten. |
|
||||
| `daynivo-kiosk-updater` | `root` (timer, of eigen transient unit via de helper) | Haalt de nieuwste release op (HTTPS, SHA-256), pakt hem uit in `/opt/daynivo-kiosk-os/<versie>` en draait `update.sh --no-pull`. Nooit een oudere of gelijke versie. |
|
||||
| `daynivo-kiosk-healthcheck` | `root` | Bewaakt alle services en herstelt ze, met logging. |
|
||||
|
||||
De webinterface draait **nooit** als root. De sudoers-regel staat
|
||||
uitsluitend `/usr/lib/voxip-kiosk/voxip-kiosk-helper` toe. De helper:
|
||||
uitsluitend `/usr/lib/daynivo-kiosk/daynivo-kiosk-helper` toe. De helper:
|
||||
|
||||
- draait met `python3 -I` (geen `PYTHONPATH`, geen user site-packages);
|
||||
- kent alleen een vaste lijst werkwoorden; interne werkwoorden (zoals
|
||||
@@ -69,8 +69,8 @@ uitsluitend `/usr/lib/voxip-kiosk/voxip-kiosk-helper` toe. De helper:
|
||||
|
||||
Xorg en Chromium zijn aparte services. Daardoor is *Chromium herstarten*
|
||||
snel (X blijft draaien) en geeft het exitcode van Chromium direct aan
|
||||
systemd door. `voxip-kiosk-browser` heeft `Requires=` en `PartOf=` op
|
||||
`voxip-kiosk-x`: valt X weg, dan start de browser opnieuw mee.
|
||||
systemd door. `daynivo-kiosk-browser` heeft `Requires=` en `PartOf=` op
|
||||
`daynivo-kiosk-x`: valt X weg, dan start de browser opnieuw mee.
|
||||
|
||||
### Chromium besturen via CDP
|
||||
|
||||
@@ -86,7 +86,7 @@ webinterface gebruiken dit om:
|
||||
### Offline-gedrag
|
||||
|
||||
Chromium start altijd op een lokale startpagina
|
||||
(`/usr/share/voxip-kiosk/www/start.html`). De agent controleert met een
|
||||
(`/usr/share/daynivo-kiosk/www/start.html`). De agent controleert met een
|
||||
HTTPS-verzoek (mét certificaatvalidatie) of de doel-URL bereikbaar is en
|
||||
navigeert pas dan. De offline-pagina verschijnt alleen als de pagina niet
|
||||
**geladen** kan worden (startpagina, Chromium-foutpagina). Valt het
|
||||
@@ -111,13 +111,13 @@ draait) kan het apparaat dus claimen, iemand anders op het netwerk niet.
|
||||
|
||||
1. De helper maakt een snapshot van `/etc/NetworkManager/system-connections`
|
||||
en van de actieve verbindingen.
|
||||
2. Hij schrijft het nieuwe profiel (`voxip-eth0` / `voxip-wlan0`, prioriteit
|
||||
2. Hij schrijft het nieuwe profiel (`daynivo-eth0` / `daynivo-wlan0`, prioriteit
|
||||
100) en plant een rollback-timer (`systemd-run`, 180 s).
|
||||
3. Activatie gebeurt asynchroon, zodat het HTTP-antwoord nog aankomt.
|
||||
4. De beheerder bevestigt binnen 180 s (eventueel via het nieuwe adres),
|
||||
anders zet de timer de snapshot terug.
|
||||
5. Valt de stroom uit tijdens die periode, dan zet
|
||||
`voxip-network-guard.service` de snapshot terug bij het opstarten, nog
|
||||
`daynivo-network-guard.service` de snapshot terug bij het opstarten, nog
|
||||
vóór NetworkManager start.
|
||||
6. Noodherstel: een leeg bestand `daynivo-network-reset` op de bootpartitie
|
||||
verwijdert bij de volgende boot alle Daynivo-netwerkprofielen (terug naar
|
||||
@@ -125,12 +125,12 @@ draait) kan het apparaat dus claimen, iemand anders op het netwerk niet.
|
||||
|
||||
### Configuratie
|
||||
|
||||
- `/etc/voxip-kiosk/config.json`: eigenaar `voxip`, groep `kiosk`, 0640.
|
||||
- `/etc/daynivo-kiosk/config.json`: eigenaar `daynivo`, groep `kiosk`, 0640.
|
||||
Wordt atomair opgeslagen (tijdelijk bestand, `fsync`, `rename`, `fsync`
|
||||
van de map) met een `.bak` van de vorige versie. Ongeldige waarden vallen
|
||||
bij het laden terug op de standaard, zodat de kiosk altijd start.
|
||||
Onbekende sleutels blijven bewaard (vooruit-/achteruitcompatibel).
|
||||
- `/etc/voxip-kiosk/admin.json`: Argon2id-hash, 0600, alleen `voxip`.
|
||||
- `/etc/daynivo-kiosk/admin.json`: Argon2id-hash, 0600, alleen `daynivo`.
|
||||
- Updates (`install.sh --upgrade` / `update.sh` / de updater) overschrijven
|
||||
nooit bestaande configuratie. De updater draait in een eigen systemd-unit,
|
||||
zodat het herstarten van de beheerinterface tijdens `install.sh` de update
|
||||
@@ -179,8 +179,8 @@ daynivo-kiosk-os/
|
||||
├── install.sh installatie op bestaande Pi OS + gebruikt door image-build
|
||||
├── update.sh update met backup van configuratie
|
||||
├── uninstall.sh verwijderen (--purge voor alles)
|
||||
├── bin/ dunne launchers → /usr/lib/voxip-kiosk/
|
||||
├── src/voxip_kiosk/ Python-package (alle logica)
|
||||
├── bin/ dunne launchers → /usr/lib/daynivo-kiosk/
|
||||
├── src/daynivo_kiosk/ Python-package (alle logica)
|
||||
│ ├── config.py laden/valideren/atomair opslaan
|
||||
│ ├── validation.py gedeelde invoervalidatie
|
||||
│ ├── cdp.py Chrome DevTools Protocol-client
|
||||
@@ -197,7 +197,7 @@ daynivo-kiosk-os/
|
||||
├── www/ lokale kioskpagina's (start, offline, setup)
|
||||
├── systemd/ unit-bestanden
|
||||
├── config/ standaardconfig, sudoers, journald, logrotate, …
|
||||
├── image/ pi-gen image-builder (stage-voxip)
|
||||
├── image/ pi-gen image-builder (stage-daynivo)
|
||||
├── tests/ pytest-suite
|
||||
└── docs/ architectuur, hardwaretest
|
||||
```
|
||||
@@ -20,7 +20,7 @@ Noteer per stap: ✅ / ❌ + opmerking, de imageversie en het schermtype.
|
||||
- [ ] Binnen 90 s na stroom aan verschijnt het setupscherm met de setupcode,
|
||||
`http://kiosk.local:8080` en het IP-adres. Er is geen muiscursor en
|
||||
er zijn geen dialoogvensters.
|
||||
- [ ] SSH is uit (`ssh voxipadmin@kiosk.local` wordt geweigerd), en er is
|
||||
- [ ] SSH is uit (`ssh daynivoadmin@kiosk.local` wordt geweigerd), en er is
|
||||
geen bruikbaar standaardwachtwoord.
|
||||
|
||||
## 2. Initiële configuratie
|
||||
@@ -47,7 +47,7 @@ Noteer per stap: ✅ / ❌ + opmerking, de imageversie en het schermtype.
|
||||
(zie Logboek → Kiosk-agent).
|
||||
- [ ] Screenshot op de Kiosk-pagina toont wat er op het scherm staat.
|
||||
- [ ] Daynivo-URL met `?token=`: het token is overal gemaskeerd
|
||||
(dashboard, `daynivo-kiosk-ctl status`, `journalctl -u voxip-kiosk-agent`).
|
||||
(dashboard, `daynivo-kiosk-ctl status`, `journalctl -u daynivo-kiosk-agent`).
|
||||
|
||||
## 4. Herstel
|
||||
|
||||
@@ -61,9 +61,9 @@ Noteer per stap: ✅ / ❌ + opmerking, de imageversie en het schermtype.
|
||||
Zet de URL daarna terug.
|
||||
- [ ] Vastgelopen pagina: open via SSH-tunnel DevTools of zet een testpagina met
|
||||
`while(true){}` in. Na ±15 s herlaadt de agent, na ±30 s herstart Chromium.
|
||||
- [ ] `sudo systemctl stop voxip-kiosk-agent`: de healthcheck start hem binnen 2 minuten.
|
||||
- [ ] `sudo systemctl stop daynivo-kiosk-agent`: de healthcheck start hem binnen 2 minuten.
|
||||
- [ ] De webinterface blijft werken terwijl Chromium gestopt is
|
||||
(`sudo systemctl stop voxip-kiosk-browser`).
|
||||
(`sudo systemctl stop daynivo-kiosk-browser`).
|
||||
|
||||
## 5. Netwerk
|
||||
|
||||
@@ -123,11 +123,11 @@ Noteer per stap: ✅ / ❌ + opmerking, de imageversie en het schermtype.
|
||||
op Gitea: "beschikbaar". *Bijwerken naar …*: binnen enkele minuten is de
|
||||
nieuwe versie actief, de beheerinterface komt terug, configuratie en
|
||||
wachtwoord ongewijzigd.
|
||||
- [ ] *Automatisch bijwerken* aan, `sudo systemctl start voxip-kiosk-update.service`:
|
||||
- [ ] *Automatisch bijwerken* aan, `sudo systemctl start daynivo-kiosk-update.service`:
|
||||
de nieuwere release wordt geïnstalleerd. Met het vinkje uit: alleen gecontroleerd.
|
||||
- [ ] `curl -fsSL https://daynivo.nl/kiosk/install.sh | sudo bash` op een verse
|
||||
Raspberry Pi OS Lite: installeert de nieuwste release, setupcode op het scherm.
|
||||
- [ ] `sudo ./uninstall.sh --purge` op een testkaart: geen Daynivo-services of
|
||||
-bestanden meer (`systemctl list-unit-files | grep voxip` is leeg).
|
||||
-bestanden meer (`systemctl list-unit-files | grep daynivo` is leeg).
|
||||
- [ ] Installatie op een bestaande Raspberry Pi OS Lite (Bookworm) met de
|
||||
Daynivo-kiosk erop: URL overgenomen, `display-kiosk.service` uitgeschakeld.
|
||||
@@ -8,7 +8,7 @@ een fout; de reden staat dan in daynivo-config.error.txt).
|
||||
|
||||
daynivo-config.json
|
||||
Instellingen voor deze kiosk. Zie daynivo-config.example.json. Toegestaan:
|
||||
alle sleutels uit /etc/voxip-kiosk/config.json plus "hostname" en
|
||||
alle sleutels uit /etc/daynivo-kiosk/config.json plus "hostname" en
|
||||
"wifi_country". Voorbeeld:
|
||||
|
||||
{
|
||||
@@ -20,7 +20,7 @@ daynivo-config.json
|
||||
|
||||
daynivo-authorized_keys
|
||||
Openbare SSH-sleutels (één per regel). SSH wordt dan ingeschakeld, alleen
|
||||
met sleutel-login, voor de beheerder (voxipadmin of de gebruiker die je
|
||||
met sleutel-login, voor de beheerder (daynivoadmin of de gebruiker die je
|
||||
in Raspberry Pi Imager hebt aangemaakt).
|
||||
|
||||
daynivo-network-reset
|
||||
|
||||
+14
-13
@@ -8,8 +8,8 @@
|
||||
# Environment (all optional):
|
||||
# PI_GEN_REF pi-gen tag/branch (32-bit armhf). Default: pinned Bookworm release.
|
||||
# RELEASE bookworm | trixie (must match PI_GEN_REF). Default: bookworm.
|
||||
# VOXIP_DEFAULT_URL kiosk URL baked into the default config.
|
||||
# VOXIP_DEVICE_NAME default device name.
|
||||
# DAYNIVO_DEFAULT_URL kiosk URL baked into the default config.
|
||||
# DAYNIVO_DEVICE_NAME default device name.
|
||||
# TARGET_HOSTNAME default hostname (the admin UI is at http://<hostname>.local:8080).
|
||||
# ALLOW_DIRTY=1 build from the working tree instead of the committed HEAD.
|
||||
#
|
||||
@@ -23,8 +23,9 @@ OUT="$ROOT/image/deploy"
|
||||
PI_GEN_REPO="${PI_GEN_REPO:-https://github.com/RPi-Distro/pi-gen.git}"
|
||||
PI_GEN_REF="${PI_GEN_REF:-2024-11-19-raspios-bookworm-armhf}"
|
||||
RELEASE="${RELEASE:-bookworm}"
|
||||
VOXIP_DEFAULT_URL="${VOXIP_DEFAULT_URL:-https://schermen-ijsselflat.voxip.nl}"
|
||||
VOXIP_DEVICE_NAME="${VOXIP_DEVICE_NAME:-Daynivo Kiosk}"
|
||||
# VOXIP_* are the names from before the rename and still work.
|
||||
DAYNIVO_DEFAULT_URL="${DAYNIVO_DEFAULT_URL:-${VOXIP_DEFAULT_URL:-https://schermen-ijsselflat.voxip.nl}}"
|
||||
DAYNIVO_DEVICE_NAME="${DAYNIVO_DEVICE_NAME:-${VOXIP_DEVICE_NAME:-Daynivo Kiosk}}"
|
||||
TARGET_HOSTNAME="${TARGET_HOSTNAME:-kiosk}"
|
||||
NATIVE=0
|
||||
[[ ${1:-} == "--native" ]] && NATIVE=1
|
||||
@@ -45,13 +46,13 @@ git clone --quiet --depth 1 --branch "$PI_GEN_REF" "$PI_GEN_REPO" "$WORK/pi-gen"
|
||||
PI_GEN_COMMIT="$(git -C "$WORK/pi-gen" rev-parse HEAD)"
|
||||
PIGEN="$WORK/pi-gen"
|
||||
|
||||
# Do not export the plain Lite image from stage2; ours is exported after stage-voxip.
|
||||
# Do not export the plain Lite image from stage2; ours is exported after stage-daynivo.
|
||||
touch "$PIGEN/stage2/SKIP_IMAGES"
|
||||
rm -f "$PIGEN/stage2/EXPORT_NOOBS"
|
||||
|
||||
echo "==> Daynivo-stage voorbereiden"
|
||||
cp -a "$ROOT/image/stage-voxip" "$PIGEN/stage-voxip"
|
||||
PAYLOAD="$PIGEN/stage-voxip/00-voxip/files/voxip-kiosk"
|
||||
cp -a "$ROOT/image/stage-daynivo" "$PIGEN/stage-daynivo"
|
||||
PAYLOAD="$PIGEN/stage-daynivo/00-daynivo/files/daynivo-kiosk"
|
||||
mkdir -p "$PAYLOAD"
|
||||
if [[ ${ALLOW_DIRTY:-0} == 1 ]] || ! git -C "$ROOT" rev-parse --git-dir >/dev/null 2>&1; then
|
||||
echo " (werkmap wordt gebruikt, niet de laatste commit)"
|
||||
@@ -67,12 +68,12 @@ else
|
||||
fi
|
||||
|
||||
BUILD_DATE="$(date -u +%Y-%m-%d)"
|
||||
cat > "$PIGEN/stage-voxip/00-voxip/files/build.env" <<EOF
|
||||
VOXIP_DEFAULT_URL=$(printf %q "$VOXIP_DEFAULT_URL")
|
||||
VOXIP_DEVICE_NAME=$(printf %q "$VOXIP_DEVICE_NAME")
|
||||
cat > "$PIGEN/stage-daynivo/00-daynivo/files/build.env" <<EOF
|
||||
DAYNIVO_DEFAULT_URL=$(printf %q "$DAYNIVO_DEFAULT_URL")
|
||||
DAYNIVO_DEVICE_NAME=$(printf %q "$DAYNIVO_DEVICE_NAME")
|
||||
EOF
|
||||
cat > "$PAYLOAD/BUILD-INFO" <<EOF
|
||||
voxip_kiosk_os=$VERSION
|
||||
daynivo_kiosk_os=$VERSION
|
||||
source_revision=$SOURCE_REV
|
||||
pi_gen_ref=$PI_GEN_REF
|
||||
pi_gen_commit=$PI_GEN_COMMIT
|
||||
@@ -97,11 +98,11 @@ KEYBOARD_LAYOUT="English (US)"
|
||||
TIMEZONE_DEFAULT=Europe/Amsterdam
|
||||
WPA_COUNTRY=NL
|
||||
TARGET_HOSTNAME=$TARGET_HOSTNAME
|
||||
FIRST_USER_NAME=voxipadmin
|
||||
FIRST_USER_NAME=daynivoadmin
|
||||
FIRST_USER_PASS=$FIRST_USER_PASS
|
||||
DISABLE_FIRST_BOOT_USER_RENAME=1
|
||||
ENABLE_SSH=0
|
||||
STAGE_LIST="stage0 stage1 stage2 stage-voxip"
|
||||
STAGE_LIST="stage0 stage1 stage2 stage-daynivo"
|
||||
EOF
|
||||
|
||||
echo "==> Image bouwen (dit duurt 30–90 minuten)"
|
||||
|
||||
Executable
+23
@@ -0,0 +1,23 @@
|
||||
#!/bin/bash -e
|
||||
# Install Daynivo Kiosk OS into the image with the same install.sh that is
|
||||
# used on existing Raspberry Pi OS installations.
|
||||
|
||||
# shellcheck disable=SC1091
|
||||
source files/build.env
|
||||
|
||||
rm -rf "${ROOTFS_DIR}/usr/src/daynivo-kiosk"
|
||||
install -d "${ROOTFS_DIR}/usr/src/daynivo-kiosk"
|
||||
cp -a files/daynivo-kiosk/. "${ROOTFS_DIR}/usr/src/daynivo-kiosk/"
|
||||
|
||||
on_chroot << EOF_CHROOT
|
||||
/usr/src/daynivo-kiosk/install.sh --image --url $(printf %q "${DAYNIVO_DEFAULT_URL}") --name $(printf %q "${DAYNIVO_DEVICE_NAME}")
|
||||
install -m 0644 /usr/src/daynivo-kiosk/BUILD-INFO /usr/share/daynivo-kiosk/BUILD-INFO
|
||||
# No usable default password: unlocked only by Raspberry Pi Imager credentials.
|
||||
passwd -l ${FIRST_USER_NAME}
|
||||
systemctl disable ssh.service 2>/dev/null || true
|
||||
apt-get clean
|
||||
EOF_CHROOT
|
||||
|
||||
# Instructions next to config.txt for whoever opens the boot partition.
|
||||
install -m 0644 files/daynivo-kiosk/image/boot-README.txt "${ROOTFS_DIR}/boot/firmware/DAYNIVO-LEESMIJ.txt"
|
||||
install -m 0644 files/daynivo-kiosk/image/daynivo-config.example.json "${ROOTFS_DIR}/boot/firmware/daynivo-config.example.json"
|
||||
File renamed without changes.
File renamed without changes.
@@ -1,23 +0,0 @@
|
||||
#!/bin/bash -e
|
||||
# Install Daynivo Kiosk OS into the image with the same install.sh that is
|
||||
# used on existing Raspberry Pi OS installations.
|
||||
|
||||
# shellcheck disable=SC1091
|
||||
source files/build.env
|
||||
|
||||
rm -rf "${ROOTFS_DIR}/usr/src/voxip-kiosk"
|
||||
install -d "${ROOTFS_DIR}/usr/src/voxip-kiosk"
|
||||
cp -a files/voxip-kiosk/. "${ROOTFS_DIR}/usr/src/voxip-kiosk/"
|
||||
|
||||
on_chroot << EOF_CHROOT
|
||||
/usr/src/voxip-kiosk/install.sh --image --url $(printf %q "${VOXIP_DEFAULT_URL}") --name $(printf %q "${VOXIP_DEVICE_NAME}")
|
||||
install -m 0644 /usr/src/voxip-kiosk/BUILD-INFO /usr/share/voxip-kiosk/BUILD-INFO
|
||||
# No usable default password: unlocked only by Raspberry Pi Imager credentials.
|
||||
passwd -l ${FIRST_USER_NAME}
|
||||
systemctl disable ssh.service 2>/dev/null || true
|
||||
apt-get clean
|
||||
EOF_CHROOT
|
||||
|
||||
# Instructions next to config.txt for whoever opens the boot partition.
|
||||
install -m 0644 files/voxip-kiosk/image/boot-README.txt "${ROOTFS_DIR}/boot/firmware/VOXIP-LEESMIJ.txt"
|
||||
install -m 0644 files/voxip-kiosk/image/daynivo-config.example.json "${ROOTFS_DIR}/boot/firmware/daynivo-config.example.json"
|
||||
+125
-56
@@ -8,9 +8,9 @@
|
||||
#
|
||||
# sudo ./install.sh [--url https://…] [--name "Hal begane grond"] [--hostname hal-bg]
|
||||
# sudo ./install.sh --upgrade # update code, keep configuration
|
||||
# ./install.sh --image … # used by image/stage-voxip (no service starts)
|
||||
# ./install.sh --image … # used by image/stage-daynivo (no service starts)
|
||||
#
|
||||
# Existing configuration in /etc/voxip-kiosk is never overwritten.
|
||||
# Existing configuration in /etc/daynivo-kiosk is never overwritten.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
@@ -21,15 +21,15 @@ NAME=""
|
||||
NEW_HOSTNAME=""
|
||||
FORCE=0
|
||||
|
||||
LIB=/usr/lib/voxip-kiosk
|
||||
SHARE=/usr/share/voxip-kiosk
|
||||
DOC=/usr/share/doc/voxip-kiosk
|
||||
ETC=/etc/voxip-kiosk
|
||||
UNITS=(voxip-kiosk-x.service voxip-kiosk-browser.service voxip-kiosk-agent.service voxip-kiosk-manager.service
|
||||
voxip-kiosk-healthcheck.service voxip-kiosk-healthcheck.timer voxip-network-guard.service voxip-boot-import.service
|
||||
voxip-kiosk-update.service voxip-kiosk-update.timer)
|
||||
ENABLE=(voxip-kiosk-x.service voxip-kiosk-browser.service voxip-kiosk-agent.service voxip-kiosk-manager.service
|
||||
voxip-kiosk-healthcheck.timer voxip-network-guard.service voxip-boot-import.service voxip-kiosk-update.timer)
|
||||
LIB=/usr/lib/daynivo-kiosk
|
||||
SHARE=/usr/share/daynivo-kiosk
|
||||
DOC=/usr/share/doc/daynivo-kiosk
|
||||
ETC=/etc/daynivo-kiosk
|
||||
UNITS=(daynivo-kiosk-x.service daynivo-kiosk-browser.service daynivo-kiosk-agent.service daynivo-kiosk-manager.service
|
||||
daynivo-kiosk-healthcheck.service daynivo-kiosk-healthcheck.timer daynivo-network-guard.service daynivo-boot-import.service
|
||||
daynivo-kiosk-update.service daynivo-kiosk-update.timer)
|
||||
ENABLE=(daynivo-kiosk-x.service daynivo-kiosk-browser.service daynivo-kiosk-agent.service daynivo-kiosk-manager.service
|
||||
daynivo-kiosk-healthcheck.timer daynivo-network-guard.service daynivo-boot-import.service daynivo-kiosk-update.timer)
|
||||
|
||||
say() { printf '\033[1;34m==>\033[0m %s\n' "$*"; }
|
||||
warn() { printf '\033[1;33mLet op:\033[0m %s\n' "$*" >&2; }
|
||||
@@ -54,7 +54,7 @@ while [[ $# -gt 0 ]]; do
|
||||
done
|
||||
|
||||
[[ $EUID -eq 0 ]] || die "Voer dit script uit met sudo."
|
||||
[[ -f "$SRC/src/voxip_kiosk/config.py" ]] || die "Start install.sh vanuit de Daynivo Kiosk OS-repository."
|
||||
[[ -f "$SRC/src/daynivo_kiosk/config.py" ]] || die "Start install.sh vanuit de Daynivo Kiosk OS-repository."
|
||||
|
||||
in_chroot() {
|
||||
if command -v systemd-detect-virt >/dev/null 2>&1 && systemd-detect-virt --chroot --quiet; then
|
||||
@@ -106,6 +106,75 @@ pkg_available() { apt-cache policy "$1" 2>/dev/null | grep -q 'Candidate: [0-9]'
|
||||
apt-get install -y --no-install-recommends "${PKGS[@]}"
|
||||
}
|
||||
|
||||
# --- migration from the VoxIP names ---------------------------------------------------
|
||||
# Kiosks installed before the rename (up to 1.2.x) use voxip-* services and folders
|
||||
# and a "voxip" user. Move them over once, keeping the configuration, the password,
|
||||
# the browser profile (offline cache) and the network settings. The old folder
|
||||
# names stay as symlinks for whatever still runs from the old version, such as
|
||||
# the updater that started this upgrade.
|
||||
if [[ -d /etc/voxip-kiosk && ! -L /etc/voxip-kiosk && ! -e /etc/daynivo-kiosk ]]; then
|
||||
say "Overstappen van de VoxIP-namen naar Daynivo"
|
||||
if [[ $LIVE -eq 1 ]]; then
|
||||
# Not voxip-kiosk-update.service: a nightly auto-update runs this very script in it.
|
||||
systemctl stop voxip-kiosk-healthcheck.timer voxip-kiosk-update.timer voxip-kiosk-agent.service \
|
||||
voxip-kiosk-browser.service voxip-kiosk-x.service voxip-kiosk-manager.service 2>/dev/null || true
|
||||
fi
|
||||
for unit in voxip-kiosk-x.service voxip-kiosk-browser.service voxip-kiosk-agent.service voxip-kiosk-manager.service \
|
||||
voxip-kiosk-healthcheck.service voxip-kiosk-healthcheck.timer voxip-kiosk-update.service \
|
||||
voxip-kiosk-update.timer voxip-network-guard.service voxip-boot-import.service; do
|
||||
systemctl disable "$unit" >/dev/null 2>&1 || true
|
||||
rm -f "/etc/systemd/system/$unit"
|
||||
done
|
||||
if [[ $LIVE -eq 1 ]]; then systemctl daemon-reload; fi
|
||||
|
||||
for old in /etc/voxip-kiosk /var/lib/voxip-kiosk /var/lib/voxip-kiosk-network /var/lib/voxip-kiosk-browser \
|
||||
/var/log/voxip-kiosk /var/cache/voxip-kiosk; do
|
||||
new="${old/voxip/daynivo}"
|
||||
if [[ -d $old && ! -L $old && ! -e $new ]]; then
|
||||
mv "$old" "$new"
|
||||
ln -s "$new" "$old"
|
||||
fi
|
||||
done
|
||||
|
||||
# Same uid and gid, so every file keeps the right owner.
|
||||
if id voxip >/dev/null 2>&1 && ! id daynivo >/dev/null 2>&1; then
|
||||
usermod -l daynivo -d /var/lib/daynivo-kiosk voxip
|
||||
if getent group voxip >/dev/null; then groupmod -n daynivo voxip; fi
|
||||
fi
|
||||
|
||||
# Network profiles written by the web interface; not while a change awaits confirmation.
|
||||
if [[ ! -f /var/lib/daynivo-kiosk-network/pending.json ]]; then
|
||||
renamed=0
|
||||
for iface in eth0 wlan0; do
|
||||
old=/etc/NetworkManager/system-connections/voxip-$iface.nmconnection
|
||||
new=/etc/NetworkManager/system-connections/daynivo-$iface.nmconnection
|
||||
if [[ -f $old && ! -e $new ]]; then
|
||||
sed -i "s/^id=voxip-$iface\$/id=daynivo-$iface/" "$old"
|
||||
mv "$old" "$new"
|
||||
chmod 0600 "$new"
|
||||
renamed=1
|
||||
fi
|
||||
done
|
||||
if [[ $renamed -eq 1 && $LIVE -eq 1 ]]; then nmcli connection reload 2>/dev/null || true; fi
|
||||
fi
|
||||
|
||||
rm -f /etc/sudoers.d/voxip-kiosk /etc/systemd/journald.conf.d/voxip-kiosk.conf \
|
||||
/etc/systemd/system.conf.d/voxip-watchdog.conf /etc/logrotate.d/voxip-kiosk \
|
||||
/etc/avahi/services/voxip-kiosk.service /etc/chromium/policies/managed/voxip-kiosk.json \
|
||||
/etc/chromium-browser/policies/managed/voxip-kiosk.json /usr/local/sbin/voxip-kiosk-ctl
|
||||
if [[ -f /etc/ssh/sshd_config.d/voxip-kiosk.conf ]]; then
|
||||
mv /etc/ssh/sshd_config.d/voxip-kiosk.conf /etc/ssh/sshd_config.d/daynivo-kiosk.conf
|
||||
fi
|
||||
if [[ -f /etc/X11/Xwrapper.config.voxip-orig ]]; then
|
||||
mv /etc/X11/Xwrapper.config.voxip-orig /etc/X11/Xwrapper.config.daynivo-orig
|
||||
fi
|
||||
if [[ -f /etc/systemd/system/display-kiosk.service.voxip-disabled ]]; then
|
||||
mv /etc/systemd/system/display-kiosk.service.voxip-disabled /etc/systemd/system/display-kiosk.service.daynivo-disabled
|
||||
fi
|
||||
# The old programs: an updater that started this install has its code loaded already.
|
||||
rm -rf /usr/lib/voxip-kiosk /usr/share/voxip-kiosk /usr/share/doc/voxip-kiosk /usr/share/plymouth/themes/voxip-kiosk
|
||||
fi
|
||||
|
||||
# --- users ----------------------------------------------------------------------
|
||||
say "Gebruikers aanmaken"
|
||||
getent group kiosk >/dev/null || groupadd --system kiosk
|
||||
@@ -114,11 +183,11 @@ id kiosk >/dev/null 2>&1 || useradd --system --gid kiosk --home-dir /home/kiosk
|
||||
for g in video render input audio; do
|
||||
getent group "$g" >/dev/null && usermod -aG "$g" kiosk
|
||||
done
|
||||
getent group voxip >/dev/null || groupadd --system voxip
|
||||
id voxip >/dev/null 2>&1 || useradd --system --gid voxip --home-dir /var/lib/voxip-kiosk --no-create-home \
|
||||
--shell /usr/sbin/nologin --comment "Daynivo Kiosk Manager" voxip
|
||||
getent group daynivo >/dev/null || groupadd --system daynivo
|
||||
id daynivo >/dev/null 2>&1 || useradd --system --gid daynivo --home-dir /var/lib/daynivo-kiosk --no-create-home \
|
||||
--shell /usr/sbin/nologin --comment "Daynivo Kiosk Manager" daynivo
|
||||
for g in systemd-journal video; do
|
||||
getent group "$g" >/dev/null && usermod -aG "$g" voxip
|
||||
getent group "$g" >/dev/null && usermod -aG "$g" daynivo
|
||||
done
|
||||
|
||||
# --- application files --------------------------------------------------------------
|
||||
@@ -126,7 +195,7 @@ say "Programmabestanden installeren"
|
||||
install -d -m 0755 "$LIB" "$SHARE" "$DOC"
|
||||
rm -rf "$LIB/python.new"
|
||||
install -d -m 0755 "$LIB/python.new"
|
||||
tar -C "$SRC/src" --exclude='__pycache__' --exclude='*.pyc' -cf - voxip_kiosk | tar -C "$LIB/python.new" -xf -
|
||||
tar -C "$SRC/src" --exclude='__pycache__' --exclude='*.pyc' -cf - daynivo_kiosk | tar -C "$LIB/python.new" -xf -
|
||||
chown -R root:root "$LIB/python.new"
|
||||
chmod -R u=rwX,go=rX "$LIB/python.new"
|
||||
python3 -m compileall -q "$LIB/python.new" >/dev/null
|
||||
@@ -135,7 +204,7 @@ rm -rf "$LIB/python.old"
|
||||
mv "$LIB/python.new" "$LIB/python"
|
||||
rm -rf "$LIB/python.old"
|
||||
|
||||
for bin in voxip-kiosk-agent voxip-kiosk-browser voxip-kiosk-healthcheck voxip-kiosk-helper voxip-kiosk-manager voxip-kiosk-updater voxip-kiosk-x; do
|
||||
for bin in daynivo-kiosk-agent daynivo-kiosk-browser daynivo-kiosk-healthcheck daynivo-kiosk-helper daynivo-kiosk-manager daynivo-kiosk-updater daynivo-kiosk-x; do
|
||||
install -m 0755 -o root -g root "$SRC/bin/$bin" "$LIB/$bin"
|
||||
done
|
||||
install -m 0755 -o root -g root "$SRC/bin/daynivo-kiosk-ctl" /usr/local/sbin/daynivo-kiosk-ctl
|
||||
@@ -151,10 +220,10 @@ install -m 0644 "$SRC"/docs/*.md "$DOC/"
|
||||
|
||||
# --- configuration -------------------------------------------------------------------
|
||||
say "Configuratie"
|
||||
install -d -o voxip -g kiosk -m 2750 "$ETC"
|
||||
install -d -o voxip -g voxip -m 0750 /var/lib/voxip-kiosk
|
||||
install -d -o root -g root -m 0755 /var/lib/voxip-kiosk-network
|
||||
install -d -o voxip -g voxip -m 0750 /var/log/voxip-kiosk
|
||||
install -d -o daynivo -g kiosk -m 2750 "$ETC"
|
||||
install -d -o daynivo -g daynivo -m 0750 /var/lib/daynivo-kiosk
|
||||
install -d -o root -g root -m 0755 /var/lib/daynivo-kiosk-network
|
||||
install -d -o daynivo -g daynivo -m 0750 /var/log/daynivo-kiosk
|
||||
|
||||
# Migrate from Daynivo's raspberry-pi/install-kiosk.sh: take over its URL, then disable it.
|
||||
LEGACY_KIOSK_UNIT=/etc/systemd/system/display-kiosk.service
|
||||
@@ -165,14 +234,14 @@ if [[ -f $LEGACY_KIOSK_UNIT ]]; then
|
||||
say "URL overgenomen van de Daynivo-kioskinstallatie"
|
||||
fi
|
||||
if [[ $LIVE -eq 1 ]]; then systemctl disable --now display-kiosk.service 2>/dev/null || true; fi
|
||||
mv "$LEGACY_KIOSK_UNIT" "$LEGACY_KIOSK_UNIT.voxip-disabled"
|
||||
mv "$LEGACY_KIOSK_UNIT" "$LEGACY_KIOSK_UNIT.daynivo-disabled"
|
||||
fi
|
||||
|
||||
PYRUN=(python3 -I -c)
|
||||
"${PYRUN[@]}" '
|
||||
import json, os, sys
|
||||
sys.path.insert(0, "/usr/lib/voxip-kiosk/python")
|
||||
from voxip_kiosk import config as c
|
||||
sys.path.insert(0, "/usr/lib/daynivo-kiosk/python")
|
||||
from daynivo_kiosk import config as c
|
||||
path, default, url, name = sys.argv[1:5]
|
||||
updates = {}
|
||||
if url: updates["url"] = url
|
||||
@@ -194,36 +263,36 @@ else:
|
||||
c.atomic_write_json(c.Path(path), data)
|
||||
print("Nieuwe configuratie aangemaakt")
|
||||
' "$ETC/config.json" "$SRC/config/config.default.json" "$URL" "$NAME" || die "Ongeldige --url of --name."
|
||||
chown voxip:kiosk "$ETC/config.json" "$ETC/config.json.bak" 2>/dev/null || true
|
||||
chown daynivo:kiosk "$ETC/config.json" "$ETC/config.json.bak" 2>/dev/null || true
|
||||
chmod 0640 "$ETC/config.json"
|
||||
touch "$ETC/.config.lock" && chown voxip:kiosk "$ETC/.config.lock" && chmod 0640 "$ETC/.config.lock"
|
||||
[[ -f $ETC/admin.json ]] && chown voxip:voxip "$ETC/admin.json" && chmod 0600 "$ETC/admin.json"
|
||||
touch "$ETC/.config.lock" && chown daynivo:kiosk "$ETC/.config.lock" && chmod 0640 "$ETC/.config.lock"
|
||||
[[ -f $ETC/admin.json ]] && chown daynivo:daynivo "$ETC/admin.json" && chmod 0600 "$ETC/admin.json"
|
||||
|
||||
# --- system integration ----------------------------------------------------------------
|
||||
say "Systeemintegratie"
|
||||
TMP_SUDOERS="$(mktemp)"
|
||||
install -m 0440 "$SRC/config/sudoers" "$TMP_SUDOERS"
|
||||
visudo -cqf "$TMP_SUDOERS" || die "sudoers-bestand ongeldig"
|
||||
mv "$TMP_SUDOERS" /etc/sudoers.d/voxip-kiosk
|
||||
chown root:root /etc/sudoers.d/voxip-kiosk
|
||||
mv "$TMP_SUDOERS" /etc/sudoers.d/daynivo-kiosk
|
||||
chown root:root /etc/sudoers.d/daynivo-kiosk
|
||||
|
||||
install -D -m 0644 "$SRC/config/journald.conf" /etc/systemd/journald.conf.d/voxip-kiosk.conf
|
||||
install -D -m 0644 "$SRC/config/watchdog.conf" /etc/systemd/system.conf.d/voxip-watchdog.conf
|
||||
install -D -m 0644 "$SRC/config/logrotate" /etc/logrotate.d/voxip-kiosk
|
||||
install -D -m 0644 "$SRC/config/journald.conf" /etc/systemd/journald.conf.d/daynivo-kiosk.conf
|
||||
install -D -m 0644 "$SRC/config/watchdog.conf" /etc/systemd/system.conf.d/daynivo-watchdog.conf
|
||||
install -D -m 0644 "$SRC/config/logrotate" /etc/logrotate.d/daynivo-kiosk
|
||||
|
||||
if [[ -f /etc/X11/Xwrapper.config && ! -f /etc/X11/Xwrapper.config.voxip-orig ]]; then
|
||||
cp -a /etc/X11/Xwrapper.config /etc/X11/Xwrapper.config.voxip-orig
|
||||
if [[ -f /etc/X11/Xwrapper.config && ! -f /etc/X11/Xwrapper.config.daynivo-orig ]]; then
|
||||
cp -a /etc/X11/Xwrapper.config /etc/X11/Xwrapper.config.daynivo-orig
|
||||
fi
|
||||
install -D -m 0644 "$SRC/config/Xwrapper.config" /etc/X11/Xwrapper.config
|
||||
|
||||
for dir in /etc/chromium/policies/managed /etc/chromium-browser/policies/managed; do
|
||||
install -D -m 0644 "$SRC/config/chromium/voxip-kiosk.json" "$dir/voxip-kiosk.json"
|
||||
install -D -m 0644 "$SRC/config/chromium/daynivo-kiosk.json" "$dir/daynivo-kiosk.json"
|
||||
done
|
||||
|
||||
ADMIN_PORT="$(python3 -I -c 'import sys; sys.path.insert(0, "/usr/lib/voxip-kiosk/python"); from voxip_kiosk.config import load_config; print(load_config(__import__("pathlib").Path(sys.argv[1]))["admin_port"])' "$ETC/config.json")"
|
||||
ADMIN_PORT="$(python3 -I -c 'import sys; sys.path.insert(0, "/usr/lib/daynivo-kiosk/python"); from daynivo_kiosk.config import load_config; print(load_config(__import__("pathlib").Path(sys.argv[1]))["admin_port"])' "$ETC/config.json")"
|
||||
install -d -m 0755 /etc/avahi/services
|
||||
sed "s#<port>8080</port>#<port>${ADMIN_PORT}</port>#" "$SRC/config/avahi-voxip.service" > /etc/avahi/services/voxip-kiosk.service
|
||||
chmod 0644 /etc/avahi/services/voxip-kiosk.service
|
||||
sed "s#<port>8080</port>#<port>${ADMIN_PORT}</port>#" "$SRC/config/avahi-daynivo.service" > /etc/avahi/services/daynivo-kiosk.service
|
||||
chmod 0644 /etc/avahi/services/daynivo-kiosk.service
|
||||
|
||||
# Swap: zram in RAM instead of a swap file on the SD card (Trixie's rpi-swap already does this).
|
||||
if dpkg -s systemd-zram-generator >/dev/null 2>&1 && ! dpkg -s rpi-swap >/dev/null 2>&1; then
|
||||
@@ -263,18 +332,18 @@ done
|
||||
|
||||
# Boot splash: logo on white (config/plymouth). Rebuilding the initramfs takes a while
|
||||
# on a Pi 3, so only when the theme changed.
|
||||
SPLASH_DIR=/usr/share/plymouth/themes/voxip-kiosk
|
||||
SPLASH_DIR=/usr/share/plymouth/themes/daynivo-kiosk
|
||||
install -d -m 0755 "$SPLASH_DIR"
|
||||
install -m 0644 "$SRC"/config/plymouth/voxip-kiosk/* "$SPLASH_DIR/"
|
||||
if [[ ! -f /var/lib/voxip-kiosk/previous-plymouth-theme ]]; then
|
||||
plymouth-set-default-theme > /var/lib/voxip-kiosk/previous-plymouth-theme 2>/dev/null || true
|
||||
install -m 0644 "$SRC"/config/plymouth/daynivo-kiosk/* "$SPLASH_DIR/"
|
||||
if [[ ! -f /var/lib/daynivo-kiosk/previous-plymouth-theme ]]; then
|
||||
plymouth-set-default-theme > /var/lib/daynivo-kiosk/previous-plymouth-theme 2>/dev/null || true
|
||||
fi
|
||||
SPLASH_SUM="$(cat "$SPLASH_DIR"/* | sha256sum | cut -d' ' -f1)"
|
||||
if [[ "$(plymouth-set-default-theme 2>/dev/null)" != voxip-kiosk || "$(cat /var/lib/voxip-kiosk/splash.sha256 2>/dev/null)" != "$SPLASH_SUM" ]]; then
|
||||
if [[ "$(plymouth-set-default-theme 2>/dev/null)" != daynivo-kiosk || "$(cat /var/lib/daynivo-kiosk/splash.sha256 2>/dev/null)" != "$SPLASH_SUM" ]]; then
|
||||
say "Opstartscherm instellen (initramfs bijwerken, dit duurt even)"
|
||||
plymouth-set-default-theme voxip-kiosk
|
||||
plymouth-set-default-theme daynivo-kiosk
|
||||
if update-initramfs -u -k all >/dev/null 2>&1; then
|
||||
echo "$SPLASH_SUM" > /var/lib/voxip-kiosk/splash.sha256
|
||||
echo "$SPLASH_SUM" > /var/lib/daynivo-kiosk/splash.sha256
|
||||
else
|
||||
warn "initramfs bijwerken mislukt; het opstartscherm verschijnt mogelijk pas later tijdens het opstarten."
|
||||
fi
|
||||
@@ -286,8 +355,8 @@ for dm in lightdm.service gdm3.service sddm.service; do
|
||||
if systemctl is-enabled --quiet "$dm" 2>/dev/null; then
|
||||
warn "$dm wordt uitgeschakeld (de kiosk gebruikt een eigen minimale X-sessie)."
|
||||
systemctl disable "$dm" 2>/dev/null || true
|
||||
grep -qx "$dm" /var/lib/voxip-kiosk/disabled-display-managers 2>/dev/null \
|
||||
|| echo "$dm" >> /var/lib/voxip-kiosk/disabled-display-managers
|
||||
grep -qx "$dm" /var/lib/daynivo-kiosk/disabled-display-managers 2>/dev/null \
|
||||
|| echo "$dm" >> /var/lib/daynivo-kiosk/disabled-display-managers
|
||||
fi
|
||||
if [[ $LIVE -eq 1 ]] && systemctl is-active --quiet "$dm" 2>/dev/null; then
|
||||
warn "$dm wordt gestopt: de desktop op het scherm sluit nu af."
|
||||
@@ -302,15 +371,15 @@ for unit in "${UNITS[@]}"; do
|
||||
done
|
||||
if [[ $LIVE -eq 1 ]]; then systemctl daemon-reload; fi
|
||||
systemctl enable "${ENABLE[@]}" NetworkManager.service avahi-daemon.service >/dev/null
|
||||
if [[ ! -f /var/lib/voxip-kiosk/previous-default-target ]]; then
|
||||
systemctl get-default > /var/lib/voxip-kiosk/previous-default-target 2>/dev/null || true
|
||||
if [[ ! -f /var/lib/daynivo-kiosk/previous-default-target ]]; then
|
||||
systemctl get-default > /var/lib/daynivo-kiosk/previous-default-target 2>/dev/null || true
|
||||
fi
|
||||
systemctl set-default multi-user.target >/dev/null
|
||||
|
||||
if [[ -n $NEW_HOSTNAME && $LIVE -eq 0 ]]; then
|
||||
warn "--hostname wordt genegeerd in image-modus (gebruik TARGET_HOSTNAME van pi-gen)."
|
||||
elif [[ -n $NEW_HOSTNAME ]]; then
|
||||
"$LIB/voxip-kiosk-helper" set-hostname "$NEW_HOSTNAME" >/dev/null || die "Ongeldige hostname: $NEW_HOSTNAME"
|
||||
"$LIB/daynivo-kiosk-helper" set-hostname "$NEW_HOSTNAME" >/dev/null || die "Ongeldige hostname: $NEW_HOSTNAME"
|
||||
fi
|
||||
|
||||
if [[ $LIVE -eq 0 ]]; then
|
||||
@@ -321,11 +390,11 @@ fi
|
||||
say "Services starten"
|
||||
systemctl restart systemd-journald.service || true
|
||||
systemctl restart avahi-daemon.service || true
|
||||
systemctl restart voxip-kiosk-manager.service voxip-kiosk-agent.service
|
||||
systemctl start voxip-kiosk-healthcheck.timer voxip-kiosk-update.timer
|
||||
systemctl reset-failed voxip-kiosk-x.service voxip-kiosk-browser.service 2>/dev/null || true
|
||||
systemctl restart voxip-kiosk-x.service
|
||||
systemctl restart voxip-kiosk-browser.service
|
||||
systemctl restart daynivo-kiosk-manager.service daynivo-kiosk-agent.service
|
||||
systemctl start daynivo-kiosk-healthcheck.timer daynivo-kiosk-update.timer
|
||||
systemctl reset-failed daynivo-kiosk-x.service daynivo-kiosk-browser.service 2>/dev/null || true
|
||||
systemctl restart daynivo-kiosk-x.service
|
||||
systemctl restart daynivo-kiosk-browser.service
|
||||
|
||||
HOST="$(hostname)"
|
||||
SCHEME=http
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
[project]
|
||||
name = "voxip-kiosk"
|
||||
name = "daynivo-kiosk"
|
||||
dynamic = ["version"]
|
||||
description = "Daynivo Kiosk OS — kiosk runtime and Daynivo Kiosk Manager"
|
||||
requires-python = ">=3.11"
|
||||
|
||||
File renamed without changes.
@@ -1,4 +1,4 @@
|
||||
"""voxip-kiosk-agent — keeps the kiosk page where it should be.
|
||||
"""daynivo-kiosk-agent — keeps the kiosk page where it should be.
|
||||
|
||||
Runs as user ``kiosk`` and steers Chromium through the DevTools Protocol on
|
||||
127.0.0.1:
|
||||
@@ -33,7 +33,7 @@ from .config import admin_url, load_config
|
||||
from .paths import Paths, load_paths
|
||||
from .validation import mask_url
|
||||
|
||||
log = logging.getLogger("voxip-kiosk-agent")
|
||||
log = logging.getLogger("daynivo-kiosk-agent")
|
||||
|
||||
TICK_SECONDS = 5
|
||||
PROBE_MAX_AGE_LOADING = 10
|
||||
@@ -1,6 +1,6 @@
|
||||
"""voxip-kiosk-browser — prepares the display and runs Chromium.
|
||||
"""daynivo-kiosk-browser — prepares the display and runs Chromium.
|
||||
|
||||
Started by ``voxip-kiosk-browser.service`` as user ``kiosk`` once the X
|
||||
Started by ``daynivo-kiosk-browser.service`` as user ``kiosk`` once the X
|
||||
server is up. Exit status tells systemd whether to restart: 1 when
|
||||
``browser_restart`` is enabled (``Restart=on-failure`` restarts it and the
|
||||
start limit catches crash loops), 0 when it is disabled.
|
||||
@@ -25,7 +25,7 @@ from .config import admin_url, load_config
|
||||
from .paths import Paths, load_paths
|
||||
from .validation import mask_url, parse_resolution
|
||||
|
||||
log = logging.getLogger("voxip-kiosk-browser")
|
||||
log = logging.getLogger("daynivo-kiosk-browser")
|
||||
|
||||
CDP_PORT = 9222
|
||||
CHROMIUM_CANDIDATES = ("/usr/bin/chromium-browser", "/usr/bin/chromium")
|
||||
@@ -212,14 +212,14 @@ def clean_profile(profile_dir: Path) -> None:
|
||||
profile = data.setdefault("profile", {})
|
||||
profile["exit_type"] = "Normal"
|
||||
profile["exited_cleanly"] = True
|
||||
tmp = prefs.with_suffix(".voxip-tmp")
|
||||
tmp = prefs.with_suffix(".daynivo-tmp")
|
||||
tmp.write_text(json.dumps(data), encoding="utf-8")
|
||||
os.replace(tmp, prefs)
|
||||
|
||||
|
||||
def profile_dirs(config: dict[str, Any]) -> tuple[Path, Path]:
|
||||
runtime = Path(os.environ.get("RUNTIME_DIRECTORY", "/run/voxip-kiosk-browser"))
|
||||
state = Path(os.environ.get("STATE_DIRECTORY", "/var/lib/voxip-kiosk-browser"))
|
||||
runtime = Path(os.environ.get("RUNTIME_DIRECTORY", "/run/daynivo-kiosk-browser"))
|
||||
state = Path(os.environ.get("STATE_DIRECTORY", "/var/lib/daynivo-kiosk-browser"))
|
||||
profile = state / "profile" if config.get("browser_profile") == "persistent" else runtime / "profile"
|
||||
if config.get("browser_profile") != "persistent" and (state / "profile").exists():
|
||||
shutil.rmtree(state / "profile", ignore_errors=True)
|
||||
File renamed without changes.
@@ -236,7 +236,7 @@ def save_config(path: Path, updates: dict[str, Any]) -> dict[str, Any]:
|
||||
lock_path = path.with_name(".config.lock")
|
||||
with _thread_lock:
|
||||
# Read-only is enough for flock and works for every writer (the web
|
||||
# interface as voxip, the helper as root), whoever created the file.
|
||||
# interface as daynivo, the helper as root), whoever created the file.
|
||||
lock_fd = os.open(str(lock_path), os.O_RDONLY | os.O_CREAT, 0o640)
|
||||
try:
|
||||
fcntl.flock(lock_fd, fcntl.LOCK_EX)
|
||||
@@ -48,7 +48,7 @@ def cmd_setup_code(_args) -> int:
|
||||
return 1
|
||||
code = setupcode.read(paths.setup_code_file)
|
||||
if not code:
|
||||
print("Nog geen setupcode; start de beheerinterface: sudo systemctl restart voxip-kiosk-manager")
|
||||
print("Nog geen setupcode; start de beheerinterface: sudo systemctl restart daynivo-kiosk-manager")
|
||||
return 1
|
||||
print(code)
|
||||
return 0
|
||||
@@ -65,10 +65,10 @@ def cmd_reset_password(args) -> int:
|
||||
code = setupcode.generate()
|
||||
setupcode.write(paths.setup_code_file, code)
|
||||
try:
|
||||
os.chown(paths.setup_code_file, pwd.getpwnam("voxip").pw_uid, grp.getgrnam("kiosk").gr_gid)
|
||||
os.chown(paths.setup_code_file, pwd.getpwnam("daynivo").pw_uid, grp.getgrnam("kiosk").gr_gid)
|
||||
except KeyError:
|
||||
pass
|
||||
subprocess.run(["systemctl", "restart", "voxip-kiosk-manager.service"], check=False)
|
||||
subprocess.run(["systemctl", "restart", "daynivo-kiosk-manager.service"], check=False)
|
||||
print(f"Wachtwoord verwijderd. Nieuwe setupcode: {code}")
|
||||
print("Open de beheerinterface en stel een nieuw wachtwoord in; de code staat ook op het kioskscherm.")
|
||||
return 0
|
||||
@@ -1,4 +1,4 @@
|
||||
"""voxip-kiosk-healthcheck — runs every minute (timer) as root.
|
||||
"""daynivo-kiosk-healthcheck — runs every minute (timer) as root.
|
||||
|
||||
Keeps every kiosk service alive without hiding problems: each recovery is
|
||||
logged, and a crash loop that hit systemd's start limit is retried only
|
||||
@@ -21,7 +21,7 @@ from .cdp import CDP, CDPError
|
||||
from .config import load_config
|
||||
from .paths import Paths, load_paths
|
||||
|
||||
log = logging.getLogger("voxip-kiosk-healthcheck")
|
||||
log = logging.getLogger("daynivo-kiosk-healthcheck")
|
||||
|
||||
ALWAYS_ON = (services.AGENT_UNIT, services.MANAGER_UNIT)
|
||||
DISPLAY_UNITS = (services.X_UNIT, services.BROWSER_UNIT)
|
||||
@@ -147,7 +147,7 @@ class HealthCheck:
|
||||
|
||||
|
||||
def main(argv: list[str] | None = None) -> int:
|
||||
parser = argparse.ArgumentParser(prog="voxip-kiosk-healthcheck")
|
||||
parser = argparse.ArgumentParser(prog="daynivo-kiosk-healthcheck")
|
||||
parser.add_argument("--check-only", action="store_true", help="alleen rapporteren, niets herstellen")
|
||||
parser.add_argument("--json", action="store_true", help="rapport als JSON")
|
||||
args = parser.parse_args(argv)
|
||||
@@ -1,11 +1,11 @@
|
||||
"""voxip-kiosk-helper — the only privileged entry point of Daynivo Kiosk OS.
|
||||
"""daynivo-kiosk-helper — the only privileged entry point of Daynivo Kiosk OS.
|
||||
|
||||
Runs as root, either through ``sudo -n`` from the web interface (user
|
||||
``voxip``) or from systemd units. It accepts a fixed set of verbs, validates
|
||||
``daynivo``) or from systemd units. It accepts a fixed set of verbs, validates
|
||||
every argument itself, never invokes a shell and prints a single JSON
|
||||
object on stdout.
|
||||
|
||||
Usage: voxip-kiosk-helper <verb> [args] (network-apply reads JSON on stdin)
|
||||
Usage: daynivo-kiosk-helper <verb> [args] (network-apply reads JSON on stdin)
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
@@ -27,13 +27,13 @@ from . import netconfig, updater
|
||||
from . import validation as v
|
||||
from .paths import Paths, default_paths
|
||||
|
||||
HELPER_PATH = "/usr/lib/voxip-kiosk/voxip-kiosk-helper"
|
||||
ROLLBACK_UNIT = "voxip-network-rollback"
|
||||
UPDATE_UNIT = "voxip-kiosk-update-now"
|
||||
UPDATER_PATH = "/usr/lib/voxip-kiosk/voxip-kiosk-updater"
|
||||
ACTIVATE_UNIT = "voxip-network-activate"
|
||||
HELPER_PATH = "/usr/lib/daynivo-kiosk/daynivo-kiosk-helper"
|
||||
ROLLBACK_UNIT = "daynivo-network-rollback"
|
||||
UPDATE_UNIT = "daynivo-kiosk-update-now"
|
||||
UPDATER_PATH = "/usr/lib/daynivo-kiosk/daynivo-kiosk-updater"
|
||||
ACTIVATE_UNIT = "daynivo-network-activate"
|
||||
BOOT_DIRS = (Path("/boot/firmware"), Path("/boot"))
|
||||
# Files on the boot partition. The voxip-* names are from before the rename to
|
||||
# Files on the boot partition. The daynivo-* names are from before the rename to
|
||||
# Daynivo and keep working, so existing SD cards and instructions stay valid.
|
||||
RESET_TRIGGERS = ("daynivo-network-reset", "voxip-network-reset")
|
||||
IMPORT_CONFIGS = ("voxip-config.json", "daynivo-config.json") # applied in this order: the Daynivo name wins
|
||||
@@ -83,24 +83,24 @@ class Helper:
|
||||
# -- power & browser -------------------------------------------------
|
||||
|
||||
def reboot(self) -> dict[str, Any]:
|
||||
self.run(["systemd-run", "--on-active=3", "--unit=voxip-power-reboot", "/bin/systemctl", "reboot"])
|
||||
self.run(["systemd-run", "--on-active=3", "--unit=daynivo-power-reboot", "/bin/systemctl", "reboot"])
|
||||
log(syslog.LOG_NOTICE, "Herstart aangevraagd via beheerinterface")
|
||||
return {"message": "De Raspberry Pi wordt over enkele seconden herstart."}
|
||||
|
||||
def poweroff(self) -> dict[str, Any]:
|
||||
self.run(["systemd-run", "--on-active=3", "--unit=voxip-power-off", "/bin/systemctl", "poweroff"])
|
||||
self.run(["systemd-run", "--on-active=3", "--unit=daynivo-power-off", "/bin/systemctl", "poweroff"])
|
||||
log(syslog.LOG_NOTICE, "Uitschakelen aangevraagd via beheerinterface")
|
||||
return {"message": "De Raspberry Pi wordt over enkele seconden uitgeschakeld."}
|
||||
|
||||
def restart_browser(self) -> dict[str, Any]:
|
||||
self.run(["systemctl", "reset-failed", "voxip-kiosk-browser.service", "voxip-kiosk-x.service"], check=False)
|
||||
self.run(["systemctl", "restart", "--no-block", "voxip-kiosk-browser.service"])
|
||||
self.run(["systemctl", "reset-failed", "daynivo-kiosk-browser.service", "daynivo-kiosk-x.service"], check=False)
|
||||
self.run(["systemctl", "restart", "--no-block", "daynivo-kiosk-browser.service"])
|
||||
log(syslog.LOG_NOTICE, "Chromium wordt herstart")
|
||||
return {"message": "Chromium wordt herstart."}
|
||||
|
||||
def restart_manager(self) -> dict[str, Any]:
|
||||
# Delayed so the web interface can still answer the request that asked for it.
|
||||
self.run(["systemd-run", "--on-active=2", "--unit=voxip-manager-restart", "/bin/systemctl", "restart", "voxip-kiosk-manager.service"])
|
||||
self.run(["systemd-run", "--on-active=2", "--unit=daynivo-manager-restart", "/bin/systemctl", "restart", "daynivo-kiosk-manager.service"])
|
||||
log(syslog.LOG_NOTICE, "Beheerinterface wordt herstart")
|
||||
return {"message": "De beheerinterface wordt herstart."}
|
||||
|
||||
@@ -326,7 +326,7 @@ class Helper:
|
||||
|
||||
def _chown_config(self, path: Path) -> None:
|
||||
try:
|
||||
uid = pwd.getpwnam("voxip").pw_uid
|
||||
uid = pwd.getpwnam("daynivo").pw_uid
|
||||
gid = grp.getgrnam("kiosk").gr_gid
|
||||
except KeyError:
|
||||
return
|
||||
@@ -392,7 +392,7 @@ class Helper:
|
||||
os.chown(ssh_dir, user.pw_uid, user.pw_gid)
|
||||
sshd_dir = self.etc / "ssh" / "sshd_config.d"
|
||||
sshd_dir.mkdir(parents=True, exist_ok=True)
|
||||
(sshd_dir / "voxip-kiosk.conf").write_text(
|
||||
(sshd_dir / "daynivo-kiosk.conf").write_text(
|
||||
"# Beheerd door Daynivo Kiosk OS: alleen inloggen met SSH-sleutels.\n"
|
||||
"PasswordAuthentication no\nKbdInteractiveAuthentication no\nPermitRootLogin no\n",
|
||||
encoding="utf-8",
|
||||
@@ -449,9 +449,9 @@ def _parse_json(text: str) -> Any:
|
||||
def main(argv: list[str] | None = None, helper: Helper | None = None, environ: dict[str, str] | None = None) -> int:
|
||||
argv = sys.argv[1:] if argv is None else argv
|
||||
environ = os.environ if environ is None else environ
|
||||
syslog.openlog("voxip-kiosk-helper", 0, syslog.LOG_DAEMON)
|
||||
syslog.openlog("daynivo-kiosk-helper", 0, syslog.LOG_DAEMON)
|
||||
if not argv:
|
||||
print(json.dumps({"ok": False, "error": "Gebruik: voxip-kiosk-helper <opdracht> [argumenten]"}))
|
||||
print(json.dumps({"ok": False, "error": "Gebruik: daynivo-kiosk-helper <opdracht> [argumenten]"}))
|
||||
return 2
|
||||
verb, args = argv[0], argv[1:]
|
||||
caller = environ.get("SUDO_USER")
|
||||
@@ -460,7 +460,7 @@ def main(argv: list[str] | None = None, helper: Helper | None = None, environ: d
|
||||
print(json.dumps({"ok": False, "error": "Opdracht niet toegestaan"}))
|
||||
return 1
|
||||
if helper is None and os.geteuid() != 0:
|
||||
print(json.dumps({"ok": False, "error": "voxip-kiosk-helper moet als root draaien"}))
|
||||
print(json.dumps({"ok": False, "error": "daynivo-kiosk-helper moet als root draaien"}))
|
||||
return 1
|
||||
helper = helper or Helper()
|
||||
stdin = sys.stdin.read(16385) if verb == "network-apply" else ""
|
||||
File renamed without changes.
File renamed without changes.
@@ -1,4 +1,4 @@
|
||||
"""voxip-kiosk-manager — starts the web interface under gunicorn.
|
||||
"""daynivo-kiosk-manager — starts the web interface under gunicorn.
|
||||
|
||||
Reads port and HTTPS settings from the kiosk configuration, creates a
|
||||
self-signed certificate when HTTPS is enabled and none exists yet, then
|
||||
@@ -19,8 +19,8 @@ from . import logutil
|
||||
from .config import load_config
|
||||
from .paths import Paths, load_paths
|
||||
|
||||
log = logging.getLogger("voxip-kiosk-manager")
|
||||
PYTHON_LIB = "/usr/lib/voxip-kiosk/python"
|
||||
log = logging.getLogger("daynivo-kiosk-manager")
|
||||
PYTHON_LIB = "/usr/lib/daynivo-kiosk/python"
|
||||
|
||||
|
||||
def ensure_certificate(paths: Paths) -> tuple[Path, Path]:
|
||||
@@ -55,7 +55,7 @@ def gunicorn_argv(paths: Paths, config: dict) -> list[str]:
|
||||
if config["admin_https"]:
|
||||
cert, key = ensure_certificate(paths)
|
||||
argv += ["--certfile", str(cert), "--keyfile", str(key)]
|
||||
argv.append("voxip_kiosk.web:create_app()")
|
||||
argv.append("daynivo_kiosk.web:create_app()")
|
||||
return argv
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ from typing import Any
|
||||
from . import validation as v
|
||||
|
||||
INTERFACES = {"eth0": "ethernet", "wlan0": "wifi"}
|
||||
PROFILE_PREFIX = "voxip-"
|
||||
PROFILE_PREFIX = "daynivo-"
|
||||
ROLLBACK_SECONDS = 180
|
||||
|
||||
|
||||
File renamed without changes.
@@ -1,7 +1,7 @@
|
||||
"""Filesystem locations used by every Daynivo Kiosk component.
|
||||
|
||||
The defaults match the installed layout. Unprivileged components may
|
||||
override them through ``VOXIP_*`` environment variables (used by the test
|
||||
override them through ``DAYNIVO_*`` environment variables (used by the test
|
||||
suite and for local development). The root helper always uses
|
||||
:func:`default_paths` and never reads the environment.
|
||||
"""
|
||||
@@ -15,15 +15,15 @@ from pathlib import Path
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class Paths:
|
||||
config_dir: Path = Path("/etc/voxip-kiosk")
|
||||
state_dir: Path = Path("/var/lib/voxip-kiosk")
|
||||
network_state_dir: Path = Path("/var/lib/voxip-kiosk-network")
|
||||
agent_run_dir: Path = Path("/run/voxip-kiosk-agent")
|
||||
browser_run_dir: Path = Path("/run/voxip-kiosk-browser")
|
||||
health_run_dir: Path = Path("/run/voxip-kiosk-health")
|
||||
share_dir: Path = Path("/usr/share/voxip-kiosk")
|
||||
log_dir: Path = Path("/var/log/voxip-kiosk")
|
||||
helper: Path = Path("/usr/lib/voxip-kiosk/voxip-kiosk-helper")
|
||||
config_dir: Path = Path("/etc/daynivo-kiosk")
|
||||
state_dir: Path = Path("/var/lib/daynivo-kiosk")
|
||||
network_state_dir: Path = Path("/var/lib/daynivo-kiosk-network")
|
||||
agent_run_dir: Path = Path("/run/daynivo-kiosk-agent")
|
||||
browser_run_dir: Path = Path("/run/daynivo-kiosk-browser")
|
||||
health_run_dir: Path = Path("/run/daynivo-kiosk-health")
|
||||
share_dir: Path = Path("/usr/share/daynivo-kiosk")
|
||||
log_dir: Path = Path("/var/log/daynivo-kiosk")
|
||||
helper: Path = Path("/usr/lib/daynivo-kiosk/daynivo-kiosk-helper")
|
||||
nm_connections_dir: Path = Path("/etc/NetworkManager/system-connections")
|
||||
|
||||
@property
|
||||
@@ -90,15 +90,15 @@ def default_paths() -> Paths:
|
||||
|
||||
|
||||
_ENV = {
|
||||
"config_dir": "VOXIP_CONFIG_DIR",
|
||||
"state_dir": "VOXIP_STATE_DIR",
|
||||
"network_state_dir": "VOXIP_NETWORK_STATE_DIR",
|
||||
"agent_run_dir": "VOXIP_AGENT_RUN_DIR",
|
||||
"browser_run_dir": "VOXIP_BROWSER_RUN_DIR",
|
||||
"health_run_dir": "VOXIP_HEALTH_RUN_DIR",
|
||||
"share_dir": "VOXIP_SHARE_DIR",
|
||||
"log_dir": "VOXIP_LOG_DIR",
|
||||
"helper": "VOXIP_HELPER",
|
||||
"config_dir": "DAYNIVO_CONFIG_DIR",
|
||||
"state_dir": "DAYNIVO_STATE_DIR",
|
||||
"network_state_dir": "DAYNIVO_NETWORK_STATE_DIR",
|
||||
"agent_run_dir": "DAYNIVO_AGENT_RUN_DIR",
|
||||
"browser_run_dir": "DAYNIVO_BROWSER_RUN_DIR",
|
||||
"health_run_dir": "DAYNIVO_HEALTH_RUN_DIR",
|
||||
"share_dir": "DAYNIVO_SHARE_DIR",
|
||||
"log_dir": "DAYNIVO_LOG_DIR",
|
||||
"helper": "DAYNIVO_HELPER",
|
||||
}
|
||||
|
||||
|
||||
@@ -5,11 +5,11 @@ from __future__ import annotations
|
||||
import subprocess
|
||||
from typing import Any
|
||||
|
||||
BROWSER_UNIT = "voxip-kiosk-browser.service"
|
||||
X_UNIT = "voxip-kiosk-x.service"
|
||||
AGENT_UNIT = "voxip-kiosk-agent.service"
|
||||
MANAGER_UNIT = "voxip-kiosk-manager.service"
|
||||
HEALTH_UNIT = "voxip-kiosk-healthcheck.service"
|
||||
BROWSER_UNIT = "daynivo-kiosk-browser.service"
|
||||
X_UNIT = "daynivo-kiosk-x.service"
|
||||
AGENT_UNIT = "daynivo-kiosk-agent.service"
|
||||
MANAGER_UNIT = "daynivo-kiosk-manager.service"
|
||||
HEALTH_UNIT = "daynivo-kiosk-healthcheck.service"
|
||||
|
||||
# Units whose logs the web interface may show (label → journalctl -u args).
|
||||
LOG_SOURCES: dict[str, tuple[str, list[str]]] = {
|
||||
@@ -18,7 +18,7 @@ LOG_SOURCES: dict[str, tuple[str, list[str]]] = {
|
||||
"agent": ("Kiosk-agent", [AGENT_UNIT]),
|
||||
"health": ("Healthcheck", [HEALTH_UNIT]),
|
||||
"manager": ("Beheerinterface", [MANAGER_UNIT]),
|
||||
"network": ("Netwerk", ["NetworkManager.service", "voxip-network-guard.service", "voxip-network-rollback.service", "voxip-network-activate.service"]),
|
||||
"network": ("Netwerk", ["NetworkManager.service", "daynivo-network-guard.service", "daynivo-network-rollback.service", "daynivo-network-activate.service"]),
|
||||
"system": ("Volledig systeem", []),
|
||||
}
|
||||
LOG_LINES = (50, 100, 200, 500)
|
||||
File renamed without changes.
File renamed without changes.
@@ -1,4 +1,4 @@
|
||||
"""voxip-kiosk-updater — installs new Daynivo Kiosk OS releases (runs as root).
|
||||
"""daynivo-kiosk-updater — installs new Daynivo Kiosk OS releases (runs as root).
|
||||
|
||||
A release is a tag ``vX.Y.Z`` on the Gitea repository with two assets:
|
||||
``daynivo-kiosk-os-X.Y.Z.tar.gz`` and its ``.sha256``. The updater
|
||||
@@ -11,9 +11,9 @@ A release is a tag ``vX.Y.Z`` on the Gitea repository with two assets:
|
||||
|
||||
It never installs an older or equal version. Usage::
|
||||
|
||||
voxip-kiosk-updater --check # only check
|
||||
voxip-kiosk-updater --auto # check; install when auto_update is on (daily timer)
|
||||
voxip-kiosk-updater --install # check and install when newer (button in the web interface)
|
||||
daynivo-kiosk-updater --check # only check
|
||||
daynivo-kiosk-updater --auto # check; install when auto_update is on (daily timer)
|
||||
daynivo-kiosk-updater --install # check and install when newer (button in the web interface)
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
@@ -39,12 +39,12 @@ from . import logutil
|
||||
from .config import atomic_write_json, load_config
|
||||
from .paths import Paths, default_paths, read_version
|
||||
|
||||
log = logging.getLogger("voxip-kiosk-updater")
|
||||
log = logging.getLogger("daynivo-kiosk-updater")
|
||||
|
||||
RELEASE_API = "https://git.rothman.nl/api/v1/repos/rothman/daynivo-kiosk-os/releases/latest"
|
||||
ASSET_PREFIX = "daynivo-kiosk-os-"
|
||||
RELEASE_DIR = Path("/opt/daynivo-kiosk-os")
|
||||
CACHE_DIR = Path("/var/cache/voxip-kiosk")
|
||||
CACHE_DIR = Path("/var/cache/daynivo-kiosk")
|
||||
KEEP_RELEASES = 2
|
||||
MAX_DOWNLOAD = 64 * 1024 * 1024
|
||||
USER_AGENT = "Daynivo-Kiosk-OS/updater"
|
||||
@@ -219,7 +219,7 @@ class Updater:
|
||||
|
||||
|
||||
def main(argv: list[str] | None = None, updater: Updater | None = None) -> int:
|
||||
parser = argparse.ArgumentParser(prog="voxip-kiosk-updater", description="Daynivo Kiosk OS bijwerken.")
|
||||
parser = argparse.ArgumentParser(prog="daynivo-kiosk-updater", description="Daynivo Kiosk OS bijwerken.")
|
||||
mode = parser.add_mutually_exclusive_group(required=True)
|
||||
mode.add_argument("--check", action="store_true", help="alleen controleren")
|
||||
mode.add_argument("--auto", action="store_true", help="controleren en bijwerken als automatisch bijwerken aan staat")
|
||||
File renamed without changes.
@@ -48,7 +48,7 @@ def create_app(paths: Paths | None = None, *, helper: Any = None, cdp: Any = Non
|
||||
app = Flask(__name__)
|
||||
app.secret_key = _secret_key(paths)
|
||||
app.config.update(
|
||||
SESSION_COOKIE_NAME="voxip_session",
|
||||
SESSION_COOKIE_NAME="daynivo_session",
|
||||
SESSION_COOKIE_HTTPONLY=True,
|
||||
SESSION_COOKIE_SAMESITE="Strict",
|
||||
SESSION_COOKIE_SECURE=config["admin_https"],
|
||||
@@ -58,7 +58,7 @@ def create_app(paths: Paths | None = None, *, helper: Any = None, cdp: Any = Non
|
||||
)
|
||||
credentials = CredentialStore(paths.admin_file, paths.setup_code_file)
|
||||
credentials.ensure_setup_code()
|
||||
app.extensions["voxip"] = Services(
|
||||
app.extensions["daynivo"] = Services(
|
||||
paths=paths,
|
||||
credentials=credentials,
|
||||
throttle=LoginThrottle(),
|
||||
@@ -21,7 +21,7 @@ HASHER = PasswordHasher(time_cost=3, memory_cost=65536, parallelism=2, hash_len=
|
||||
|
||||
MIN_LENGTH = 10
|
||||
MAX_LENGTH = 128
|
||||
WEAK = {"admin", "administrator", "password", "wachtwoord", "raspberry", "voxip", "kiosk", "welkom", "welcome", "qwerty", "123456"}
|
||||
WEAK = {"admin", "administrator", "password", "wachtwoord", "raspberry", "daynivo", "kiosk", "welkom", "welcome", "qwerty", "123456"}
|
||||
|
||||
|
||||
def password_problem(password: str, confirm: str | None = None) -> str | None:
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
|
Before Width: | Height: | Size: 3.6 KiB After Width: | Height: | Size: 3.6 KiB |
File renamed without changes.
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 17 KiB |
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -22,14 +22,14 @@ from . import security
|
||||
from .auth import password_problem
|
||||
|
||||
bp = Blueprint("ui", __name__)
|
||||
audit_log = logging.getLogger("voxip-kiosk-audit")
|
||||
audit_log = logging.getLogger("daynivo-kiosk-audit")
|
||||
|
||||
IDLE_TIMEOUT = 3600
|
||||
PUBLIC_ENDPOINTS = {"ui.login", "ui.setup", "static"}
|
||||
|
||||
|
||||
def svc():
|
||||
return current_app.extensions["voxip"]
|
||||
return current_app.extensions["daynivo"]
|
||||
|
||||
|
||||
def cfg() -> dict[str, Any]:
|
||||
@@ -1,8 +1,8 @@
|
||||
[Unit]
|
||||
Description=Daynivo configuratie en SSH-sleutels importeren vanaf de bootpartitie
|
||||
Documentation=file:///usr/share/doc/voxip-kiosk/README.md
|
||||
Documentation=file:///usr/share/doc/daynivo-kiosk/README.md
|
||||
After=local-fs.target
|
||||
Before=voxip-kiosk-manager.service voxip-kiosk-browser.service ssh.service
|
||||
Before=daynivo-kiosk-manager.service daynivo-kiosk-browser.service ssh.service
|
||||
ConditionPathExistsGlob=|/boot/firmware/daynivo-config.json
|
||||
ConditionPathExistsGlob=|/boot/firmware/daynivo-authorized_keys
|
||||
ConditionPathExistsGlob=|/boot/daynivo-config.json
|
||||
@@ -15,7 +15,7 @@ ConditionPathExistsGlob=|/boot/voxip-authorized_keys
|
||||
[Service]
|
||||
Type=oneshot
|
||||
RemainAfterExit=yes
|
||||
ExecStart=/usr/lib/voxip-kiosk/voxip-kiosk-helper boot-import
|
||||
ExecStart=/usr/lib/daynivo-kiosk/daynivo-kiosk-helper boot-import
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
@@ -1,18 +1,18 @@
|
||||
[Unit]
|
||||
Description=Daynivo Kiosk agent (navigatie, offlinepagina, verversen)
|
||||
Documentation=file:///usr/share/doc/voxip-kiosk/README.md
|
||||
After=voxip-kiosk-browser.service
|
||||
Documentation=file:///usr/share/doc/daynivo-kiosk/README.md
|
||||
After=daynivo-kiosk-browser.service
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=kiosk
|
||||
Group=kiosk
|
||||
RuntimeDirectory=voxip-kiosk-agent
|
||||
RuntimeDirectory=daynivo-kiosk-agent
|
||||
RuntimeDirectoryMode=0755
|
||||
ExecStart=/usr/lib/voxip-kiosk/voxip-kiosk-agent
|
||||
ExecStart=/usr/lib/daynivo-kiosk/daynivo-kiosk-agent
|
||||
Restart=always
|
||||
RestartSec=5
|
||||
SyslogIdentifier=voxip-kiosk-agent
|
||||
SyslogIdentifier=daynivo-kiosk-agent
|
||||
NoNewPrivileges=yes
|
||||
ProtectSystem=strict
|
||||
ProtectHome=yes
|
||||
@@ -0,0 +1,36 @@
|
||||
[Unit]
|
||||
Description=Daynivo Kiosk Chromium
|
||||
Documentation=file:///usr/share/doc/daynivo-kiosk/README.md
|
||||
Requires=daynivo-kiosk-x.service
|
||||
After=daynivo-kiosk-x.service daynivo-boot-import.service
|
||||
# Restarting or stopping X restarts/stops the browser as well.
|
||||
PartOf=daynivo-kiosk-x.service
|
||||
# A crash loop stops after 6 starts in 10 minutes; the healthcheck logs it
|
||||
# and retries after a cool-down (see daynivo-kiosk-healthcheck).
|
||||
StartLimitIntervalSec=600
|
||||
StartLimitBurst=6
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=kiosk
|
||||
Group=kiosk
|
||||
Environment=DISPLAY=:0
|
||||
Environment=XAUTHORITY=/run/daynivo-kiosk-x/Xauthority
|
||||
Environment=HOME=/home/kiosk
|
||||
RuntimeDirectory=daynivo-kiosk-browser
|
||||
RuntimeDirectoryMode=0755
|
||||
StateDirectory=daynivo-kiosk-browser
|
||||
StateDirectoryMode=0700
|
||||
ExecStart=/usr/lib/daynivo-kiosk/daynivo-kiosk-browser
|
||||
# daynivo-kiosk-browser exits 1 after a crash when browser_restart is on, 0 when it is off.
|
||||
Restart=on-failure
|
||||
RestartSec=5
|
||||
TimeoutStopSec=15
|
||||
KillMode=control-group
|
||||
OOMScoreAdjust=-100
|
||||
LogRateLimitIntervalSec=30s
|
||||
LogRateLimitBurst=300
|
||||
SyslogIdentifier=daynivo-kiosk-browser
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
@@ -0,0 +1,11 @@
|
||||
[Unit]
|
||||
Description=Daynivo Kiosk healthcheck
|
||||
Documentation=file:///usr/share/doc/daynivo-kiosk/README.md
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
RuntimeDirectory=daynivo-kiosk-health
|
||||
RuntimeDirectoryPreserve=yes
|
||||
ExecStart=/usr/lib/daynivo-kiosk/daynivo-kiosk-healthcheck
|
||||
SyslogIdentifier=daynivo-kiosk-healthcheck
|
||||
TimeoutStartSec=60
|
||||
File renamed without changes.
@@ -1,27 +1,27 @@
|
||||
[Unit]
|
||||
Description=Daynivo Kiosk Manager (webbeheer)
|
||||
Documentation=file:///usr/share/doc/voxip-kiosk/README.md
|
||||
After=network.target voxip-boot-import.service
|
||||
Documentation=file:///usr/share/doc/daynivo-kiosk/README.md
|
||||
After=network.target daynivo-boot-import.service
|
||||
StartLimitIntervalSec=300
|
||||
StartLimitBurst=10
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=voxip
|
||||
Group=voxip
|
||||
User=daynivo
|
||||
Group=daynivo
|
||||
# systemd-journal: read logs; video: vcgencmd (voltage/throttling)
|
||||
SupplementaryGroups=systemd-journal video
|
||||
ExecStart=/usr/lib/voxip-kiosk/voxip-kiosk-manager
|
||||
ExecStart=/usr/lib/daynivo-kiosk/daynivo-kiosk-manager
|
||||
Restart=always
|
||||
RestartSec=3
|
||||
UMask=0027
|
||||
LogsDirectory=voxip-kiosk
|
||||
LogsDirectory=daynivo-kiosk
|
||||
LogsDirectoryMode=0750
|
||||
SyslogIdentifier=voxip-kiosk-manager
|
||||
SyslogIdentifier=daynivo-kiosk-manager
|
||||
# No NoNewPrivileges/ProtectSystem/ProtectHome: the helper started through
|
||||
# "sudo -n voxip-kiosk-helper" inherits this unit's mount namespace and must
|
||||
# "sudo -n daynivo-kiosk-helper" inherits this unit's mount namespace and must
|
||||
# be able to write /etc (hostname, NetworkManager profiles). The web process
|
||||
# itself runs unprivileged as user voxip and can only write its own dirs.
|
||||
# itself runs unprivileged as user daynivo and can only write its own dirs.
|
||||
# Also no ProtectKernelTunables/ProtectKernelModules/LockPersonality,
|
||||
# RestrictSUIDSGID, SystemCall*, ...: for a non-root unit those imply
|
||||
# NoNewPrivileges, and sudo then refuses to run (tests/test_units.py).
|
||||
@@ -1,11 +1,11 @@
|
||||
[Unit]
|
||||
Description=Daynivo Kiosk OS: controleren op updates (en installeren als dat aan staat)
|
||||
Documentation=file:///usr/share/doc/voxip-kiosk/README.md
|
||||
Documentation=file:///usr/share/doc/daynivo-kiosk/README.md
|
||||
Wants=network-online.target
|
||||
After=network-online.target
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
ExecStart=/usr/lib/voxip-kiosk/voxip-kiosk-updater --auto
|
||||
ExecStart=/usr/lib/daynivo-kiosk/daynivo-kiosk-updater --auto
|
||||
TimeoutStartSec=1h
|
||||
SyslogIdentifier=voxip-kiosk-updater
|
||||
SyslogIdentifier=daynivo-kiosk-updater
|
||||
File renamed without changes.
@@ -1,6 +1,6 @@
|
||||
[Unit]
|
||||
Description=Daynivo Kiosk X-server (vt7)
|
||||
Documentation=file:///usr/share/doc/voxip-kiosk/README.md
|
||||
Documentation=file:///usr/share/doc/daynivo-kiosk/README.md
|
||||
After=systemd-user-sessions.service systemd-logind.service
|
||||
Conflicts=getty@tty7.service
|
||||
StartLimitIntervalSec=600
|
||||
@@ -20,10 +20,10 @@ StandardOutput=journal
|
||||
StandardError=journal
|
||||
UtmpIdentifier=tty7
|
||||
UtmpMode=user
|
||||
RuntimeDirectory=voxip-kiosk-x
|
||||
RuntimeDirectory=daynivo-kiosk-x
|
||||
RuntimeDirectoryMode=0700
|
||||
ExecStartPre=+/usr/bin/chvt 7
|
||||
ExecStart=/usr/lib/voxip-kiosk/voxip-kiosk-x
|
||||
ExecStart=/usr/lib/daynivo-kiosk/daynivo-kiosk-x
|
||||
Restart=always
|
||||
RestartSec=3
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[Unit]
|
||||
Description=Daynivo netwerkherstel bij opstarten (rollback en noodreset)
|
||||
Documentation=file:///usr/share/doc/voxip-kiosk/README.md
|
||||
Documentation=file:///usr/share/doc/daynivo-kiosk/README.md
|
||||
DefaultDependencies=no
|
||||
After=local-fs.target
|
||||
Before=NetworkManager.service network-pre.target shutdown.target
|
||||
@@ -10,7 +10,7 @@ Conflicts=shutdown.target
|
||||
[Service]
|
||||
Type=oneshot
|
||||
RemainAfterExit=yes
|
||||
ExecStart=/usr/lib/voxip-kiosk/voxip-kiosk-helper network-boot-guard
|
||||
ExecStart=/usr/lib/daynivo-kiosk/daynivo-kiosk-helper network-boot-guard
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
@@ -1,36 +0,0 @@
|
||||
[Unit]
|
||||
Description=Daynivo Kiosk Chromium
|
||||
Documentation=file:///usr/share/doc/voxip-kiosk/README.md
|
||||
Requires=voxip-kiosk-x.service
|
||||
After=voxip-kiosk-x.service voxip-boot-import.service
|
||||
# Restarting or stopping X restarts/stops the browser as well.
|
||||
PartOf=voxip-kiosk-x.service
|
||||
# A crash loop stops after 6 starts in 10 minutes; the healthcheck logs it
|
||||
# and retries after a cool-down (see voxip-kiosk-healthcheck).
|
||||
StartLimitIntervalSec=600
|
||||
StartLimitBurst=6
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=kiosk
|
||||
Group=kiosk
|
||||
Environment=DISPLAY=:0
|
||||
Environment=XAUTHORITY=/run/voxip-kiosk-x/Xauthority
|
||||
Environment=HOME=/home/kiosk
|
||||
RuntimeDirectory=voxip-kiosk-browser
|
||||
RuntimeDirectoryMode=0755
|
||||
StateDirectory=voxip-kiosk-browser
|
||||
StateDirectoryMode=0700
|
||||
ExecStart=/usr/lib/voxip-kiosk/voxip-kiosk-browser
|
||||
# voxip-kiosk-browser exits 1 after a crash when browser_restart is on, 0 when it is off.
|
||||
Restart=on-failure
|
||||
RestartSec=5
|
||||
TimeoutStopSec=15
|
||||
KillMode=control-group
|
||||
OOMScoreAdjust=-100
|
||||
LogRateLimitIntervalSec=30s
|
||||
LogRateLimitBurst=300
|
||||
SyslogIdentifier=voxip-kiosk-browser
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
@@ -1,11 +0,0 @@
|
||||
[Unit]
|
||||
Description=Daynivo Kiosk healthcheck
|
||||
Documentation=file:///usr/share/doc/voxip-kiosk/README.md
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
RuntimeDirectory=voxip-kiosk-health
|
||||
RuntimeDirectoryPreserve=yes
|
||||
ExecStart=/usr/lib/voxip-kiosk/voxip-kiosk-healthcheck
|
||||
SyslogIdentifier=voxip-kiosk-healthcheck
|
||||
TimeoutStartSec=60
|
||||
+7
-7
@@ -4,7 +4,7 @@ from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
from voxip_kiosk.paths import Paths
|
||||
from daynivo_kiosk.paths import Paths
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
|
||||
@@ -39,7 +39,7 @@ class FakeHelper:
|
||||
self.responses = {}
|
||||
|
||||
def call(self, verb, *args, payload=None, timeout=60):
|
||||
from voxip_kiosk.helper_client import HelperError
|
||||
from daynivo_kiosk.helper_client import HelperError
|
||||
|
||||
self.calls.append((verb, args, payload))
|
||||
if self.fail:
|
||||
@@ -53,7 +53,7 @@ class FakeCDP:
|
||||
self.fail = None
|
||||
|
||||
def _do(self, name, *args):
|
||||
from voxip_kiosk.cdp import CDPError
|
||||
from daynivo_kiosk.cdp import CDPError
|
||||
|
||||
self.calls.append((name, args))
|
||||
if self.fail:
|
||||
@@ -80,7 +80,7 @@ def fake_cdp():
|
||||
@pytest.fixture
|
||||
def quiet_system(monkeypatch):
|
||||
"""Keep the web tests independent of the machine they run on."""
|
||||
from voxip_kiosk import netinfo, services, sysinfo
|
||||
from daynivo_kiosk import netinfo, services, sysinfo
|
||||
|
||||
monkeypatch.setattr(services, "summary", lambda: {
|
||||
"browser": {"unit": services.BROWSER_UNIT, "status": "ok", "label": "Actief", "restarts": "2", "since": ""},
|
||||
@@ -96,7 +96,7 @@ def quiet_system(monkeypatch):
|
||||
"available": True, "wifi_country": "NL",
|
||||
"devices": {
|
||||
iface: {"interface": iface, "kind": kind, "present": True, "state": "connected", "state_label": "Verbonden",
|
||||
"connected": True, "connection": f"voxip-{iface}", "addresses": ["192.168.10.20/24"], "gateway": "192.168.10.1",
|
||||
"connected": True, "connection": f"daynivo-{iface}", "addresses": ["192.168.10.20/24"], "gateway": "192.168.10.1",
|
||||
"dns": ["192.168.10.1"], "hwaddr": "B8:27:EB:00:00:01",
|
||||
"profile": {"exists": True, "method": "dhcp", "address": "", "gateway": "", "dns": "", "ssid": "Kantoor", "hidden": False}}
|
||||
for iface, kind in (("eth0", "ethernet"), ("wlan0", "wifi"))
|
||||
@@ -106,7 +106,7 @@ def quiet_system(monkeypatch):
|
||||
|
||||
@pytest.fixture
|
||||
def app(paths, fake_helper, fake_cdp, quiet_system):
|
||||
from voxip_kiosk.web import create_app
|
||||
from daynivo_kiosk.web import create_app
|
||||
|
||||
application = create_app(paths, helper=fake_helper, cdp=fake_cdp)
|
||||
application.config["TESTING"] = True
|
||||
@@ -130,7 +130,7 @@ PASSWORD = "Correct-Paard-Batterij-42"
|
||||
@pytest.fixture
|
||||
def logged_in(app, client, paths):
|
||||
"""A client with a configured admin password and an active session."""
|
||||
app.extensions["voxip"].credentials.set_password(PASSWORD)
|
||||
app.extensions["daynivo"].credentials.set_password(PASSWORD)
|
||||
page = client.get("/login")
|
||||
token = csrf_from(page)
|
||||
resp = client.post("/login", data={"password": PASSWORD, "csrf_token": token})
|
||||
|
||||
+3
-3
@@ -2,9 +2,9 @@ import json
|
||||
|
||||
import pytest
|
||||
|
||||
from voxip_kiosk.agent import Agent, HUNG_RELOAD_AFTER, HUNG_RESTART_AFTER
|
||||
from voxip_kiosk.cdp import CDPError
|
||||
from voxip_kiosk.config import save_config
|
||||
from daynivo_kiosk.agent import Agent, HUNG_RELOAD_AFTER, HUNG_RESTART_AFTER
|
||||
from daynivo_kiosk.cdp import CDPError
|
||||
from daynivo_kiosk.config import save_config
|
||||
|
||||
|
||||
class Clock:
|
||||
|
||||
@@ -2,8 +2,8 @@ import json
|
||||
from pathlib import Path
|
||||
from urllib.parse import parse_qs, urlsplit
|
||||
|
||||
from voxip_kiosk import browser as b
|
||||
from voxip_kiosk.config import DEFAULTS
|
||||
from daynivo_kiosk import browser as b
|
||||
from daynivo_kiosk.config import DEFAULTS
|
||||
|
||||
XRANDR = """Screen 0: minimum 320 x 200, current 1920 x 1080, maximum 7680 x 7680
|
||||
HDMI-1 connected primary 1920x1080+0+0 (normal left inverted right x axis y axis) 531mm x 299mm
|
||||
|
||||
@@ -4,7 +4,7 @@ import stat
|
||||
|
||||
import pytest
|
||||
|
||||
from voxip_kiosk import config as c
|
||||
from daynivo_kiosk import config as c
|
||||
|
||||
|
||||
def test_default_file_matches_defaults(paths):
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import pytest
|
||||
|
||||
from voxip_kiosk import services
|
||||
from voxip_kiosk.cdp import CDPError
|
||||
from voxip_kiosk.config import save_config
|
||||
from voxip_kiosk.healthcheck import CDP_FAILURES_BEFORE_RESTART, DISPLAY_RETRY_AFTER, HealthCheck
|
||||
from daynivo_kiosk import services
|
||||
from daynivo_kiosk.cdp import CDPError
|
||||
from daynivo_kiosk.config import save_config
|
||||
from daynivo_kiosk.healthcheck import CDP_FAILURES_BEFORE_RESTART, DISPLAY_RETRY_AFTER, HealthCheck
|
||||
|
||||
|
||||
class World:
|
||||
|
||||
+20
-20
@@ -3,8 +3,8 @@ import subprocess
|
||||
|
||||
import pytest
|
||||
|
||||
from voxip_kiosk import helper as h
|
||||
from voxip_kiosk.config import load_config
|
||||
from daynivo_kiosk import helper as h
|
||||
from daynivo_kiosk.config import load_config
|
||||
|
||||
|
||||
class Recorder:
|
||||
@@ -35,7 +35,7 @@ def helper(paths, tmp_path, boot):
|
||||
etc.mkdir()
|
||||
(etc / "hosts").write_text("127.0.0.1\tlocalhost\n127.0.1.1\tkiosk\n")
|
||||
run = Recorder({"nmcli -t -f": "aaaa-1111\n"})
|
||||
return h.Helper(paths=paths, run=run, etc=etc, boot_dirs=(boot,), helper_path="/usr/lib/voxip-kiosk/voxip-kiosk-helper")
|
||||
return h.Helper(paths=paths, run=run, etc=etc, boot_dirs=(boot,), helper_path="/usr/lib/daynivo-kiosk/daynivo-kiosk-helper")
|
||||
|
||||
|
||||
def run_main(helper, argv, env=None, capsys=None):
|
||||
@@ -45,17 +45,17 @@ def run_main(helper, argv, env=None, capsys=None):
|
||||
|
||||
|
||||
def test_internal_verbs_refused_via_sudo(helper, capsys):
|
||||
code, out = run_main(helper, ["network-restore"], {"SUDO_USER": "voxip"}, capsys)
|
||||
code, out = run_main(helper, ["network-restore"], {"SUDO_USER": "daynivo"}, capsys)
|
||||
assert code == 1 and out["ok"] is False
|
||||
code, out = run_main(helper, ["boot-import"], {"SUDO_USER": "voxip"}, capsys)
|
||||
code, out = run_main(helper, ["boot-import"], {"SUDO_USER": "daynivo"}, capsys)
|
||||
assert code == 1
|
||||
assert helper.run.commands == []
|
||||
|
||||
|
||||
def test_public_verb_via_sudo(helper, capsys):
|
||||
code, out = run_main(helper, ["restart-browser"], {"SUDO_USER": "voxip"}, capsys)
|
||||
code, out = run_main(helper, ["restart-browser"], {"SUDO_USER": "daynivo"}, capsys)
|
||||
assert code == 0 and out["ok"] is True
|
||||
assert helper.run.ran("systemctl", "restart", "--no-block", "voxip-kiosk-browser.service")
|
||||
assert helper.run.ran("systemctl", "restart", "--no-block", "daynivo-kiosk-browser.service")
|
||||
|
||||
|
||||
def test_unknown_verb_and_bad_arity(helper, capsys):
|
||||
@@ -99,7 +99,7 @@ ETH_STATIC = {"interface": "eth0", "method": "static", "address": "192.168.5.10/
|
||||
def test_network_apply_snapshot_pending_and_timers(helper, paths):
|
||||
(paths.nm_connections_dir / "Wired connection 1.nmconnection").write_text("[connection]\nid=Wired connection 1\n")
|
||||
result = helper.network_apply(ETH_STATIC)
|
||||
keyfile = paths.nm_connections_dir / "voxip-eth0.nmconnection"
|
||||
keyfile = paths.nm_connections_dir / "daynivo-eth0.nmconnection"
|
||||
assert keyfile.exists() and oct(keyfile.stat().st_mode & 0o777) == "0o600"
|
||||
assert "address1=192.168.5.10/24,192.168.5.1" in keyfile.read_text()
|
||||
assert (paths.network_backup_dir / "Wired connection 1.nmconnection").exists()
|
||||
@@ -108,8 +108,8 @@ def test_network_apply_snapshot_pending_and_timers(helper, paths):
|
||||
assert pending["deadline"] - pending["created"] == 180
|
||||
assert result["pending"]["interface"] == "eth0"
|
||||
runs = [c for c in helper.run.commands if c[0] == "systemd-run"]
|
||||
assert runs[0][1:3] == ["--unit=voxip-network-rollback", "--on-active=180"] and runs[0][-1] == "network-restore"
|
||||
assert runs[1][1:3] == ["--unit=voxip-network-activate", "--on-active=2"] and runs[1][-2:] == ["network-activate", "eth0"]
|
||||
assert runs[0][1:3] == ["--unit=daynivo-network-rollback", "--on-active=180"] and runs[0][-1] == "network-restore"
|
||||
assert runs[1][1:3] == ["--unit=daynivo-network-activate", "--on-active=2"] and runs[1][-2:] == ["network-activate", "eth0"]
|
||||
|
||||
|
||||
def test_second_apply_refused_while_pending(helper):
|
||||
@@ -123,8 +123,8 @@ def test_confirm_clears_pending(helper, paths):
|
||||
helper.network_confirm()
|
||||
assert not paths.network_pending_file.exists()
|
||||
assert not paths.network_backup_dir.exists()
|
||||
assert (paths.nm_connections_dir / "voxip-eth0.nmconnection").exists()
|
||||
assert helper.run.ran("systemctl", "stop", "voxip-network-rollback.timer")
|
||||
assert (paths.nm_connections_dir / "daynivo-eth0.nmconnection").exists()
|
||||
assert helper.run.ran("systemctl", "stop", "daynivo-network-rollback.timer")
|
||||
|
||||
|
||||
def test_restore_puts_old_profiles_back(helper, paths):
|
||||
@@ -134,7 +134,7 @@ def test_restore_puts_old_profiles_back(helper, paths):
|
||||
old.unlink()
|
||||
helper.network_restore()
|
||||
assert old.exists()
|
||||
assert not (paths.nm_connections_dir / "voxip-eth0.nmconnection").exists()
|
||||
assert not (paths.nm_connections_dir / "daynivo-eth0.nmconnection").exists()
|
||||
assert not paths.network_pending_file.exists()
|
||||
assert helper.run.ran("nmcli", "--wait", "30", "connection", "up", "uuid", "aaaa-1111")
|
||||
|
||||
@@ -152,7 +152,7 @@ def test_wifi_keeps_existing_password_for_same_ssid(helper, paths):
|
||||
helper.network_confirm()
|
||||
helper.network_apply({"interface": "wlan0", "method": "static", "address": "10.0.0.9/24", "gateway": "10.0.0.1", "dns": "10.0.0.1",
|
||||
"wifi": {"ssid": "Kantoor", "psk": ""}})
|
||||
assert "psk=geheim-wachtwoord" in (paths.nm_connections_dir / "voxip-wlan0.nmconnection").read_text()
|
||||
assert "psk=geheim-wachtwoord" in (paths.nm_connections_dir / "daynivo-wlan0.nmconnection").read_text()
|
||||
helper.network_confirm()
|
||||
with pytest.raises(h.HelperError, match="wachtwoord"):
|
||||
helper.network_apply({"interface": "wlan0", "method": "dhcp", "wifi": {"ssid": "Ander", "psk": ""}})
|
||||
@@ -165,22 +165,22 @@ def test_boot_guard_rolls_back_and_honours_reset(helper, paths, boot):
|
||||
helper.run.commands.clear()
|
||||
result = helper.network_boot_guard()
|
||||
assert result == {"rolled_back": True}
|
||||
assert not (paths.nm_connections_dir / "voxip-eth0.nmconnection").exists()
|
||||
assert not (paths.nm_connections_dir / "daynivo-eth0.nmconnection").exists()
|
||||
assert not any(c[0] == "nmcli" for c in helper.run.commands), "NetworkManager is not running yet at boot"
|
||||
|
||||
(paths.nm_connections_dir / "voxip-wlan0.nmconnection").write_text("x")
|
||||
(paths.nm_connections_dir / "daynivo-wlan0.nmconnection").write_text("x")
|
||||
(boot / "daynivo-network-reset").write_text("")
|
||||
assert helper.network_boot_guard() == {"reset": True}
|
||||
assert not (paths.nm_connections_dir / "voxip-wlan0.nmconnection").exists()
|
||||
assert not (paths.nm_connections_dir / "daynivo-wlan0.nmconnection").exists()
|
||||
assert not (boot / "daynivo-network-reset").exists()
|
||||
assert old.exists()
|
||||
|
||||
|
||||
def test_boot_guard_still_honours_the_old_reset_name(helper, paths, boot):
|
||||
(paths.nm_connections_dir / "voxip-eth0.nmconnection").write_text("x")
|
||||
(paths.nm_connections_dir / "daynivo-eth0.nmconnection").write_text("x")
|
||||
(boot / "voxip-network-reset").write_text("")
|
||||
assert helper.network_boot_guard() == {"reset": True}
|
||||
assert not (paths.nm_connections_dir / "voxip-eth0.nmconnection").exists()
|
||||
assert not (paths.nm_connections_dir / "daynivo-eth0.nmconnection").exists()
|
||||
assert not (boot / "voxip-network-reset").exists()
|
||||
|
||||
|
||||
@@ -230,7 +230,7 @@ def test_update_verbs_are_public_and_install_runs_in_its_own_unit(helper):
|
||||
|
||||
def test_update_install_reports_a_running_update(helper):
|
||||
def busy(args, timeout=30, check=True, input=None):
|
||||
return subprocess.CompletedProcess(args, 1, stdout="", stderr="Unit voxip-kiosk-update-now.service already exists.")
|
||||
return subprocess.CompletedProcess(args, 1, stdout="", stderr="Unit daynivo-kiosk-update-now.service already exists.")
|
||||
|
||||
helper.run = busy
|
||||
with pytest.raises(h.HelperError, match="al een update"):
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import pytest
|
||||
|
||||
from voxip_kiosk import netconfig as n
|
||||
from voxip_kiosk.validation import ValidationError
|
||||
from daynivo_kiosk import netconfig as n
|
||||
from daynivo_kiosk.validation import ValidationError
|
||||
|
||||
|
||||
def test_validate_static_ethernet():
|
||||
@@ -27,7 +27,7 @@ def test_keyfile_static_ethernet():
|
||||
req = n.validate_request({"interface": "eth0", "method": "static", "address": "10.1.2.3/16", "gateway": "10.1.0.1", "dns": ["10.1.0.1"]})
|
||||
text = n.build_keyfile(req, uuid="11111111-2222-3333-4444-555555555555")
|
||||
kf = n.read_keyfile(text)
|
||||
assert kf.get("connection", "id") == "voxip-eth0"
|
||||
assert kf.get("connection", "id") == "daynivo-eth0"
|
||||
assert kf.get("connection", "type") == "ethernet"
|
||||
assert kf.get("connection", "autoconnect-priority") == "100"
|
||||
assert kf.get("ipv4", "method") == "manual"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from voxip_kiosk import services, sysinfo
|
||||
from daynivo_kiosk import services, sysinfo
|
||||
|
||||
|
||||
def test_parse_throttled():
|
||||
|
||||
+2
-2
@@ -22,8 +22,8 @@ def settings(unit):
|
||||
|
||||
|
||||
def test_manager_can_use_sudo_for_the_helper():
|
||||
unit = settings("voxip-kiosk-manager.service")
|
||||
assert unit["User"] == "voxip"
|
||||
unit = settings("daynivo-kiosk-manager.service")
|
||||
assert unit["User"] == "daynivo"
|
||||
for key in IMPLY_NO_NEW_PRIVILEGES:
|
||||
assert unit.get(key, "no") in ("no", "false", ""), key
|
||||
for key in ("ProtectSystem", "ProtectHome", "PrivateTmp"):
|
||||
|
||||
@@ -6,8 +6,8 @@ import tarfile
|
||||
|
||||
import pytest
|
||||
|
||||
from voxip_kiosk import updater as u
|
||||
from voxip_kiosk.config import save_config
|
||||
from daynivo_kiosk import updater as u
|
||||
from daynivo_kiosk.config import save_config
|
||||
|
||||
API = u.RELEASE_API
|
||||
BASE = "https://git.example.nl/rothman/daynivo-kiosk-os/releases/download"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import pytest
|
||||
|
||||
from voxip_kiosk import validation as v
|
||||
from daynivo_kiosk import validation as v
|
||||
|
||||
|
||||
@pytest.mark.parametrize("url", [
|
||||
|
||||
+9
-9
@@ -2,8 +2,8 @@ import json
|
||||
|
||||
import pytest
|
||||
|
||||
from voxip_kiosk.config import load_config
|
||||
from voxip_kiosk.web import auth
|
||||
from daynivo_kiosk.config import load_config
|
||||
from daynivo_kiosk.web import auth
|
||||
|
||||
from .conftest import PASSWORD, csrf_from, token
|
||||
|
||||
@@ -52,7 +52,7 @@ def test_setup_without_csrf_is_rejected(client, paths):
|
||||
# --- login & session -------------------------------------------------------------
|
||||
|
||||
def test_login_logout(app, client):
|
||||
app.extensions["voxip"].credentials.set_password(PASSWORD)
|
||||
app.extensions["daynivo"].credentials.set_password(PASSWORD)
|
||||
assert client.get("/").status_code == 302
|
||||
tok = csrf_from(client.get("/login"))
|
||||
assert "Onjuist" in client.post("/login", data={"csrf_token": tok, "password": "fout-wachtwoord"}).get_data(as_text=True)
|
||||
@@ -63,7 +63,7 @@ def test_login_logout(app, client):
|
||||
|
||||
|
||||
def test_session_cookie_flags(app, client):
|
||||
app.extensions["voxip"].credentials.set_password(PASSWORD)
|
||||
app.extensions["daynivo"].credentials.set_password(PASSWORD)
|
||||
tok = csrf_from(client.get("/login"))
|
||||
resp = client.post("/login", data={"csrf_token": tok, "password": PASSWORD})
|
||||
cookie = resp.headers["Set-Cookie"]
|
||||
@@ -71,7 +71,7 @@ def test_session_cookie_flags(app, client):
|
||||
|
||||
|
||||
def test_login_throttling(app, client):
|
||||
app.extensions["voxip"].credentials.set_password(PASSWORD)
|
||||
app.extensions["daynivo"].credentials.set_password(PASSWORD)
|
||||
tok = csrf_from(client.get("/login"))
|
||||
for _ in range(5):
|
||||
client.post("/login", data={"csrf_token": tok, "password": "verkeerd-wachtwoord"})
|
||||
@@ -113,7 +113,7 @@ def test_password_change_invalidates_other_sessions(app, logged_in):
|
||||
|
||||
|
||||
def test_idle_session_expires(app, logged_in, monkeypatch):
|
||||
from voxip_kiosk.web import views
|
||||
from daynivo_kiosk.web import views
|
||||
|
||||
real = views.time.time
|
||||
monkeypatch.setattr(views.time, "time", lambda: real() + views.IDLE_TIMEOUT + 5)
|
||||
@@ -141,7 +141,7 @@ def test_public_clients_blocked(client, addr, allowed):
|
||||
|
||||
|
||||
def test_same_subnet_global_ipv6_allowed():
|
||||
from voxip_kiosk.web.security import client_allowed
|
||||
from daynivo_kiosk.web.security import client_allowed
|
||||
|
||||
assert client_allowed("2a02:a210:1:2::99", False, ["2a02:a210:1:2::5/64"])
|
||||
assert not client_allowed("2a02:a210:9:9::99", False, ["2a02:a210:1:2::5/64"])
|
||||
@@ -156,7 +156,7 @@ def test_security_headers(client):
|
||||
|
||||
|
||||
def test_api_requires_login(app, client):
|
||||
app.extensions["voxip"].credentials.set_password(PASSWORD)
|
||||
app.extensions["daynivo"].credentials.set_password(PASSWORD)
|
||||
resp = client.get("/api/status")
|
||||
assert resp.status_code == 401 and resp.json["error"]
|
||||
|
||||
@@ -180,7 +180,7 @@ def test_pages_render(logged_in, path):
|
||||
|
||||
|
||||
def test_dashboard_masks_token(logged_in, paths):
|
||||
from voxip_kiosk.config import save_config
|
||||
from daynivo_kiosk.config import save_config
|
||||
|
||||
save_config(paths.config_file, {"url": "https://portal.example.nl/display/hal?token=supergeheimtoken99"})
|
||||
body = logged_in.get("/").get_data(as_text=True)
|
||||
|
||||
Loaded 100 of 102 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user