5 Commits
Author SHA1 Message Date
rothmanandClaude Opus 5.5 88e60f9bf9 Release 1.3.0
CI / test-bookworm (push) Successful in 28s
CI / test-latest (push) Successful in 32s
Release / package (push) Successful in 32s
Release / image (push) Successful in 45m9s
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-05 23:59:04 +01:00
rothmanandClaude Opus 5.5 eb0242c182 Rename every remaining voxip name to daynivo, with a one-time migration
CI / test-bookworm (push) Successful in 29s
CI / test-latest (push) Successful in 31s
Services (daynivo-kiosk-*, daynivo-network-guard, daynivo-boot-import),
programs in /usr/lib/daynivo-kiosk, folders /etc, /var/lib, /var/log and
/var/cache daynivo-kiosk*, the system user daynivo, DAYNIVO_* settings,
the plymouth theme, network profiles daynivo-eth0/wlan0, the image stage
and its first user daynivoadmin.

install.sh moves a kiosk installed up to 1.2.x over once: old services
stopped and removed, folders moved (configuration, password, browser
profile, logs) with symlinks under the old names, the voxip user renamed
with the same uid, network profiles renamed unless a change awaits
confirmation, and old leftovers removed. Tested by upgrading a configured
1.2.0 installation in a container, and running the install a second time.

Old names keep working where people type or place them: voxip-config.json,
voxip-authorized_keys, voxip-network-reset, voxip-kiosk-ctl, and
VOXIP_DEFAULT_URL / VOXIP_DEVICE_NAME for image builds.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-05 23:58:03 +01:00
rothmanandClaude Opus 5.5 6733aacb6e Rename the Python package to daynivo_kiosk
CI / test-bookworm (push) Successful in 29s
CI / test-latest (push) Successful in 33s
src/voxip_kiosk -> src/daynivo_kiosk, with every import, the launchers,
install.sh, the Makefile and the manager's gunicorn app path. An upgrade
replaces /usr/lib/voxip-kiosk/python as a whole, so the old package
disappears; tested by upgrading an installation of the previous commit.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-05 23:52:39 +01:00
rothman d0f3206ff4 Release 1.2.0
CI / test-bookworm (push) Successful in 28s
CI / test-latest (push) Successful in 33s
Release / package (push) Successful in 29s
Release / image (push) Successful in 46m7s
2026-11-05 23:26:41 +01:00
rothmanandClaude Opus 5.5 b0840d240e Daynivo names for the files on the SD card and the command line tool
CI / test-bookworm (push) Successful in 25s
CI / test-latest (push) Successful in 31s
- daynivo-config.json, daynivo-authorized_keys and daynivo-network-reset
  on the boot partition; errors go to daynivo-config.error.txt.
- daynivo-kiosk-ctl, with voxip-kiosk-ctl as a symlink to it.
- The voxip-* names keep working, so existing SD cards and instructions
  stay valid; when both config files are present the Daynivo one is
  applied last.
- Internal names (services, /etc/voxip-kiosk, user voxip) are unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-05 23:25:42 +01:00
106 changed files with 685 additions and 550 deletions

No files matched your search

+1 -1
View File
@@ -26,7 +26,7 @@ jobs:
- name: Tests - name: Tests
run: python3 -m pytest -p no:cacheprovider run: python3 -m pytest -p no:cacheprovider
- name: Shellcheck - 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: test-latest:
runs-on: ubuntu-latest runs-on: ubuntu-latest
+7 -7
View File
@@ -1,8 +1,8 @@
PY ?= .venv/bin/python PY ?= .venv/bin/python
DEV := $(CURDIR)/.dev DEV := $(CURDIR)/.dev
DEV_ENV := PYTHONPATH=src VOXIP_CONFIG_DIR=$(DEV)/etc VOXIP_STATE_DIR=$(DEV)/state VOXIP_LOG_DIR=$(DEV)/log \ DEV_ENV := PYTHONPATH=src DAYNIVO_CONFIG_DIR=$(DEV)/etc DAYNIVO_STATE_DIR=$(DEV)/state DAYNIVO_LOG_DIR=$(DEV)/log \
VOXIP_SHARE_DIR=$(DEV)/share VOXIP_AGENT_RUN_DIR=$(DEV)/run VOXIP_BROWSER_RUN_DIR=$(DEV)/run \ DAYNIVO_SHARE_DIR=$(DEV)/share DAYNIVO_AGENT_RUN_DIR=$(DEV)/run DAYNIVO_BROWSER_RUN_DIR=$(DEV)/run \
VOXIP_NETWORK_STATE_DIR=$(DEV)/net VOXIP_HELPER=/nonexistent/voxip-kiosk-helper DAYNIVO_NETWORK_STATE_DIR=$(DEV)/net DAYNIVO_HELPER=/nonexistent/daynivo-kiosk-helper
.PHONY: venv test lint test-bookworm dev image clean .PHONY: venv test lint test-bookworm dev image clean
@@ -13,8 +13,8 @@ test:
$(PY) -m pytest $(PY) -m pytest
lint: lint:
shellcheck -S warning install.sh update.sh uninstall.sh image/build.sh image/stage-voxip/prerun.sh \ shellcheck -S warning install.sh update.sh uninstall.sh image/build.sh image/stage-daynivo/prerun.sh \
image/stage-voxip/00-voxip/00-run.sh bin/voxip-kiosk-x 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. # The test suite against the exact Python/Flask/argon2 versions that Raspberry Pi OS Bookworm ships.
test-bookworm: test-bookworm:
@@ -22,7 +22,7 @@ test-bookworm:
apt-get update -qq && DEBIAN_FRONTEND=noninteractive apt-get install -y -qq --no-install-recommends \ 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 && \ 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 && \ 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). # Web interface on http://127.0.0.1:8080 with throwaway state in .dev/ (system actions fail gracefully).
dev: dev:
@@ -30,7 +30,7 @@ dev:
test -f $(DEV)/etc/config.json || cp config/config.default.json $(DEV)/etc/config.json test -f $(DEV)/etc/config.json || cp config/config.default.json $(DEV)/etc/config.json
cp VERSION $(DEV)/share/VERSION cp VERSION $(DEV)/share/VERSION
@echo "Setupcode: zie $(DEV)/etc/setup-code (wordt aangemaakt bij de start)" @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:
./image/build.sh ./image/build.sh
+56 -31
View File
@@ -12,7 +12,7 @@ webinterface **Daynivo Kiosk Manager** op `http://kiosk.local:8080`.
een setupcode die op het scherm staat. een setupcode die op het scherm staat.
- Netwerkwijzigingen met automatische rollback. - Netwerkwijzigingen met automatische rollback.
- Geen klantspecifieke code: alles per klant staat in - 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. worden meegegeven.
De architectuur en de veiligheidskeuzes staan in De architectuur en de veiligheidskeuzes staan in
@@ -31,7 +31,7 @@ hardware staat in [`docs/HARDWARE-TEST.md`](docs/HARDWARE-TEST.md).
3. Optioneel, in Raspberry Pi Imager → *OS-aanpassingen*: wifi, gebruiker 3. Optioneel, in Raspberry Pi Imager → *OS-aanpassingen*: wifi, gebruiker
met wachtwoord en/of SSH-sleutel, tijdzone. Zonder aanpassingen heeft de met wachtwoord en/of SSH-sleutel, tijdzone. Zonder aanpassingen heeft de
image **geen** bruikbaar wachtwoord en staat SSH uit. image **geen** bruikbaar wachtwoord en staat SSH uit.
4. Optioneel: zet een `voxip-config.json` op de bootpartitie (zie 4. Optioneel: zet een `daynivo-config.json` op de bootpartitie (zie
[Uitrol bij meerdere klanten](#uitrol-bij-meerdere-klanten)). [Uitrol bij meerdere klanten](#uitrol-bij-meerdere-klanten)).
5. Start de Pi. Bij de eerste boot wordt de rootpartitie automatisch 5. Start de Pi. Bij de eerste boot wordt de rootpartitie automatisch
vergroot tot de hele SD-kaart. Daarna verschijnt een setupscherm met de vergroot tot de hele SD-kaart. Daarna verschijnt een setupscherm met de
@@ -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, 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. start de kiosk. Aan het eind toont het de setupcode.
Draaide hier eerder de Daynivo-kiosk (`raspberry-pi/install-kiosk.sh` uit 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*, In de webinterface onder **Systeem → Software-updates**: *Nu controleren*,
*Bijwerken naar …*, en het vinkje **Automatisch bijwerken**. Elke nacht *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 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. ook op een apparaat dat met de image of vanuit een clone is geïnstalleerd.
Vanaf de commandoregel: Vanaf de commandoregel:
```bash ```bash
sudo /usr/lib/voxip-kiosk/voxip-kiosk-updater --check # alleen kijken sudo /usr/lib/daynivo-kiosk/daynivo-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 --install # nieuwste release installeren
``` ```
De updater installeert alleen een nieuwere versie met een kloppende De updater installeert alleen een nieuwere versie met een kloppende
@@ -89,15 +89,40 @@ het ook met git:
```bash ```bash
cd daynivo-kiosk-os 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 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 (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`. 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 ### Verwijderen
```bash ```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), 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 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. installatie.
Vereist: een Linux-buildmachine (x86_64 of arm64) met Docker en git. Op 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) | | `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) | | `RELEASE` | `bookworm` | `bookworm` of `trixie` (moet bij `PI_GEN_REF` passen) |
| `VOXIP_DEFAULT_URL` | `https://schermen-ijsselflat.voxip.nl` | standaard kiosk-URL | | `DAYNIVO_DEFAULT_URL` | `https://schermen-ijsselflat.voxip.nl` | standaard kiosk-URL |
| `VOXIP_DEVICE_NAME` | `Daynivo Kiosk` | standaard apparaatnaam | | `DAYNIVO_DEVICE_NAME` | `Daynivo Kiosk` | standaard apparaatnaam |
| `TARGET_HOSTNAME` | `kiosk` | hostname (`http://<hostname>.local:8080`) | | `TARGET_HOSTNAME` | `kiosk` | hostname (`http://<hostname>.local:8080`) |
| `ALLOW_DIRTY` | `0` | `1` = bouw de werkmap in plaats van de laatste commit | | `ALLOW_DIRTY` | `0` | `1` = bouw de werkmap in plaats van de laatste commit |
**Reproduceerbaarheid.** pi-gen staat vast op een tag en de broncode **Reproduceerbaarheid.** pi-gen staat vast op een tag en de broncode
gaat via `git archive` van één commit mee. Beide staan in `BUILD-INFO` 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 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 bouwen zijn. Er bestaat geen snapshot-archief voor Raspberry Pi OS, dus
twee builds op verschillende dagen kunnen nieuwere pakketversies bevatten. twee builds op verschillende dagen kunnen nieuwere pakketversies bevatten.
@@ -191,18 +216,18 @@ browser blijft draaien. *Chromium herstarten* herstart alleen de browser.
### Commandoregel (via SSH) ### Commandoregel (via SSH)
```bash ```bash
sudo voxip-kiosk-ctl status # overzicht sudo daynivo-kiosk-ctl status # overzicht
sudo voxip-kiosk-ctl setup-code # setupcode voor de eerste configuratie sudo daynivo-kiosk-ctl setup-code # setupcode voor de eerste configuratie
sudo voxip-kiosk-ctl reset-password # wachtwoord vergeten: nieuwe setupcode sudo daynivo-kiosk-ctl reset-password # wachtwoord vergeten: nieuwe setupcode
sudo voxip-kiosk-ctl set-url https://… # URL wijzigen sudo daynivo-kiosk-ctl set-url https://… # URL wijzigen
sudo voxip-kiosk-ctl reload # pagina herladen sudo daynivo-kiosk-ctl reload # pagina herladen
sudo voxip-kiosk-ctl restart-browser # Chromium herstarten sudo daynivo-kiosk-ctl restart-browser # Chromium herstarten
sudo voxip-kiosk-ctl health # healthcheck-rapport sudo daynivo-kiosk-ctl health # healthcheck-rapport
sudo voxip-kiosk-ctl maintenance on|off # healthcheck tijdelijk niets laten herstellen 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 | | Sleutel | Standaard | Betekenis |
|---|---|---| |---|---|---|
@@ -221,7 +246,7 @@ journalctl -u voxip-kiosk-browser -u voxip-kiosk-agent -f
| `admin_allow_public` | `false` | ook toegang vanaf publieke IP-adressen (afgeraden) | | `admin_allow_public` | `false` | ook toegang vanaf publieke IP-adressen (afgeraden) |
| `auto_update` | `false` | elke nacht een nieuwe release installeren | | `auto_update` | `false` | elke nacht een nieuwe release installeren |
Wijzigingen via de webinterface of `voxip-kiosk-ctl` worden atomair Wijzigingen via de webinterface of `daynivo-kiosk-ctl` worden atomair
opgeslagen, met een `.bak` van de vorige versie. Een URL-wijziging gaat in opgeslagen, met een `.bak` van de vorige versie. Een URL-wijziging gaat in
zonder dat Chromium herstart. Scherminstellingen gelden na een herstart van zonder dat Chromium herstart. Scherminstellingen gelden na een herstart van
Chromium (een vinkje in de webinterface doet dat direct). Chromium (een vinkje in de webinterface doet dat direct).
@@ -231,15 +256,15 @@ Chromium (een vinkje in de webinterface doet dat direct).
Eén image voor alle klanten. Per apparaat zet je na het flashen op de Eén image voor alle klanten. Per apparaat zet je na het flashen op de
bootpartitie (`bootfs`, zichtbaar op Windows en macOS): bootpartitie (`bootfs`, zichtbaar op Windows en macOS):
- **`voxip-config.json`**: instellingen voor deze kiosk, plus optioneel - **`daynivo-config.json`**: instellingen voor deze kiosk, plus optioneel
`hostname` en `wifi_country`. Een voorbeeld staat als `hostname` en `wifi_country`. Een voorbeeld staat als
`voxip-config.example.json` op de bootpartitie. `daynivo-config.example.json` op de bootpartitie.
- **`voxip-authorized_keys`**: openbare SSH-sleutels. SSH gaat dan aan, alleen - **`daynivo-authorized_keys`**: openbare SSH-sleutels. SSH gaat dan aan, alleen
met sleutel-login. met sleutel-login.
- Wifi en een eigen gebruiker stel je in via Raspberry Pi Imager. - Wifi en een eigen gebruiker stel je in via Raspberry Pi Imager.
De bestanden worden bij het opstarten verwerkt en hernoemd naar De bestanden worden bij het opstarten verwerkt en hernoemd naar
`*.applied`, of naar `*.rejected` met de reden in `voxip-config.error.txt`. `*.applied`, of naar `*.rejected` met de reden in `daynivo-config.error.txt`.
### Daynivo-schermen ### Daynivo-schermen
@@ -270,8 +295,8 @@ make dev # http://127.0.0.1:8080 met tijdelijke mappen in .dev/
| Symptoom | Oplossing | | Symptoom | Oplossing |
|---|---| |---|---|
| Zwart scherm na opstarten | `sudo voxip-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 `voxip-network-reset` op de bootpartitie en herstart. | | Beheerinterface onbereikbaar na netwerkwijziging | Wacht 3 minuten (automatische rollback) of zet `daynivo-network-reset` op de bootpartitie en herstart. |
| Wachtwoord vergeten | `sudo voxip-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. | | `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. | | Onderspanning in het dashboard | Gebruik de officiële 5,1 V/2,5 A-voeding. Onderspanning veroorzaakt crashes en SD-kaartcorruptie. |
+1 -1
View File
@@ -1 +1 @@
1.1.0 1.3.0
+9
View File
@@ -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())
+9
View File
@@ -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())
+9
View File
@@ -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.ctl import main # noqa: E402
sys.exit(main())
+9
View File
@@ -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())
+9
View File
@@ -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())
+9
View File
@@ -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())
+9
View File
@@ -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())
+1 -1
View File
@@ -2,7 +2,7 @@
# Starts the kiosk X server on vt7 for user "kiosk" (via Xorg.wrap). # Starts the kiosk X server on vt7 for user "kiosk" (via Xorg.wrap).
# A fresh MIT cookie per start; Chromium reads it via XAUTHORITY. # A fresh MIT cookie per start; Chromium reads it via XAUTHORITY.
set -eu set -eu
AUTH="${RUNTIME_DIRECTORY:-/run/voxip-kiosk-x}/Xauthority" AUTH="${RUNTIME_DIRECTORY:-/run/daynivo-kiosk-x}/Xauthority"
umask 077 umask 077
: > "$AUTH" : > "$AUTH"
xauth -q -f "$AUTH" add :0 . "$(mcookie)" xauth -q -f "$AUTH" add :0 . "$(mcookie)"
-9
View File
@@ -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())
-9
View File
@@ -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())
-9
View File
@@ -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.ctl import main # noqa: E402
sys.exit(main())
-9
View File
@@ -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())
-9
View File
@@ -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())
-9
View File
@@ -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())
-9
View File
@@ -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
View File
@@ -1,4 +1,4 @@
/var/log/voxip-kiosk/*.log { /var/log/daynivo-kiosk/*.log {
weekly weekly
rotate 8 rotate 8
maxsize 2M maxsize 2M
@@ -6,6 +6,6 @@
delaycompress delaycompress
missingok missingok
notifempty notifempty
create 0640 voxip voxip create 0640 daynivo daynivo
su voxip voxip 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.

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
View File
@@ -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. # as root: the restricted helper, which validates all of its input itself.
Defaults:voxip !requiretty Defaults:daynivo !requiretty
voxip ALL=(root) NOPASSWD: /usr/lib/voxip-kiosk/voxip-kiosk-helper daynivo ALL=(root) NOPASSWD: /usr/lib/daynivo-kiosk/daynivo-kiosk-helper
+32 -32
View File
@@ -11,33 +11,33 @@ samenwerken en waarom bepaalde keuzes zijn gemaakt.
- Eén HTTPS-pagina fullscreen in Chromium, zonder desktopomgeving. - Eén HTTPS-pagina fullscreen in Chromium, zonder desktopomgeving.
- Lokale webbeheerinterface (*Daynivo Kiosk Manager*) op poort 8080. - Lokale webbeheerinterface (*Daynivo Kiosk Manager*) op poort 8080.
- Geen klantspecifieke code: alles wat per klant verschilt staat in - 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 (`voxip-config.json`). worden meegegeven (`daynivo-config.json`).
## Overzicht ## Overzicht
``` ```
┌──────────────────────── Raspberry Pi ────────────────────────┐ ┌──────────────────────── 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 │ │ │ DISPLAY=:0 │
│ voxip-kiosk-browser.service Chromium --kiosk (user kiosk) │ │ daynivo-kiosk-browser.service Chromium --kiosk (user kiosk) │
│ ▲ CDP 127.0.0.1:9222 │ │ ▲ CDP 127.0.0.1:9222 │
│ │ ┌─────────────────────────────┐ │ │ │ ┌─────────────────────────────┐ │
│ ├──┤ voxip-kiosk-agent.service │ navigatie, offline-│ │ ├──┤ daynivo-kiosk-agent.service │ navigatie, offline-│
│ │ │ (user kiosk) │ pagina, verversen, │ │ │ │ (user kiosk) │ pagina, verversen, │
│ │ └─────────────────────────────┘ hang-detectie │ │ │ └─────────────────────────────┘ 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) │ │ │ sudo -n (één commando) │
│ ▼ │ │ ▼ │
│ voxip-kiosk-helper root, vaste werkwoorden, │ │ daynivo-kiosk-helper root, vaste werkwoorden, │
│ strikte invoervalidatie │ │ strikte invoervalidatie │
│ │ │ │
│ voxip-kiosk-healthcheck.timer elke minuut, root │ │ daynivo-kiosk-healthcheck.timer elke minuut, root │
│ voxip-network-guard.service netwerk-rollback bij boot │ │ daynivo-network-guard.service netwerk-rollback bij boot │
│ voxip-boot-import.service config/SSH-sleutels vanaf │ │ daynivo-boot-import.service config/SSH-sleutels vanaf │
│ de bootpartitie │ │ de bootpartitie │
└──────────────────────────────────────────────────────────────┘ └──────────────────────────────────────────────────────────────┘
``` ```
@@ -46,16 +46,16 @@ samenwerken en waarom bepaalde keuzes zijn gemaakt.
| Onderdeel | Gebruiker | Taak | | Onderdeel | Gebruiker | Taak |
|---|---|---| |---|---|---|
| `voxip-kiosk-x` | `kiosk` (via Xorg.wrap) | Minimale X-server op vt7, zonder cursor, zonder TCP. | | `daynivo-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. | | `daynivo-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. | | `daynivo-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). | | `daynivo-kiosk-manager` | `daynivo` | 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. | | `daynivo-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. | | `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. |
| `voxip-kiosk-healthcheck` | `root` | Bewaakt alle services en herstelt ze, met logging. | | `daynivo-kiosk-healthcheck` | `root` | Bewaakt alle services en herstelt ze, met logging. |
De webinterface draait **nooit** als root. De sudoers-regel staat 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); - draait met `python3 -I` (geen `PYTHONPATH`, geen user site-packages);
- kent alleen een vaste lijst werkwoorden; interne werkwoorden (zoals - 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* Xorg en Chromium zijn aparte services. Daardoor is *Chromium herstarten*
snel (X blijft draaien) en geeft het exitcode van Chromium direct aan snel (X blijft draaien) en geeft het exitcode van Chromium direct aan
systemd door. `voxip-kiosk-browser` heeft `Requires=` en `PartOf=` op systemd door. `daynivo-kiosk-browser` heeft `Requires=` en `PartOf=` op
`voxip-kiosk-x`: valt X weg, dan start de browser opnieuw mee. `daynivo-kiosk-x`: valt X weg, dan start de browser opnieuw mee.
### Chromium besturen via CDP ### Chromium besturen via CDP
@@ -86,7 +86,7 @@ webinterface gebruiken dit om:
### Offline-gedrag ### Offline-gedrag
Chromium start altijd op een lokale startpagina 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 HTTPS-verzoek (mét certificaatvalidatie) of de doel-URL bereikbaar is en
navigeert pas dan. De offline-pagina verschijnt alleen als de pagina niet navigeert pas dan. De offline-pagina verschijnt alleen als de pagina niet
**geladen** kan worden (startpagina, Chromium-foutpagina). Valt het **geladen** kan worden (startpagina, Chromium-foutpagina). Valt het
@@ -104,33 +104,33 @@ Zolang er geen beheerderswachtwoord is:
adressen van de beheerinterface; adressen van de beheerinterface;
3. kan een wachtwoord alleen ingesteld worden met die code. 3. kan een wachtwoord alleen ingesteld worden met die code.
Wie fysiek bij het scherm staat (of via SSH `voxip-kiosk-ctl setup-code` Wie fysiek bij het scherm staat (of via SSH `daynivo-kiosk-ctl setup-code`
draait) kan het apparaat dus claimen, iemand anders op het netwerk niet. draait) kan het apparaat dus claimen, iemand anders op het netwerk niet.
### Netwerkwijzigingen met rollback ### Netwerkwijzigingen met rollback
1. De helper maakt een snapshot van `/etc/NetworkManager/system-connections` 1. De helper maakt een snapshot van `/etc/NetworkManager/system-connections`
en van de actieve verbindingen. 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). 100) en plant een rollback-timer (`systemd-run`, 180 s).
3. Activatie gebeurt asynchroon, zodat het HTTP-antwoord nog aankomt. 3. Activatie gebeurt asynchroon, zodat het HTTP-antwoord nog aankomt.
4. De beheerder bevestigt binnen 180 s (eventueel via het nieuwe adres), 4. De beheerder bevestigt binnen 180 s (eventueel via het nieuwe adres),
anders zet de timer de snapshot terug. anders zet de timer de snapshot terug.
5. Valt de stroom uit tijdens die periode, dan zet 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. vóór NetworkManager start.
6. Noodherstel: een leeg bestand `voxip-network-reset` op de bootpartitie 6. Noodherstel: een leeg bestand `daynivo-network-reset` op de bootpartitie
verwijdert bij de volgende boot alle Daynivo-netwerkprofielen (terug naar verwijdert bij de volgende boot alle Daynivo-netwerkprofielen (terug naar
DHCP). DHCP).
### Configuratie ### 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` Wordt atomair opgeslagen (tijdelijk bestand, `fsync`, `rename`, `fsync`
van de map) met een `.bak` van de vorige versie. Ongeldige waarden vallen 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. bij het laden terug op de standaard, zodat de kiosk altijd start.
Onbekende sleutels blijven bewaard (vooruit-/achteruitcompatibel). 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 - Updates (`install.sh --upgrade` / `update.sh` / de updater) overschrijven
nooit bestaande configuratie. De updater draait in een eigen systemd-unit, nooit bestaande configuratie. De updater draait in een eigen systemd-unit,
zodat het herstarten van de beheerinterface tijdens `install.sh` de update zodat het herstarten van de beheerinterface tijdens `install.sh` de update
@@ -165,7 +165,7 @@ draait) kan het apparaat dus claimen, iemand anders op het netwerk niet.
(toegestaan: privé, link-local, loopback, CGNAT/VPN 100.64.0.0/10). (toegestaan: privé, link-local, loopback, CGNAT/VPN 100.64.0.0/10).
- Optioneel HTTPS (zelfondertekend certificaat, automatisch aangemaakt). - Optioneel HTTPS (zelfondertekend certificaat, automatisch aangemaakt).
- SSH staat in de image standaard uit en wordt alleen ingeschakeld met - SSH staat in de image standaard uit en wordt alleen ingeschakeld met
credentials uit Raspberry Pi Imager of `voxip-authorized_keys` op de credentials uit Raspberry Pi Imager of `daynivo-authorized_keys` op de
bootpartitie (dan alleen sleutel-login). bootpartitie (dan alleen sleutel-login).
- Strikte security-headers en CSP (`default-src 'self'`). - Strikte security-headers en CSP (`default-src 'self'`).
@@ -179,8 +179,8 @@ daynivo-kiosk-os/
├── install.sh installatie op bestaande Pi OS + gebruikt door image-build ├── install.sh installatie op bestaande Pi OS + gebruikt door image-build
├── update.sh update met backup van configuratie ├── update.sh update met backup van configuratie
├── uninstall.sh verwijderen (--purge voor alles) ├── uninstall.sh verwijderen (--purge voor alles)
├── bin/ dunne launchers → /usr/lib/voxip-kiosk/ ├── bin/ dunne launchers → /usr/lib/daynivo-kiosk/
├── src/voxip_kiosk/ Python-package (alle logica) ├── src/daynivo_kiosk/ Python-package (alle logica)
│ ├── config.py laden/valideren/atomair opslaan │ ├── config.py laden/valideren/atomair opslaan
│ ├── validation.py gedeelde invoervalidatie │ ├── validation.py gedeelde invoervalidatie
│ ├── cdp.py Chrome DevTools Protocol-client │ ├── cdp.py Chrome DevTools Protocol-client
@@ -192,12 +192,12 @@ daynivo-kiosk-os/
│ ├── netinfo.py netwerkstatus (zonder rechten) │ ├── netinfo.py netwerkstatus (zonder rechten)
│ ├── sysinfo.py CPU, temperatuur, geheugen, opslag, IP │ ├── sysinfo.py CPU, temperatuur, geheugen, opslag, IP
│ ├── services.py systemd- en journal-status │ ├── services.py systemd- en journal-status
│ ├── ctl.py CLI voor SSH-beheer (voxip-kiosk-ctl) │ ├── ctl.py CLI voor SSH-beheer (daynivo-kiosk-ctl)
│ └── web/ Flask-app, templates, statische bestanden │ └── web/ Flask-app, templates, statische bestanden
├── www/ lokale kioskpagina's (start, offline, setup) ├── www/ lokale kioskpagina's (start, offline, setup)
├── systemd/ unit-bestanden ├── systemd/ unit-bestanden
├── config/ standaardconfig, sudoers, journald, logrotate, … ├── config/ standaardconfig, sudoers, journald, logrotate, …
├── image/ pi-gen image-builder (stage-voxip) ├── image/ pi-gen image-builder (stage-daynivo)
├── tests/ pytest-suite ├── tests/ pytest-suite
└── docs/ architectuur, hardwaretest └── docs/ architectuur, hardwaretest
``` ```
+13 -13
View File
@@ -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, - [ ] 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 `http://kiosk.local:8080` en het IP-adres. Er is geen muiscursor en
er zijn geen dialoogvensters. 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. geen bruikbaar standaardwachtwoord.
## 2. Initiële configuratie ## 2. Initiële configuratie
@@ -47,23 +47,23 @@ Noteer per stap: ✅ / ❌ + opmerking, de imageversie en het schermtype.
(zie Logboek → Kiosk-agent). (zie Logboek → Kiosk-agent).
- [ ] Screenshot op de Kiosk-pagina toont wat er op het scherm staat. - [ ] Screenshot op de Kiosk-pagina toont wat er op het scherm staat.
- [ ] Daynivo-URL met `?token=`: het token is overal gemaskeerd - [ ] Daynivo-URL met `?token=`: het token is overal gemaskeerd
(dashboard, `voxip-kiosk-ctl status`, `journalctl -u voxip-kiosk-agent`). (dashboard, `daynivo-kiosk-ctl status`, `journalctl -u daynivo-kiosk-agent`).
## 4. Herstel ## 4. Herstel
- [ ] `sudo pkill -9 chromium`: binnen 15 s is de pagina terug. Het logboek - [ ] `sudo pkill -9 chromium`: binnen 15 s is de pagina terug. Het logboek
toont de herstart. toont de herstart.
- [ ] `sudo pkill -9 Xorg`: X en Chromium komen binnen 20 s terug. - [ ] `sudo pkill -9 Xorg`: X en Chromium komen binnen 20 s terug.
- [ ] Crashloop: `sudo voxip-kiosk-ctl set-url https://localhost:1/` en - [ ] Crashloop: `sudo daynivo-kiosk-ctl set-url https://localhost:1/` en
daarna 7× `sudo pkill -9 chromium` binnen 2 minuten. De service komt daarna 7× `sudo pkill -9 chromium` binnen 2 minuten. De service komt
in de fouttoestand (start-limit). De healthcheck logt een fout en start in de fouttoestand (start-limit). De healthcheck logt een fout en start
hem na ongeveer 5 minuten opnieuw. Er is geen eindeloze stille lus. hem na ongeveer 5 minuten opnieuw. Er is geen eindeloze stille lus.
Zet de URL daarna terug. Zet de URL daarna terug.
- [ ] Vastgelopen pagina: open via SSH-tunnel DevTools of zet een testpagina met - [ ] 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. `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 - [ ] De webinterface blijft werken terwijl Chromium gestopt is
(`sudo systemctl stop voxip-kiosk-browser`). (`sudo systemctl stop daynivo-kiosk-browser`).
## 5. Netwerk ## 5. Netwerk
@@ -82,7 +82,7 @@ Noteer per stap: ✅ / ❌ + opmerking, de imageversie en het schermtype.
- [ ] **Rollback na stroomuitval:** herhaal, trek binnen 3 minuten de stroom - [ ] **Rollback na stroomuitval:** herhaal, trek binnen 3 minuten de stroom
eruit. Na het opstarten zijn de oude instellingen actief. eruit. Na het opstarten zijn de oude instellingen actief.
- [ ] **Noodreset:** stel iets onbereikbaars in en bevestig het. Zet - [ ] **Noodreset:** stel iets onbereikbaars in en bevestig het. Zet
`voxip-network-reset` op de bootpartitie en start: weer DHCP. `daynivo-network-reset` op de bootpartitie en start: weer DHCP.
## 6. Systeem ## 6. Systeem
@@ -104,7 +104,7 @@ Noteer per stap: ✅ / ❌ + opmerking, de imageversie en het schermtype.
zonder ingrijpen terug in de kiosk, zonder "herstellen"-melding. zonder ingrijpen terug in de kiosk, zonder "herstellen"-melding.
- [ ] 72 uur continu draaien met verversinterval 5 min. Daarna: geheugen - [ ] 72 uur continu draaien met verversinterval 5 min. Daarna: geheugen
< 80% (dashboard), geen onderspanning, `journalctl -p err -b` zonder < 80% (dashboard), geen onderspanning, `journalctl -p err -b` zonder
terugkerende fouten, `voxip-kiosk-ctl status` alles *Actief*. terugkerende fouten, `daynivo-kiosk-ctl status` alles *Actief*.
- [ ] SD-kaartschrijfacties: `cat /proc/diskstats | grep mmcblk0` op t=0 en - [ ] SD-kaartschrijfacties: `cat /proc/diskstats | grep mmcblk0` op t=0 en
na 1 uur stationair. Het verschil in geschreven sectoren (kolom 10) is na 1 uur stationair. Het verschil in geschreven sectoren (kolom 10) is
minder dan 50 MB per uur. minder dan 50 MB per uur.
@@ -112,22 +112,22 @@ Noteer per stap: ✅ / ❌ + opmerking, de imageversie en het schermtype.
## 8. Uitrol en updates ## 8. Uitrol en updates
- [ ] Nieuwe kaart met `voxip-config.json` (url, device_name, hostname, - [ ] Nieuwe kaart met `daynivo-config.json` (url, device_name, hostname,
rotation) en `voxip-authorized_keys` op de bootpartitie: bij de eerste rotation) en `daynivo-authorized_keys` op de bootpartitie: bij de eerste
boot toegepast, hernoemd naar `.applied`, SSH alleen met sleutel. boot toegepast, hernoemd naar `.applied`, SSH alleen met sleutel.
- [ ] Ongeldige `voxip-config.json` (http-URL): `.rejected` en - [ ] Ongeldige `daynivo-config.json` (http-URL): `.rejected` en
`voxip-config.error.txt` met de reden. De kiosk start met de standaardconfiguratie. `daynivo-config.error.txt` met de reden. De kiosk start met de standaardconfiguratie.
- [ ] `sudo ./update.sh` vanuit een nieuwere checkout: versie in het dashboard - [ ] `sudo ./update.sh` vanuit een nieuwere checkout: versie in het dashboard
verhoogd, configuratie en wachtwoord ongewijzigd, back-up in `/var/backups`. verhoogd, configuratie en wachtwoord ongewijzigd, back-up in `/var/backups`.
- [ ] Systeem → Software-updates → *Nu controleren* met een nieuwere release - [ ] Systeem → Software-updates → *Nu controleren* met een nieuwere release
op Gitea: "beschikbaar". *Bijwerken naar …*: binnen enkele minuten is de op Gitea: "beschikbaar". *Bijwerken naar …*: binnen enkele minuten is de
nieuwe versie actief, de beheerinterface komt terug, configuratie en nieuwe versie actief, de beheerinterface komt terug, configuratie en
wachtwoord ongewijzigd. 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. 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 - [ ] `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. Raspberry Pi OS Lite: installeert de nieuwste release, setupcode op het scherm.
- [ ] `sudo ./uninstall.sh --purge` op een testkaart: geen Daynivo-services of - [ ] `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 - [ ] Installatie op een bestaande Raspberry Pi OS Lite (Bookworm) met de
Daynivo-kiosk erop: URL overgenomen, `display-kiosk.service` uitgeschakeld. Daynivo-kiosk erop: URL overgenomen, `display-kiosk.service` uitgeschakeld.
+7 -7
View File
@@ -4,11 +4,11 @@ Daynivo Kiosk OS — bestanden op deze bootpartitie
Je kunt een kiosk vooraf instellen door vóór de eerste start bestanden op Je kunt een kiosk vooraf instellen door vóór de eerste start bestanden op
deze partitie te zetten (bijvoorbeeld direct na het flashen). Ze worden bij deze partitie te zetten (bijvoorbeeld direct na het flashen). Ze worden bij
het opstarten verwerkt en daarna hernoemd naar *.applied (of *.rejected bij het opstarten verwerkt en daarna hernoemd naar *.applied (of *.rejected bij
een fout; de reden staat dan in voxip-config.error.txt). een fout; de reden staat dan in daynivo-config.error.txt).
voxip-config.json daynivo-config.json
Instellingen voor deze kiosk. Zie voxip-config.example.json. Toegestaan: 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: "wifi_country". Voorbeeld:
{ {
@@ -18,12 +18,12 @@ voxip-config.json
"rotation": 90 "rotation": 90
} }
voxip-authorized_keys daynivo-authorized_keys
Openbare SSH-sleutels (één per regel). SSH wordt dan ingeschakeld, alleen 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). in Raspberry Pi Imager hebt aangemaakt).
voxip-network-reset daynivo-network-reset
Leeg bestand. Verwijdert bij de volgende start alle via de Leeg bestand. Verwijdert bij de volgende start alle via de
beheerinterface ingestelde netwerkprofielen (terug naar DHCP). Gebruik beheerinterface ingestelde netwerkprofielen (terug naar DHCP). Gebruik
dit als de kiosk na een netwerkwijziging onbereikbaar is. dit als de kiosk na een netwerkwijziging onbereikbaar is.
+14 -13
View File
@@ -8,8 +8,8 @@
# Environment (all optional): # Environment (all optional):
# PI_GEN_REF pi-gen tag/branch (32-bit armhf). Default: pinned Bookworm release. # PI_GEN_REF pi-gen tag/branch (32-bit armhf). Default: pinned Bookworm release.
# RELEASE bookworm | trixie (must match PI_GEN_REF). Default: bookworm. # RELEASE bookworm | trixie (must match PI_GEN_REF). Default: bookworm.
# VOXIP_DEFAULT_URL kiosk URL baked into the default config. # DAYNIVO_DEFAULT_URL kiosk URL baked into the default config.
# VOXIP_DEVICE_NAME default device name. # DAYNIVO_DEVICE_NAME default device name.
# TARGET_HOSTNAME default hostname (the admin UI is at http://<hostname>.local:8080). # 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. # 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_REPO="${PI_GEN_REPO:-https://github.com/RPi-Distro/pi-gen.git}"
PI_GEN_REF="${PI_GEN_REF:-2024-11-19-raspios-bookworm-armhf}" PI_GEN_REF="${PI_GEN_REF:-2024-11-19-raspios-bookworm-armhf}"
RELEASE="${RELEASE:-bookworm}" RELEASE="${RELEASE:-bookworm}"
VOXIP_DEFAULT_URL="${VOXIP_DEFAULT_URL:-https://schermen-ijsselflat.voxip.nl}" # VOXIP_* are the names from before the rename and still work.
VOXIP_DEVICE_NAME="${VOXIP_DEVICE_NAME:-Daynivo Kiosk}" 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}" TARGET_HOSTNAME="${TARGET_HOSTNAME:-kiosk}"
NATIVE=0 NATIVE=0
[[ ${1:-} == "--native" ]] && NATIVE=1 [[ ${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)" PI_GEN_COMMIT="$(git -C "$WORK/pi-gen" rev-parse HEAD)"
PIGEN="$WORK/pi-gen" 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" touch "$PIGEN/stage2/SKIP_IMAGES"
rm -f "$PIGEN/stage2/EXPORT_NOOBS" rm -f "$PIGEN/stage2/EXPORT_NOOBS"
echo "==> Daynivo-stage voorbereiden" echo "==> Daynivo-stage voorbereiden"
cp -a "$ROOT/image/stage-voxip" "$PIGEN/stage-voxip" cp -a "$ROOT/image/stage-daynivo" "$PIGEN/stage-daynivo"
PAYLOAD="$PIGEN/stage-voxip/00-voxip/files/voxip-kiosk" PAYLOAD="$PIGEN/stage-daynivo/00-daynivo/files/daynivo-kiosk"
mkdir -p "$PAYLOAD" mkdir -p "$PAYLOAD"
if [[ ${ALLOW_DIRTY:-0} == 1 ]] || ! git -C "$ROOT" rev-parse --git-dir >/dev/null 2>&1; then 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)" echo " (werkmap wordt gebruikt, niet de laatste commit)"
@@ -67,12 +68,12 @@ else
fi fi
BUILD_DATE="$(date -u +%Y-%m-%d)" BUILD_DATE="$(date -u +%Y-%m-%d)"
cat > "$PIGEN/stage-voxip/00-voxip/files/build.env" <<EOF cat > "$PIGEN/stage-daynivo/00-daynivo/files/build.env" <<EOF
VOXIP_DEFAULT_URL=$(printf %q "$VOXIP_DEFAULT_URL") DAYNIVO_DEFAULT_URL=$(printf %q "$DAYNIVO_DEFAULT_URL")
VOXIP_DEVICE_NAME=$(printf %q "$VOXIP_DEVICE_NAME") DAYNIVO_DEVICE_NAME=$(printf %q "$DAYNIVO_DEVICE_NAME")
EOF EOF
cat > "$PAYLOAD/BUILD-INFO" <<EOF cat > "$PAYLOAD/BUILD-INFO" <<EOF
voxip_kiosk_os=$VERSION daynivo_kiosk_os=$VERSION
source_revision=$SOURCE_REV source_revision=$SOURCE_REV
pi_gen_ref=$PI_GEN_REF pi_gen_ref=$PI_GEN_REF
pi_gen_commit=$PI_GEN_COMMIT pi_gen_commit=$PI_GEN_COMMIT
@@ -97,11 +98,11 @@ KEYBOARD_LAYOUT="English (US)"
TIMEZONE_DEFAULT=Europe/Amsterdam TIMEZONE_DEFAULT=Europe/Amsterdam
WPA_COUNTRY=NL WPA_COUNTRY=NL
TARGET_HOSTNAME=$TARGET_HOSTNAME TARGET_HOSTNAME=$TARGET_HOSTNAME
FIRST_USER_NAME=voxipadmin FIRST_USER_NAME=daynivoadmin
FIRST_USER_PASS=$FIRST_USER_PASS FIRST_USER_PASS=$FIRST_USER_PASS
DISABLE_FIRST_BOOT_USER_RENAME=1 DISABLE_FIRST_BOOT_USER_RENAME=1
ENABLE_SSH=0 ENABLE_SSH=0
STAGE_LIST="stage0 stage1 stage2 stage-voxip" STAGE_LIST="stage0 stage1 stage2 stage-daynivo"
EOF EOF
echo "==> Image bouwen (dit duurt 30–90 minuten)" echo "==> Image bouwen (dit duurt 30–90 minuten)"
File renamed without changes.
+23
View File
@@ -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.
-23
View File
@@ -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/voxip-config.example.json "${ROOTFS_DIR}/boot/firmware/voxip-config.example.json"
+129 -58
View File
@@ -8,9 +8,9 @@
# #
# sudo ./install.sh [--url https://…] [--name "Hal begane grond"] [--hostname hal-bg] # sudo ./install.sh [--url https://…] [--name "Hal begane grond"] [--hostname hal-bg]
# sudo ./install.sh --upgrade # update code, keep configuration # 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 set -euo pipefail
@@ -21,15 +21,15 @@ NAME=""
NEW_HOSTNAME="" NEW_HOSTNAME=""
FORCE=0 FORCE=0
LIB=/usr/lib/voxip-kiosk LIB=/usr/lib/daynivo-kiosk
SHARE=/usr/share/voxip-kiosk SHARE=/usr/share/daynivo-kiosk
DOC=/usr/share/doc/voxip-kiosk DOC=/usr/share/doc/daynivo-kiosk
ETC=/etc/voxip-kiosk ETC=/etc/daynivo-kiosk
UNITS=(voxip-kiosk-x.service voxip-kiosk-browser.service voxip-kiosk-agent.service voxip-kiosk-manager.service UNITS=(daynivo-kiosk-x.service daynivo-kiosk-browser.service daynivo-kiosk-agent.service daynivo-kiosk-manager.service
voxip-kiosk-healthcheck.service voxip-kiosk-healthcheck.timer voxip-network-guard.service voxip-boot-import.service daynivo-kiosk-healthcheck.service daynivo-kiosk-healthcheck.timer daynivo-network-guard.service daynivo-boot-import.service
voxip-kiosk-update.service voxip-kiosk-update.timer) daynivo-kiosk-update.service daynivo-kiosk-update.timer)
ENABLE=(voxip-kiosk-x.service voxip-kiosk-browser.service voxip-kiosk-agent.service voxip-kiosk-manager.service ENABLE=(daynivo-kiosk-x.service daynivo-kiosk-browser.service daynivo-kiosk-agent.service daynivo-kiosk-manager.service
voxip-kiosk-healthcheck.timer voxip-network-guard.service voxip-boot-import.service voxip-kiosk-update.timer) 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' "$*"; } say() { printf '\033[1;34m==>\033[0m %s\n' "$*"; }
warn() { printf '\033[1;33mLet op:\033[0m %s\n' "$*" >&2; } warn() { printf '\033[1;33mLet op:\033[0m %s\n' "$*" >&2; }
@@ -54,7 +54,7 @@ while [[ $# -gt 0 ]]; do
done done
[[ $EUID -eq 0 ]] || die "Voer dit script uit met sudo." [[ $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() { in_chroot() {
if command -v systemd-detect-virt >/dev/null 2>&1 && systemd-detect-virt --chroot --quiet; then 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[@]}" 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 ---------------------------------------------------------------------- # --- users ----------------------------------------------------------------------
say "Gebruikers aanmaken" say "Gebruikers aanmaken"
getent group kiosk >/dev/null || groupadd --system kiosk 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 for g in video render input audio; do
getent group "$g" >/dev/null && usermod -aG "$g" kiosk getent group "$g" >/dev/null && usermod -aG "$g" kiosk
done done
getent group voxip >/dev/null || groupadd --system voxip getent group daynivo >/dev/null || groupadd --system daynivo
id voxip >/dev/null 2>&1 || useradd --system --gid voxip --home-dir /var/lib/voxip-kiosk --no-create-home \ 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" voxip --shell /usr/sbin/nologin --comment "Daynivo Kiosk Manager" daynivo
for g in systemd-journal video; do 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 done
# --- application files -------------------------------------------------------------- # --- application files --------------------------------------------------------------
@@ -126,7 +195,7 @@ say "Programmabestanden installeren"
install -d -m 0755 "$LIB" "$SHARE" "$DOC" install -d -m 0755 "$LIB" "$SHARE" "$DOC"
rm -rf "$LIB/python.new" rm -rf "$LIB/python.new"
install -d -m 0755 "$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" chown -R root:root "$LIB/python.new"
chmod -R u=rwX,go=rX "$LIB/python.new" chmod -R u=rwX,go=rX "$LIB/python.new"
python3 -m compileall -q "$LIB/python.new" >/dev/null python3 -m compileall -q "$LIB/python.new" >/dev/null
@@ -135,10 +204,12 @@ rm -rf "$LIB/python.old"
mv "$LIB/python.new" "$LIB/python" mv "$LIB/python.new" "$LIB/python"
rm -rf "$LIB/python.old" 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" install -m 0755 -o root -g root "$SRC/bin/$bin" "$LIB/$bin"
done done
install -m 0755 -o root -g root "$SRC/bin/voxip-kiosk-ctl" /usr/local/sbin/voxip-kiosk-ctl install -m 0755 -o root -g root "$SRC/bin/daynivo-kiosk-ctl" /usr/local/sbin/daynivo-kiosk-ctl
# The name from before the rename to Daynivo keeps working.
ln -sfn daynivo-kiosk-ctl /usr/local/sbin/voxip-kiosk-ctl
rm -rf "$SHARE/www" rm -rf "$SHARE/www"
install -d -m 0755 "$SHARE/www" install -d -m 0755 "$SHARE/www"
@@ -149,10 +220,10 @@ install -m 0644 "$SRC"/docs/*.md "$DOC/"
# --- configuration ------------------------------------------------------------------- # --- configuration -------------------------------------------------------------------
say "Configuratie" say "Configuratie"
install -d -o voxip -g kiosk -m 2750 "$ETC" install -d -o daynivo -g kiosk -m 2750 "$ETC"
install -d -o voxip -g voxip -m 0750 /var/lib/voxip-kiosk install -d -o daynivo -g daynivo -m 0750 /var/lib/daynivo-kiosk
install -d -o root -g root -m 0755 /var/lib/voxip-kiosk-network install -d -o root -g root -m 0755 /var/lib/daynivo-kiosk-network
install -d -o voxip -g voxip -m 0750 /var/log/voxip-kiosk 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. # 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 LEGACY_KIOSK_UNIT=/etc/systemd/system/display-kiosk.service
@@ -163,14 +234,14 @@ if [[ -f $LEGACY_KIOSK_UNIT ]]; then
say "URL overgenomen van de Daynivo-kioskinstallatie" say "URL overgenomen van de Daynivo-kioskinstallatie"
fi fi
if [[ $LIVE -eq 1 ]]; then systemctl disable --now display-kiosk.service 2>/dev/null || true; 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 fi
PYRUN=(python3 -I -c) PYRUN=(python3 -I -c)
"${PYRUN[@]}" ' "${PYRUN[@]}" '
import json, os, sys import json, os, sys
sys.path.insert(0, "/usr/lib/voxip-kiosk/python") sys.path.insert(0, "/usr/lib/daynivo-kiosk/python")
from voxip_kiosk import config as c from daynivo_kiosk import config as c
path, default, url, name = sys.argv[1:5] path, default, url, name = sys.argv[1:5]
updates = {} updates = {}
if url: updates["url"] = url if url: updates["url"] = url
@@ -192,36 +263,36 @@ else:
c.atomic_write_json(c.Path(path), data) c.atomic_write_json(c.Path(path), data)
print("Nieuwe configuratie aangemaakt") print("Nieuwe configuratie aangemaakt")
' "$ETC/config.json" "$SRC/config/config.default.json" "$URL" "$NAME" || die "Ongeldige --url of --name." ' "$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" chmod 0640 "$ETC/config.json"
touch "$ETC/.config.lock" && chown voxip:kiosk "$ETC/.config.lock" && chmod 0640 "$ETC/.config.lock" touch "$ETC/.config.lock" && chown daynivo:kiosk "$ETC/.config.lock" && chmod 0640 "$ETC/.config.lock"
[[ -f $ETC/admin.json ]] && chown voxip:voxip "$ETC/admin.json" && chmod 0600 "$ETC/admin.json" [[ -f $ETC/admin.json ]] && chown daynivo:daynivo "$ETC/admin.json" && chmod 0600 "$ETC/admin.json"
# --- system integration ---------------------------------------------------------------- # --- system integration ----------------------------------------------------------------
say "Systeemintegratie" say "Systeemintegratie"
TMP_SUDOERS="$(mktemp)" TMP_SUDOERS="$(mktemp)"
install -m 0440 "$SRC/config/sudoers" "$TMP_SUDOERS" install -m 0440 "$SRC/config/sudoers" "$TMP_SUDOERS"
visudo -cqf "$TMP_SUDOERS" || die "sudoers-bestand ongeldig" visudo -cqf "$TMP_SUDOERS" || die "sudoers-bestand ongeldig"
mv "$TMP_SUDOERS" /etc/sudoers.d/voxip-kiosk mv "$TMP_SUDOERS" /etc/sudoers.d/daynivo-kiosk
chown root:root /etc/sudoers.d/voxip-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/journald.conf" /etc/systemd/journald.conf.d/daynivo-kiosk.conf
install -D -m 0644 "$SRC/config/watchdog.conf" /etc/systemd/system.conf.d/voxip-watchdog.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/voxip-kiosk 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 if [[ -f /etc/X11/Xwrapper.config && ! -f /etc/X11/Xwrapper.config.daynivo-orig ]]; then
cp -a /etc/X11/Xwrapper.config /etc/X11/Xwrapper.config.voxip-orig cp -a /etc/X11/Xwrapper.config /etc/X11/Xwrapper.config.daynivo-orig
fi fi
install -D -m 0644 "$SRC/config/Xwrapper.config" /etc/X11/Xwrapper.config 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 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 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 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 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/voxip-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). # 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 if dpkg -s systemd-zram-generator >/dev/null 2>&1 && ! dpkg -s rpi-swap >/dev/null 2>&1; then
@@ -261,18 +332,18 @@ done
# Boot splash: logo on white (config/plymouth). Rebuilding the initramfs takes a while # Boot splash: logo on white (config/plymouth). Rebuilding the initramfs takes a while
# on a Pi 3, so only when the theme changed. # 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 -d -m 0755 "$SPLASH_DIR"
install -m 0644 "$SRC"/config/plymouth/voxip-kiosk/* "$SPLASH_DIR/" install -m 0644 "$SRC"/config/plymouth/daynivo-kiosk/* "$SPLASH_DIR/"
if [[ ! -f /var/lib/voxip-kiosk/previous-plymouth-theme ]]; then if [[ ! -f /var/lib/daynivo-kiosk/previous-plymouth-theme ]]; then
plymouth-set-default-theme > /var/lib/voxip-kiosk/previous-plymouth-theme 2>/dev/null || true plymouth-set-default-theme > /var/lib/daynivo-kiosk/previous-plymouth-theme 2>/dev/null || true
fi fi
SPLASH_SUM="$(cat "$SPLASH_DIR"/* | sha256sum | cut -d' ' -f1)" 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)" 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 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 else
warn "initramfs bijwerken mislukt; het opstartscherm verschijnt mogelijk pas later tijdens het opstarten." warn "initramfs bijwerken mislukt; het opstartscherm verschijnt mogelijk pas later tijdens het opstarten."
fi fi
@@ -284,8 +355,8 @@ for dm in lightdm.service gdm3.service sddm.service; do
if systemctl is-enabled --quiet "$dm" 2>/dev/null; then if systemctl is-enabled --quiet "$dm" 2>/dev/null; then
warn "$dm wordt uitgeschakeld (de kiosk gebruikt een eigen minimale X-sessie)." warn "$dm wordt uitgeschakeld (de kiosk gebruikt een eigen minimale X-sessie)."
systemctl disable "$dm" 2>/dev/null || true systemctl disable "$dm" 2>/dev/null || true
grep -qx "$dm" /var/lib/voxip-kiosk/disabled-display-managers 2>/dev/null \ grep -qx "$dm" /var/lib/daynivo-kiosk/disabled-display-managers 2>/dev/null \
|| echo "$dm" >> /var/lib/voxip-kiosk/disabled-display-managers || echo "$dm" >> /var/lib/daynivo-kiosk/disabled-display-managers
fi fi
if [[ $LIVE -eq 1 ]] && systemctl is-active --quiet "$dm" 2>/dev/null; then 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." warn "$dm wordt gestopt: de desktop op het scherm sluit nu af."
@@ -300,15 +371,15 @@ for unit in "${UNITS[@]}"; do
done done
if [[ $LIVE -eq 1 ]]; then systemctl daemon-reload; fi if [[ $LIVE -eq 1 ]]; then systemctl daemon-reload; fi
systemctl enable "${ENABLE[@]}" NetworkManager.service avahi-daemon.service >/dev/null systemctl enable "${ENABLE[@]}" NetworkManager.service avahi-daemon.service >/dev/null
if [[ ! -f /var/lib/voxip-kiosk/previous-default-target ]]; then if [[ ! -f /var/lib/daynivo-kiosk/previous-default-target ]]; then
systemctl get-default > /var/lib/voxip-kiosk/previous-default-target 2>/dev/null || true systemctl get-default > /var/lib/daynivo-kiosk/previous-default-target 2>/dev/null || true
fi fi
systemctl set-default multi-user.target >/dev/null systemctl set-default multi-user.target >/dev/null
if [[ -n $NEW_HOSTNAME && $LIVE -eq 0 ]]; then if [[ -n $NEW_HOSTNAME && $LIVE -eq 0 ]]; then
warn "--hostname wordt genegeerd in image-modus (gebruik TARGET_HOSTNAME van pi-gen)." warn "--hostname wordt genegeerd in image-modus (gebruik TARGET_HOSTNAME van pi-gen)."
elif [[ -n $NEW_HOSTNAME ]]; then 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 fi
if [[ $LIVE -eq 0 ]]; then if [[ $LIVE -eq 0 ]]; then
@@ -319,11 +390,11 @@ fi
say "Services starten" say "Services starten"
systemctl restart systemd-journald.service || true systemctl restart systemd-journald.service || true
systemctl restart avahi-daemon.service || true systemctl restart avahi-daemon.service || true
systemctl restart voxip-kiosk-manager.service voxip-kiosk-agent.service systemctl restart daynivo-kiosk-manager.service daynivo-kiosk-agent.service
systemctl start voxip-kiosk-healthcheck.timer voxip-kiosk-update.timer systemctl start daynivo-kiosk-healthcheck.timer daynivo-kiosk-update.timer
systemctl reset-failed voxip-kiosk-x.service voxip-kiosk-browser.service 2>/dev/null || true systemctl reset-failed daynivo-kiosk-x.service daynivo-kiosk-browser.service 2>/dev/null || true
systemctl restart voxip-kiosk-x.service systemctl restart daynivo-kiosk-x.service
systemctl restart voxip-kiosk-browser.service systemctl restart daynivo-kiosk-browser.service
HOST="$(hostname)" HOST="$(hostname)"
SCHEME=http SCHEME=http
@@ -340,4 +411,4 @@ if [[ ! -f $ETC/admin.json ]]; then
echo " Setupcode: $(cat "$ETC/setup-code") (staat ook op het scherm)" echo " Setupcode: $(cat "$ETC/setup-code") (staat ook op het scherm)"
fi fi
fi fi
echo " Status: sudo voxip-kiosk-ctl status" echo " Status: sudo daynivo-kiosk-ctl status"
+1 -1
View File
@@ -1,5 +1,5 @@
[project] [project]
name = "voxip-kiosk" name = "daynivo-kiosk"
dynamic = ["version"] dynamic = ["version"]
description = "Daynivo Kiosk OS — kiosk runtime and Daynivo Kiosk Manager" description = "Daynivo Kiosk OS — kiosk runtime and Daynivo Kiosk Manager"
requires-python = ">=3.11" 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 Runs as user ``kiosk`` and steers Chromium through the DevTools Protocol on
127.0.0.1: 127.0.0.1:
@@ -33,7 +33,7 @@ from .config import admin_url, load_config
from .paths import Paths, load_paths from .paths import Paths, load_paths
from .validation import mask_url from .validation import mask_url
log = logging.getLogger("voxip-kiosk-agent") log = logging.getLogger("daynivo-kiosk-agent")
TICK_SECONDS = 5 TICK_SECONDS = 5
PROBE_MAX_AGE_LOADING = 10 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 server is up. Exit status tells systemd whether to restart: 1 when
``browser_restart`` is enabled (``Restart=on-failure`` restarts it and the ``browser_restart`` is enabled (``Restart=on-failure`` restarts it and the
start limit catches crash loops), 0 when it is disabled. 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 .paths import Paths, load_paths
from .validation import mask_url, parse_resolution from .validation import mask_url, parse_resolution
log = logging.getLogger("voxip-kiosk-browser") log = logging.getLogger("daynivo-kiosk-browser")
CDP_PORT = 9222 CDP_PORT = 9222
CHROMIUM_CANDIDATES = ("/usr/bin/chromium-browser", "/usr/bin/chromium") 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 = data.setdefault("profile", {})
profile["exit_type"] = "Normal" profile["exit_type"] = "Normal"
profile["exited_cleanly"] = True 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") tmp.write_text(json.dumps(data), encoding="utf-8")
os.replace(tmp, prefs) os.replace(tmp, prefs)
def profile_dirs(config: dict[str, Any]) -> tuple[Path, Path]: def profile_dirs(config: dict[str, Any]) -> tuple[Path, Path]:
runtime = Path(os.environ.get("RUNTIME_DIRECTORY", "/run/voxip-kiosk-browser")) runtime = Path(os.environ.get("RUNTIME_DIRECTORY", "/run/daynivo-kiosk-browser"))
state = Path(os.environ.get("STATE_DIRECTORY", "/var/lib/voxip-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" profile = state / "profile" if config.get("browser_profile") == "persistent" else runtime / "profile"
if config.get("browser_profile") != "persistent" and (state / "profile").exists(): if config.get("browser_profile") != "persistent" and (state / "profile").exists():
shutil.rmtree(state / "profile", ignore_errors=True) 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") lock_path = path.with_name(".config.lock")
with _thread_lock: with _thread_lock:
# Read-only is enough for flock and works for every writer (the web # 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) lock_fd = os.open(str(lock_path), os.O_RDONLY | os.O_CREAT, 0o640)
try: try:
fcntl.flock(lock_fd, fcntl.LOCK_EX) fcntl.flock(lock_fd, fcntl.LOCK_EX)
@@ -1,4 +1,4 @@
"""voxip-kiosk-ctl — command-line management over SSH (run with sudo).""" """daynivo-kiosk-ctl — command-line management over SSH (run with sudo)."""
from __future__ import annotations from __future__ import annotations
@@ -33,7 +33,7 @@ def cmd_status(args) -> int:
print(f"Apparaat: {config['device_name']}") print(f"Apparaat: {config['device_name']}")
print(f"URL: {config['url'] if args.show_secrets else mask_url(config['url'])}") print(f"URL: {config['url'] if args.show_secrets else mask_url(config['url'])}")
print(f"Beheer: {'https' if config['admin_https'] else 'http'}://{os.uname().nodename}.local:{config['admin_port']}") print(f"Beheer: {'https' if config['admin_https'] else 'http'}://{os.uname().nodename}.local:{config['admin_port']}")
print(f"Wachtwoord: {'ingesteld' if paths.admin_file.exists() else 'NOG NIET INGESTELD (setupcode: sudo voxip-kiosk-ctl setup-code)'}") print(f"Wachtwoord: {'ingesteld' if paths.admin_file.exists() else 'NOG NIET INGESTELD (setupcode: sudo daynivo-kiosk-ctl setup-code)'}")
for key, info in services.summary().items(): for key, info in services.summary().items():
print(f"{info['unit']:<34} {info['label']} (herstarts: {info['restarts']})") print(f"{info['unit']:<34} {info['label']} (herstarts: {info['restarts']})")
if agent: if agent:
@@ -48,7 +48,7 @@ def cmd_setup_code(_args) -> int:
return 1 return 1
code = setupcode.read(paths.setup_code_file) code = setupcode.read(paths.setup_code_file)
if not code: 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 return 1
print(code) print(code)
return 0 return 0
@@ -65,10 +65,10 @@ def cmd_reset_password(args) -> int:
code = setupcode.generate() code = setupcode.generate()
setupcode.write(paths.setup_code_file, code) setupcode.write(paths.setup_code_file, code)
try: 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: except KeyError:
pass 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(f"Wachtwoord verwijderd. Nieuwe setupcode: {code}")
print("Open de beheerinterface en stel een nieuw wachtwoord in; de code staat ook op het kioskscherm.") print("Open de beheerinterface en stel een nieuw wachtwoord in; de code staat ook op het kioskscherm.")
return 0 return 0
@@ -138,7 +138,7 @@ def cmd_version(_args) -> int:
def main(argv: list[str] | None = None) -> int: def main(argv: list[str] | None = None) -> int:
parser = argparse.ArgumentParser(prog="voxip-kiosk-ctl", description="Beheer van Daynivo Kiosk OS vanaf de commandoregel.") parser = argparse.ArgumentParser(prog="daynivo-kiosk-ctl", description="Beheer van Daynivo Kiosk OS vanaf de commandoregel.")
sub = parser.add_subparsers(dest="command", required=True) sub = parser.add_subparsers(dest="command", required=True)
p = sub.add_parser("status", help="toon de status") p = sub.add_parser("status", help="toon de status")
p.add_argument("--show-secrets", action="store_true", help="toon de volledige URL inclusief token") p.add_argument("--show-secrets", action="store_true", help="toon de volledige URL inclusief token")
@@ -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 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 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 .config import load_config
from .paths import Paths, load_paths 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) ALWAYS_ON = (services.AGENT_UNIT, services.MANAGER_UNIT)
DISPLAY_UNITS = (services.X_UNIT, services.BROWSER_UNIT) DISPLAY_UNITS = (services.X_UNIT, services.BROWSER_UNIT)
@@ -147,7 +147,7 @@ class HealthCheck:
def main(argv: list[str] | None = None) -> int: 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("--check-only", action="store_true", help="alleen rapporteren, niets herstellen")
parser.add_argument("--json", action="store_true", help="rapport als JSON") parser.add_argument("--json", action="store_true", help="rapport als JSON")
args = parser.parse_args(argv) 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 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 every argument itself, never invokes a shell and prints a single JSON
object on stdout. 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 from __future__ import annotations
@@ -27,15 +27,17 @@ from . import netconfig, updater
from . import validation as v from . import validation as v
from .paths import Paths, default_paths from .paths import Paths, default_paths
HELPER_PATH = "/usr/lib/voxip-kiosk/voxip-kiosk-helper" HELPER_PATH = "/usr/lib/daynivo-kiosk/daynivo-kiosk-helper"
ROLLBACK_UNIT = "voxip-network-rollback" ROLLBACK_UNIT = "daynivo-network-rollback"
UPDATE_UNIT = "voxip-kiosk-update-now" UPDATE_UNIT = "daynivo-kiosk-update-now"
UPDATER_PATH = "/usr/lib/voxip-kiosk/voxip-kiosk-updater" UPDATER_PATH = "/usr/lib/daynivo-kiosk/daynivo-kiosk-updater"
ACTIVATE_UNIT = "voxip-network-activate" ACTIVATE_UNIT = "daynivo-network-activate"
BOOT_DIRS = (Path("/boot/firmware"), Path("/boot")) BOOT_DIRS = (Path("/boot/firmware"), Path("/boot"))
RESET_TRIGGER = "voxip-network-reset" # Files on the boot partition. The daynivo-* names are from before the rename to
IMPORT_CONFIG = "voxip-config.json" # Daynivo and keep working, so existing SD cards and instructions stay valid.
IMPORT_KEYS = "voxip-authorized_keys" RESET_TRIGGERS = ("daynivo-network-reset", "voxip-network-reset")
IMPORT_CONFIGS = ("voxip-config.json", "daynivo-config.json") # applied in this order: the Daynivo name wins
IMPORT_KEYS = ("voxip-authorized_keys", "daynivo-authorized_keys")
SSH_KEY_TYPES = ("ssh-ed25519", "ssh-rsa", "ecdsa-sha2-", "sk-ssh-ed25519@openssh.com", "sk-ecdsa-sha2-") SSH_KEY_TYPES = ("ssh-ed25519", "ssh-rsa", "ecdsa-sha2-", "sk-ssh-ed25519@openssh.com", "sk-ecdsa-sha2-")
# Verbs the web interface may call through sudo. Everything else is only # Verbs the web interface may call through sudo. Everything else is only
@@ -81,24 +83,24 @@ class Helper:
# -- power & browser ------------------------------------------------- # -- power & browser -------------------------------------------------
def reboot(self) -> dict[str, Any]: 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") log(syslog.LOG_NOTICE, "Herstart aangevraagd via beheerinterface")
return {"message": "De Raspberry Pi wordt over enkele seconden herstart."} return {"message": "De Raspberry Pi wordt over enkele seconden herstart."}
def poweroff(self) -> dict[str, Any]: 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") log(syslog.LOG_NOTICE, "Uitschakelen aangevraagd via beheerinterface")
return {"message": "De Raspberry Pi wordt over enkele seconden uitgeschakeld."} return {"message": "De Raspberry Pi wordt over enkele seconden uitgeschakeld."}
def restart_browser(self) -> dict[str, Any]: 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", "reset-failed", "daynivo-kiosk-browser.service", "daynivo-kiosk-x.service"], check=False)
self.run(["systemctl", "restart", "--no-block", "voxip-kiosk-browser.service"]) self.run(["systemctl", "restart", "--no-block", "daynivo-kiosk-browser.service"])
log(syslog.LOG_NOTICE, "Chromium wordt herstart") log(syslog.LOG_NOTICE, "Chromium wordt herstart")
return {"message": "Chromium wordt herstart."} return {"message": "Chromium wordt herstart."}
def restart_manager(self) -> dict[str, Any]: def restart_manager(self) -> dict[str, Any]:
# Delayed so the web interface can still answer the request that asked for it. # 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") log(syslog.LOG_NOTICE, "Beheerinterface wordt herstart")
return {"message": "De beheerinterface wordt herstart."} return {"message": "De beheerinterface wordt herstart."}
@@ -306,15 +308,16 @@ class Helper:
self.network_restore(boot=True) self.network_restore(boot=True)
result["rolled_back"] = True result["rolled_back"] = True
boot = self._boot_dir() boot = self._boot_dir()
trigger = boot / RESET_TRIGGER if boot else None triggers = [boot / name for name in RESET_TRIGGERS if (boot / name).exists()] if boot else []
if trigger and trigger.exists(): if triggers:
stash = self.paths.network_state_dir / f"disabled-{int(time.time())}" stash = self.paths.network_state_dir / f"disabled-{int(time.time())}"
stash.mkdir(parents=True, mode=0o700) stash.mkdir(parents=True, mode=0o700)
for iface in netconfig.INTERFACES: for iface in netconfig.INTERFACES:
src = self._keyfile_path(iface) src = self._keyfile_path(iface)
if src.exists(): if src.exists():
shutil.move(str(src), str(stash / src.name)) shutil.move(str(src), str(stash / src.name))
trigger.unlink() for trigger in triggers:
trigger.unlink()
log(syslog.LOG_WARNING, f"Netwerk-reset uitgevoerd; Daynivo-profielen verplaatst naar {stash}") log(syslog.LOG_WARNING, f"Netwerk-reset uitgevoerd; Daynivo-profielen verplaatst naar {stash}")
result["reset"] = True result["reset"] = True
return result return result
@@ -323,7 +326,7 @@ class Helper:
def _chown_config(self, path: Path) -> None: def _chown_config(self, path: Path) -> None:
try: try:
uid = pwd.getpwnam("voxip").pw_uid uid = pwd.getpwnam("daynivo").pw_uid
gid = grp.getgrnam("kiosk").gr_gid gid = grp.getgrnam("kiosk").gr_gid
except KeyError: except KeyError:
return return
@@ -335,12 +338,14 @@ class Helper:
if boot is None: if boot is None:
return {"imported": []} return {"imported": []}
imported = [] imported = []
conf = boot / IMPORT_CONFIG for name in IMPORT_CONFIGS:
if conf.exists(): conf = boot / name
if not conf.exists():
continue
try: try:
data = json.loads(conf.read_text(encoding="utf-8-sig")) data = json.loads(conf.read_text(encoding="utf-8-sig"))
if not isinstance(data, dict): if not isinstance(data, dict):
raise HelperError("voxip-config.json moet een JSON-object zijn") raise HelperError(f"{name} moet een JSON-object zijn")
data = dict(data) data = dict(data)
host = data.pop("hostname", None) host = data.pop("hostname", None)
country = data.pop("wifi_country", None) country = data.pop("wifi_country", None)
@@ -351,22 +356,24 @@ class Helper:
self.set_hostname(str(host)) self.set_hostname(str(host))
if country: if country:
self.set_wifi_country(str(country)) self.set_wifi_country(str(country))
conf.rename(conf.with_name(IMPORT_CONFIG + ".applied")) conf.rename(conf.with_name(name + ".applied"))
imported.append(IMPORT_CONFIG) imported.append(name)
log(syslog.LOG_NOTICE, "Configuratie geïmporteerd vanaf de bootpartitie") log(syslog.LOG_NOTICE, f"Configuratie geïmporteerd vanaf de bootpartitie ({name})")
except (HelperError, cfg.ConfigError, v.ValidationError, ValueError, OSError) as exc: except (HelperError, cfg.ConfigError, v.ValidationError, ValueError, OSError) as exc:
(boot / "voxip-config.error.txt").write_text(f"{exc}\n", encoding="utf-8") (boot / name.replace(".json", ".error.txt")).write_text(f"{exc}\n", encoding="utf-8")
conf.rename(conf.with_name(IMPORT_CONFIG + ".rejected")) conf.rename(conf.with_name(name + ".rejected"))
log(syslog.LOG_ERR, f"voxip-config.json geweigerd: {exc}") log(syslog.LOG_ERR, f"{name} geweigerd: {exc}")
keys = boot / IMPORT_KEYS for name in IMPORT_KEYS:
if keys.exists(): keys = boot / name
if not keys.exists():
continue
try: try:
self._import_ssh_keys(keys.read_text(encoding="utf-8-sig")) self._import_ssh_keys(keys.read_text(encoding="utf-8-sig"))
keys.rename(keys.with_name(IMPORT_KEYS + ".applied")) keys.rename(keys.with_name(name + ".applied"))
imported.append(IMPORT_KEYS) imported.append(name)
except (HelperError, OSError) as exc: except (HelperError, OSError) as exc:
keys.rename(keys.with_name(IMPORT_KEYS + ".rejected")) keys.rename(keys.with_name(name + ".rejected"))
log(syslog.LOG_ERR, f"SSH-sleutels geweigerd: {exc}") log(syslog.LOG_ERR, f"SSH-sleutels geweigerd ({name}): {exc}")
return {"imported": imported} return {"imported": imported}
def _import_ssh_keys(self, text: str) -> None: def _import_ssh_keys(self, text: str) -> None:
@@ -385,7 +392,7 @@ class Helper:
os.chown(ssh_dir, user.pw_uid, user.pw_gid) os.chown(ssh_dir, user.pw_uid, user.pw_gid)
sshd_dir = self.etc / "ssh" / "sshd_config.d" sshd_dir = self.etc / "ssh" / "sshd_config.d"
sshd_dir.mkdir(parents=True, exist_ok=True) 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" "# Beheerd door Daynivo Kiosk OS: alleen inloggen met SSH-sleutels.\n"
"PasswordAuthentication no\nKbdInteractiveAuthentication no\nPermitRootLogin no\n", "PasswordAuthentication no\nKbdInteractiveAuthentication no\nPermitRootLogin no\n",
encoding="utf-8", encoding="utf-8",
@@ -442,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: 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 argv = sys.argv[1:] if argv is None else argv
environ = os.environ if environ is None else environ 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: 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 return 2
verb, args = argv[0], argv[1:] verb, args = argv[0], argv[1:]
caller = environ.get("SUDO_USER") caller = environ.get("SUDO_USER")
@@ -453,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"})) print(json.dumps({"ok": False, "error": "Opdracht niet toegestaan"}))
return 1 return 1
if helper is None and os.geteuid() != 0: 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 return 1
helper = helper or Helper() helper = helper or Helper()
stdin = sys.stdin.read(16385) if verb == "network-apply" else "" 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 Reads port and HTTPS settings from the kiosk configuration, creates a
self-signed certificate when HTTPS is enabled and none exists yet, then 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 .config import load_config
from .paths import Paths, load_paths from .paths import Paths, load_paths
log = logging.getLogger("voxip-kiosk-manager") log = logging.getLogger("daynivo-kiosk-manager")
PYTHON_LIB = "/usr/lib/voxip-kiosk/python" PYTHON_LIB = "/usr/lib/daynivo-kiosk/python"
def ensure_certificate(paths: Paths) -> tuple[Path, Path]: 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"]: if config["admin_https"]:
cert, key = ensure_certificate(paths) cert, key = ensure_certificate(paths)
argv += ["--certfile", str(cert), "--keyfile", str(key)] argv += ["--certfile", str(cert), "--keyfile", str(key)]
argv.append("voxip_kiosk.web:create_app()") argv.append("daynivo_kiosk.web:create_app()")
return argv return argv
@@ -15,7 +15,7 @@ from typing import Any
from . import validation as v from . import validation as v
INTERFACES = {"eth0": "ethernet", "wlan0": "wifi"} INTERFACES = {"eth0": "ethernet", "wlan0": "wifi"}
PROFILE_PREFIX = "voxip-" PROFILE_PREFIX = "daynivo-"
ROLLBACK_SECONDS = 180 ROLLBACK_SECONDS = 180
File renamed without changes.
@@ -1,7 +1,7 @@
"""Filesystem locations used by every Daynivo Kiosk component. """Filesystem locations used by every Daynivo Kiosk component.
The defaults match the installed layout. Unprivileged components may 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 suite and for local development). The root helper always uses
:func:`default_paths` and never reads the environment. :func:`default_paths` and never reads the environment.
""" """
@@ -15,15 +15,15 @@ from pathlib import Path
@dataclass(frozen=True) @dataclass(frozen=True)
class Paths: class Paths:
config_dir: Path = Path("/etc/voxip-kiosk") config_dir: Path = Path("/etc/daynivo-kiosk")
state_dir: Path = Path("/var/lib/voxip-kiosk") state_dir: Path = Path("/var/lib/daynivo-kiosk")
network_state_dir: Path = Path("/var/lib/voxip-kiosk-network") network_state_dir: Path = Path("/var/lib/daynivo-kiosk-network")
agent_run_dir: Path = Path("/run/voxip-kiosk-agent") agent_run_dir: Path = Path("/run/daynivo-kiosk-agent")
browser_run_dir: Path = Path("/run/voxip-kiosk-browser") browser_run_dir: Path = Path("/run/daynivo-kiosk-browser")
health_run_dir: Path = Path("/run/voxip-kiosk-health") health_run_dir: Path = Path("/run/daynivo-kiosk-health")
share_dir: Path = Path("/usr/share/voxip-kiosk") share_dir: Path = Path("/usr/share/daynivo-kiosk")
log_dir: Path = Path("/var/log/voxip-kiosk") log_dir: Path = Path("/var/log/daynivo-kiosk")
helper: Path = Path("/usr/lib/voxip-kiosk/voxip-kiosk-helper") helper: Path = Path("/usr/lib/daynivo-kiosk/daynivo-kiosk-helper")
nm_connections_dir: Path = Path("/etc/NetworkManager/system-connections") nm_connections_dir: Path = Path("/etc/NetworkManager/system-connections")
@property @property
@@ -90,15 +90,15 @@ def default_paths() -> Paths:
_ENV = { _ENV = {
"config_dir": "VOXIP_CONFIG_DIR", "config_dir": "DAYNIVO_CONFIG_DIR",
"state_dir": "VOXIP_STATE_DIR", "state_dir": "DAYNIVO_STATE_DIR",
"network_state_dir": "VOXIP_NETWORK_STATE_DIR", "network_state_dir": "DAYNIVO_NETWORK_STATE_DIR",
"agent_run_dir": "VOXIP_AGENT_RUN_DIR", "agent_run_dir": "DAYNIVO_AGENT_RUN_DIR",
"browser_run_dir": "VOXIP_BROWSER_RUN_DIR", "browser_run_dir": "DAYNIVO_BROWSER_RUN_DIR",
"health_run_dir": "VOXIP_HEALTH_RUN_DIR", "health_run_dir": "DAYNIVO_HEALTH_RUN_DIR",
"share_dir": "VOXIP_SHARE_DIR", "share_dir": "DAYNIVO_SHARE_DIR",
"log_dir": "VOXIP_LOG_DIR", "log_dir": "DAYNIVO_LOG_DIR",
"helper": "VOXIP_HELPER", "helper": "DAYNIVO_HELPER",
} }
@@ -5,11 +5,11 @@ from __future__ import annotations
import subprocess import subprocess
from typing import Any from typing import Any
BROWSER_UNIT = "voxip-kiosk-browser.service" BROWSER_UNIT = "daynivo-kiosk-browser.service"
X_UNIT = "voxip-kiosk-x.service" X_UNIT = "daynivo-kiosk-x.service"
AGENT_UNIT = "voxip-kiosk-agent.service" AGENT_UNIT = "daynivo-kiosk-agent.service"
MANAGER_UNIT = "voxip-kiosk-manager.service" MANAGER_UNIT = "daynivo-kiosk-manager.service"
HEALTH_UNIT = "voxip-kiosk-healthcheck.service" HEALTH_UNIT = "daynivo-kiosk-healthcheck.service"
# Units whose logs the web interface may show (label → journalctl -u args). # Units whose logs the web interface may show (label → journalctl -u args).
LOG_SOURCES: dict[str, tuple[str, list[str]]] = { 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]), "agent": ("Kiosk-agent", [AGENT_UNIT]),
"health": ("Healthcheck", [HEALTH_UNIT]), "health": ("Healthcheck", [HEALTH_UNIT]),
"manager": ("Beheerinterface", [MANAGER_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", []), "system": ("Volledig systeem", []),
} }
LOG_LINES = (50, 100, 200, 500) 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: 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 ``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:: It never installs an older or equal version. Usage::
voxip-kiosk-updater --check # only check daynivo-kiosk-updater --check # only check
voxip-kiosk-updater --auto # check; install when auto_update is on (daily timer) daynivo-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 --install # check and install when newer (button in the web interface)
""" """
from __future__ import annotations from __future__ import annotations
@@ -39,12 +39,12 @@ from . import logutil
from .config import atomic_write_json, load_config from .config import atomic_write_json, load_config
from .paths import Paths, default_paths, read_version 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" RELEASE_API = "https://git.rothman.nl/api/v1/repos/rothman/daynivo-kiosk-os/releases/latest"
ASSET_PREFIX = "daynivo-kiosk-os-" ASSET_PREFIX = "daynivo-kiosk-os-"
RELEASE_DIR = Path("/opt/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 KEEP_RELEASES = 2
MAX_DOWNLOAD = 64 * 1024 * 1024 MAX_DOWNLOAD = 64 * 1024 * 1024
USER_AGENT = "Daynivo-Kiosk-OS/updater" USER_AGENT = "Daynivo-Kiosk-OS/updater"
@@ -219,7 +219,7 @@ class Updater:
def main(argv: list[str] | None = None, updater: Updater | None = None) -> int: 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 = parser.add_mutually_exclusive_group(required=True)
mode.add_argument("--check", action="store_true", help="alleen controleren") 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") 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 = Flask(__name__)
app.secret_key = _secret_key(paths) app.secret_key = _secret_key(paths)
app.config.update( app.config.update(
SESSION_COOKIE_NAME="voxip_session", SESSION_COOKIE_NAME="daynivo_session",
SESSION_COOKIE_HTTPONLY=True, SESSION_COOKIE_HTTPONLY=True,
SESSION_COOKIE_SAMESITE="Strict", SESSION_COOKIE_SAMESITE="Strict",
SESSION_COOKIE_SECURE=config["admin_https"], 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 = CredentialStore(paths.admin_file, paths.setup_code_file)
credentials.ensure_setup_code() credentials.ensure_setup_code()
app.extensions["voxip"] = Services( app.extensions["daynivo"] = Services(
paths=paths, paths=paths,
credentials=credentials, credentials=credentials,
throttle=LoginThrottle(), throttle=LoginThrottle(),
@@ -21,7 +21,7 @@ HASHER = PasswordHasher(time_cost=3, memory_cost=65536, parallelism=2, hash_len=
MIN_LENGTH = 10 MIN_LENGTH = 10
MAX_LENGTH = 128 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: 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

@@ -12,6 +12,6 @@
</label> </label>
<button type="submit" class="btn btn-primary btn-block">Inloggen</button> <button type="submit" class="btn btn-primary btn-block">Inloggen</button>
</form> </form>
<p class="muted small">Wachtwoord vergeten? Log via SSH in en voer <code>sudo voxip-kiosk-ctl reset-password</code> uit.</p> <p class="muted small">Wachtwoord vergeten? Log via SSH in en voer <code>sudo daynivo-kiosk-ctl reset-password</code> uit.</p>
</section> </section>
{% endblock %} {% endblock %}
@@ -94,7 +94,7 @@
<h2>Herstel bij een onbereikbare kiosk</h2> <h2>Herstel bij een onbereikbare kiosk</h2>
<ul class="plain"> <ul class="plain">
<li>Iedere wijziging wordt na {{ rollback_seconds // 60 }} minuten automatisch teruggedraaid als je hem niet bevestigt, ook als de stroom tussendoor uitvalt.</li> <li>Iedere wijziging wordt na {{ rollback_seconds // 60 }} minuten automatisch teruggedraaid als je hem niet bevestigt, ook als de stroom tussendoor uitvalt.</li>
<li>Noodherstel: zet een leeg bestand <code>voxip-network-reset</code> op de bootpartitie van de SD-kaart en start de Pi. Alle Daynivo-netwerkprofielen worden dan verwijderd (terug naar DHCP).</li> <li>Noodherstel: zet een leeg bestand <code>daynivo-network-reset</code> op de bootpartitie van de SD-kaart en start de Pi. Alle Daynivo-netwerkprofielen worden dan verwijderd (terug naar DHCP).</li>
</ul> </ul>
</section> </section>
{% endblock %} {% endblock %}
@@ -4,7 +4,7 @@
<section class="card card-narrow"> <section class="card card-narrow">
<h1>Welkom bij Daynivo Kiosk Manager</h1> <h1>Welkom bij Daynivo Kiosk Manager</h1>
<p>Stel eerst een uniek beheerderswachtwoord in. Je hebt daarvoor de <strong>setupcode</strong> nodig die op het <p>Stel eerst een uniek beheerderswachtwoord in. Je hebt daarvoor de <strong>setupcode</strong> nodig die op het
scherm van de kiosk staat (of via SSH: <code>sudo voxip-kiosk-ctl setup-code</code>).</p> scherm van de kiosk staat (of via SSH: <code>sudo daynivo-kiosk-ctl setup-code</code>).</p>
{% if error %}<div class="alert alert-error" role="alert">{{ error }}</div>{% endif %} {% if error %}<div class="alert alert-error" role="alert">{{ error }}</div>{% endif %}
<form method="post" class="form"> <form method="post" class="form">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}"> <input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
@@ -22,14 +22,14 @@ from . import security
from .auth import password_problem from .auth import password_problem
bp = Blueprint("ui", __name__) bp = Blueprint("ui", __name__)
audit_log = logging.getLogger("voxip-kiosk-audit") audit_log = logging.getLogger("daynivo-kiosk-audit")
IDLE_TIMEOUT = 3600 IDLE_TIMEOUT = 3600
PUBLIC_ENDPOINTS = {"ui.login", "ui.setup", "static"} PUBLIC_ENDPOINTS = {"ui.login", "ui.setup", "static"}
def svc(): def svc():
return current_app.extensions["voxip"] return current_app.extensions["daynivo"]
def cfg() -> dict[str, Any]: def cfg() -> dict[str, Any]:
+21
View File
@@ -0,0 +1,21 @@
[Unit]
Description=Daynivo configuratie en SSH-sleutels importeren vanaf de bootpartitie
Documentation=file:///usr/share/doc/daynivo-kiosk/README.md
After=local-fs.target
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
ConditionPathExistsGlob=|/boot/daynivo-authorized_keys
ConditionPathExistsGlob=|/boot/firmware/voxip-config.json
ConditionPathExistsGlob=|/boot/firmware/voxip-authorized_keys
ConditionPathExistsGlob=|/boot/voxip-config.json
ConditionPathExistsGlob=|/boot/voxip-authorized_keys
[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/usr/lib/daynivo-kiosk/daynivo-kiosk-helper boot-import
[Install]
WantedBy=multi-user.target
@@ -1,18 +1,18 @@
[Unit] [Unit]
Description=Daynivo Kiosk agent (navigatie, offlinepagina, verversen) Description=Daynivo Kiosk agent (navigatie, offlinepagina, verversen)
Documentation=file:///usr/share/doc/voxip-kiosk/README.md Documentation=file:///usr/share/doc/daynivo-kiosk/README.md
After=voxip-kiosk-browser.service After=daynivo-kiosk-browser.service
[Service] [Service]
Type=simple Type=simple
User=kiosk User=kiosk
Group=kiosk Group=kiosk
RuntimeDirectory=voxip-kiosk-agent RuntimeDirectory=daynivo-kiosk-agent
RuntimeDirectoryMode=0755 RuntimeDirectoryMode=0755
ExecStart=/usr/lib/voxip-kiosk/voxip-kiosk-agent ExecStart=/usr/lib/daynivo-kiosk/daynivo-kiosk-agent
Restart=always Restart=always
RestartSec=5 RestartSec=5
SyslogIdentifier=voxip-kiosk-agent SyslogIdentifier=daynivo-kiosk-agent
NoNewPrivileges=yes NoNewPrivileges=yes
ProtectSystem=strict ProtectSystem=strict
ProtectHome=yes ProtectHome=yes
+36
View File
@@ -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
+11
View File
@@ -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
@@ -1,27 +1,27 @@
[Unit] [Unit]
Description=Daynivo Kiosk Manager (webbeheer) Description=Daynivo Kiosk Manager (webbeheer)
Documentation=file:///usr/share/doc/voxip-kiosk/README.md Documentation=file:///usr/share/doc/daynivo-kiosk/README.md
After=network.target voxip-boot-import.service After=network.target daynivo-boot-import.service
StartLimitIntervalSec=300 StartLimitIntervalSec=300
StartLimitBurst=10 StartLimitBurst=10
[Service] [Service]
Type=simple Type=simple
User=voxip User=daynivo
Group=voxip Group=daynivo
# systemd-journal: read logs; video: vcgencmd (voltage/throttling) # systemd-journal: read logs; video: vcgencmd (voltage/throttling)
SupplementaryGroups=systemd-journal video SupplementaryGroups=systemd-journal video
ExecStart=/usr/lib/voxip-kiosk/voxip-kiosk-manager ExecStart=/usr/lib/daynivo-kiosk/daynivo-kiosk-manager
Restart=always Restart=always
RestartSec=3 RestartSec=3
UMask=0027 UMask=0027
LogsDirectory=voxip-kiosk LogsDirectory=daynivo-kiosk
LogsDirectoryMode=0750 LogsDirectoryMode=0750
SyslogIdentifier=voxip-kiosk-manager SyslogIdentifier=daynivo-kiosk-manager
# No NoNewPrivileges/ProtectSystem/ProtectHome: the helper started through # 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 # 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, # Also no ProtectKernelTunables/ProtectKernelModules/LockPersonality,
# RestrictSUIDSGID, SystemCall*, ...: for a non-root unit those imply # RestrictSUIDSGID, SystemCall*, ...: for a non-root unit those imply
# NoNewPrivileges, and sudo then refuses to run (tests/test_units.py). # NoNewPrivileges, and sudo then refuses to run (tests/test_units.py).
@@ -1,11 +1,11 @@
[Unit] [Unit]
Description=Daynivo Kiosk OS: controleren op updates (en installeren als dat aan staat) 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 Wants=network-online.target
After=network-online.target After=network-online.target
[Service] [Service]
Type=oneshot Type=oneshot
ExecStart=/usr/lib/voxip-kiosk/voxip-kiosk-updater --auto ExecStart=/usr/lib/daynivo-kiosk/daynivo-kiosk-updater --auto
TimeoutStartSec=1h TimeoutStartSec=1h
SyslogIdentifier=voxip-kiosk-updater SyslogIdentifier=daynivo-kiosk-updater
File renamed without changes.
@@ -1,6 +1,6 @@
[Unit] [Unit]
Description=Daynivo Kiosk X-server (vt7) 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 After=systemd-user-sessions.service systemd-logind.service
Conflicts=getty@tty7.service Conflicts=getty@tty7.service
StartLimitIntervalSec=600 StartLimitIntervalSec=600
@@ -20,10 +20,10 @@ StandardOutput=journal
StandardError=journal StandardError=journal
UtmpIdentifier=tty7 UtmpIdentifier=tty7
UtmpMode=user UtmpMode=user
RuntimeDirectory=voxip-kiosk-x RuntimeDirectory=daynivo-kiosk-x
RuntimeDirectoryMode=0700 RuntimeDirectoryMode=0700
ExecStartPre=+/usr/bin/chvt 7 ExecStartPre=+/usr/bin/chvt 7
ExecStart=/usr/lib/voxip-kiosk/voxip-kiosk-x ExecStart=/usr/lib/daynivo-kiosk/daynivo-kiosk-x
Restart=always Restart=always
RestartSec=3 RestartSec=3
@@ -1,6 +1,6 @@
[Unit] [Unit]
Description=Daynivo netwerkherstel bij opstarten (rollback en noodreset) 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 DefaultDependencies=no
After=local-fs.target After=local-fs.target
Before=NetworkManager.service network-pre.target shutdown.target Before=NetworkManager.service network-pre.target shutdown.target
@@ -10,7 +10,7 @@ Conflicts=shutdown.target
[Service] [Service]
Type=oneshot Type=oneshot
RemainAfterExit=yes 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] [Install]
WantedBy=multi-user.target WantedBy=multi-user.target
-17
View File
@@ -1,17 +0,0 @@
[Unit]
Description=Daynivo configuratie en SSH-sleutels importeren vanaf de bootpartitie
Documentation=file:///usr/share/doc/voxip-kiosk/README.md
After=local-fs.target
Before=voxip-kiosk-manager.service voxip-kiosk-browser.service ssh.service
ConditionPathExistsGlob=|/boot/firmware/voxip-config.json
ConditionPathExistsGlob=|/boot/firmware/voxip-authorized_keys
ConditionPathExistsGlob=|/boot/voxip-config.json
ConditionPathExistsGlob=|/boot/voxip-authorized_keys
[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/usr/lib/voxip-kiosk/voxip-kiosk-helper boot-import
[Install]
WantedBy=multi-user.target
-36
View File
@@ -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
-11
View File
@@ -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
View File
@@ -4,7 +4,7 @@ from pathlib import Path
import pytest import pytest
from voxip_kiosk.paths import Paths from daynivo_kiosk.paths import Paths
ROOT = Path(__file__).resolve().parents[1] ROOT = Path(__file__).resolve().parents[1]
@@ -39,7 +39,7 @@ class FakeHelper:
self.responses = {} self.responses = {}
def call(self, verb, *args, payload=None, timeout=60): 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)) self.calls.append((verb, args, payload))
if self.fail: if self.fail:
@@ -53,7 +53,7 @@ class FakeCDP:
self.fail = None self.fail = None
def _do(self, name, *args): def _do(self, name, *args):
from voxip_kiosk.cdp import CDPError from daynivo_kiosk.cdp import CDPError
self.calls.append((name, args)) self.calls.append((name, args))
if self.fail: if self.fail:
@@ -80,7 +80,7 @@ def fake_cdp():
@pytest.fixture @pytest.fixture
def quiet_system(monkeypatch): def quiet_system(monkeypatch):
"""Keep the web tests independent of the machine they run on.""" """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: { monkeypatch.setattr(services, "summary", lambda: {
"browser": {"unit": services.BROWSER_UNIT, "status": "ok", "label": "Actief", "restarts": "2", "since": ""}, "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", "available": True, "wifi_country": "NL",
"devices": { "devices": {
iface: {"interface": iface, "kind": kind, "present": True, "state": "connected", "state_label": "Verbonden", 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", "dns": ["192.168.10.1"], "hwaddr": "B8:27:EB:00:00:01",
"profile": {"exists": True, "method": "dhcp", "address": "", "gateway": "", "dns": "", "ssid": "Kantoor", "hidden": False}} "profile": {"exists": True, "method": "dhcp", "address": "", "gateway": "", "dns": "", "ssid": "Kantoor", "hidden": False}}
for iface, kind in (("eth0", "ethernet"), ("wlan0", "wifi")) for iface, kind in (("eth0", "ethernet"), ("wlan0", "wifi"))
@@ -106,7 +106,7 @@ def quiet_system(monkeypatch):
@pytest.fixture @pytest.fixture
def app(paths, fake_helper, fake_cdp, quiet_system): 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 = create_app(paths, helper=fake_helper, cdp=fake_cdp)
application.config["TESTING"] = True application.config["TESTING"] = True
@@ -130,7 +130,7 @@ PASSWORD = "Correct-Paard-Batterij-42"
@pytest.fixture @pytest.fixture
def logged_in(app, client, paths): def logged_in(app, client, paths):
"""A client with a configured admin password and an active session.""" """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") page = client.get("/login")
token = csrf_from(page) token = csrf_from(page)
resp = client.post("/login", data={"password": PASSWORD, "csrf_token": token}) resp = client.post("/login", data={"password": PASSWORD, "csrf_token": token})
+3 -3
View File
@@ -2,9 +2,9 @@ import json
import pytest import pytest
from voxip_kiosk.agent import Agent, HUNG_RELOAD_AFTER, HUNG_RESTART_AFTER from daynivo_kiosk.agent import Agent, HUNG_RELOAD_AFTER, HUNG_RESTART_AFTER
from voxip_kiosk.cdp import CDPError from daynivo_kiosk.cdp import CDPError
from voxip_kiosk.config import save_config from daynivo_kiosk.config import save_config
class Clock: class Clock:
+2 -2
View File
@@ -2,8 +2,8 @@ import json
from pathlib import Path from pathlib import Path
from urllib.parse import parse_qs, urlsplit from urllib.parse import parse_qs, urlsplit
from voxip_kiosk import browser as b from daynivo_kiosk import browser as b
from voxip_kiosk.config import DEFAULTS from daynivo_kiosk.config import DEFAULTS
XRANDR = """Screen 0: minimum 320 x 200, current 1920 x 1080, maximum 7680 x 7680 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 HDMI-1 connected primary 1920x1080+0+0 (normal left inverted right x axis y axis) 531mm x 299mm
+1 -1
View File
@@ -4,7 +4,7 @@ import stat
import pytest import pytest
from voxip_kiosk import config as c from daynivo_kiosk import config as c
def test_default_file_matches_defaults(paths): def test_default_file_matches_defaults(paths):
+4 -4
View File
@@ -1,9 +1,9 @@
import pytest import pytest
from voxip_kiosk import services from daynivo_kiosk import services
from voxip_kiosk.cdp import CDPError from daynivo_kiosk.cdp import CDPError
from voxip_kiosk.config import save_config from daynivo_kiosk.config import save_config
from voxip_kiosk.healthcheck import CDP_FAILURES_BEFORE_RESTART, DISPLAY_RETRY_AFTER, HealthCheck from daynivo_kiosk.healthcheck import CDP_FAILURES_BEFORE_RESTART, DISPLAY_RETRY_AFTER, HealthCheck
class World: class World:
+46 -27
View File
@@ -3,8 +3,8 @@ import subprocess
import pytest import pytest
from voxip_kiosk import helper as h from daynivo_kiosk import helper as h
from voxip_kiosk.config import load_config from daynivo_kiosk.config import load_config
class Recorder: class Recorder:
@@ -35,7 +35,7 @@ def helper(paths, tmp_path, boot):
etc.mkdir() etc.mkdir()
(etc / "hosts").write_text("127.0.0.1\tlocalhost\n127.0.1.1\tkiosk\n") (etc / "hosts").write_text("127.0.0.1\tlocalhost\n127.0.1.1\tkiosk\n")
run = Recorder({"nmcli -t -f": "aaaa-1111\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): 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): 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 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 code == 1
assert helper.run.commands == [] assert helper.run.commands == []
def test_public_verb_via_sudo(helper, capsys): 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 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): 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): 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") (paths.nm_connections_dir / "Wired connection 1.nmconnection").write_text("[connection]\nid=Wired connection 1\n")
result = helper.network_apply(ETH_STATIC) 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 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 "address1=192.168.5.10/24,192.168.5.1" in keyfile.read_text()
assert (paths.network_backup_dir / "Wired connection 1.nmconnection").exists() 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 pending["deadline"] - pending["created"] == 180
assert result["pending"]["interface"] == "eth0" assert result["pending"]["interface"] == "eth0"
runs = [c for c in helper.run.commands if c[0] == "systemd-run"] 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[0][1:3] == ["--unit=daynivo-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[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): def test_second_apply_refused_while_pending(helper):
@@ -123,8 +123,8 @@ def test_confirm_clears_pending(helper, paths):
helper.network_confirm() helper.network_confirm()
assert not paths.network_pending_file.exists() assert not paths.network_pending_file.exists()
assert not paths.network_backup_dir.exists() assert not paths.network_backup_dir.exists()
assert (paths.nm_connections_dir / "voxip-eth0.nmconnection").exists() assert (paths.nm_connections_dir / "daynivo-eth0.nmconnection").exists()
assert helper.run.ran("systemctl", "stop", "voxip-network-rollback.timer") assert helper.run.ran("systemctl", "stop", "daynivo-network-rollback.timer")
def test_restore_puts_old_profiles_back(helper, paths): def test_restore_puts_old_profiles_back(helper, paths):
@@ -134,7 +134,7 @@ def test_restore_puts_old_profiles_back(helper, paths):
old.unlink() old.unlink()
helper.network_restore() helper.network_restore()
assert old.exists() 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 not paths.network_pending_file.exists()
assert helper.run.ran("nmcli", "--wait", "30", "connection", "up", "uuid", "aaaa-1111") 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_confirm()
helper.network_apply({"interface": "wlan0", "method": "static", "address": "10.0.0.9/24", "gateway": "10.0.0.1", "dns": "10.0.0.1", 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": ""}}) "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() helper.network_confirm()
with pytest.raises(h.HelperError, match="wachtwoord"): with pytest.raises(h.HelperError, match="wachtwoord"):
helper.network_apply({"interface": "wlan0", "method": "dhcp", "wifi": {"ssid": "Ander", "psk": ""}}) helper.network_apply({"interface": "wlan0", "method": "dhcp", "wifi": {"ssid": "Ander", "psk": ""}})
@@ -165,39 +165,58 @@ def test_boot_guard_rolls_back_and_honours_reset(helper, paths, boot):
helper.run.commands.clear() helper.run.commands.clear()
result = helper.network_boot_guard() result = helper.network_boot_guard()
assert result == {"rolled_back": True} 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" 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 / "voxip-network-reset").write_text("") (boot / "daynivo-network-reset").write_text("")
assert helper.network_boot_guard() == {"reset": True} 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 / "voxip-network-reset").exists() assert not (boot / "daynivo-network-reset").exists()
assert old.exists() assert old.exists()
def test_boot_guard_still_honours_the_old_reset_name(helper, paths, boot):
(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 / "daynivo-eth0.nmconnection").exists()
assert not (boot / "voxip-network-reset").exists()
def test_boot_import_applies_config(helper, paths, boot): def test_boot_import_applies_config(helper, paths, boot):
(boot / "voxip-config.json").write_text(json.dumps({"device_name": "Hal", "url": "https://klant.example.nl/display/hal?token=x", (boot / "daynivo-config.json").write_text(json.dumps({"device_name": "Hal", "url": "https://klant.example.nl/display/hal?token=x",
"hostname": "hal", "rotation": 90})) "hostname": "hal", "rotation": 90}))
assert helper.boot_import() == {"imported": ["voxip-config.json"]} assert helper.boot_import() == {"imported": ["daynivo-config.json"]}
conf = load_config(paths.config_file) conf = load_config(paths.config_file)
assert conf["device_name"] == "Hal" and conf["rotation"] == 90 and conf["url"].startswith("https://klant") assert conf["device_name"] == "Hal" and conf["rotation"] == 90 and conf["url"].startswith("https://klant")
assert (helper.etc / "hostname").read_text() == "hal\n" assert (helper.etc / "hostname").read_text() == "hal\n"
assert (boot / "daynivo-config.json.applied").exists()
def test_boot_import_still_accepts_the_old_names(helper, paths, boot):
(boot / "voxip-config.json").write_text(json.dumps({"device_name": "Oud", "rotation": 180}))
(boot / "daynivo-config.json").write_text(json.dumps({"device_name": "Nieuw"}))
assert helper.boot_import() == {"imported": ["voxip-config.json", "daynivo-config.json"]}
conf = load_config(paths.config_file)
assert conf["device_name"] == "Nieuw" and conf["rotation"] == 180 # the Daynivo file is applied last
assert (boot / "voxip-config.json.applied").exists() assert (boot / "voxip-config.json.applied").exists()
def test_boot_import_rejects_invalid_config(helper, paths, boot): def test_boot_import_rejects_invalid_config(helper, paths, boot):
before = paths.config_file.read_text() before = paths.config_file.read_text()
(boot / "voxip-config.json").write_text(json.dumps({"url": "http://onveilig.example"})) (boot / "daynivo-config.json").write_text(json.dumps({"url": "http://onveilig.example"}))
helper.boot_import() helper.boot_import()
assert paths.config_file.read_text() == before assert paths.config_file.read_text() == before
assert (boot / "voxip-config.json.rejected").exists() assert (boot / "daynivo-config.json.rejected").exists()
assert "HTTPS" in (boot / "voxip-config.error.txt").read_text() assert "HTTPS" in (boot / "daynivo-config.error.txt").read_text()
def test_boot_import_rejects_non_key_lines(helper, boot): def test_boot_import_rejects_non_key_lines(helper, boot):
(boot / "voxip-authorized_keys").write_text("dit is geen sleutel\n") (boot / "daynivo-authorized_keys").write_text("dit is geen sleutel\n")
(boot / "voxip-authorized_keys").write_text("ook geen sleutel\n")
helper.boot_import() helper.boot_import()
assert (boot / "daynivo-authorized_keys.rejected").exists()
assert (boot / "voxip-authorized_keys.rejected").exists() assert (boot / "voxip-authorized_keys.rejected").exists()
@@ -211,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 test_update_install_reports_a_running_update(helper):
def busy(args, timeout=30, check=True, input=None): 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 helper.run = busy
with pytest.raises(h.HelperError, match="al een update"): with pytest.raises(h.HelperError, match="al een update"):
+3 -3
View File
@@ -1,7 +1,7 @@
import pytest import pytest
from voxip_kiosk import netconfig as n from daynivo_kiosk import netconfig as n
from voxip_kiosk.validation import ValidationError from daynivo_kiosk.validation import ValidationError
def test_validate_static_ethernet(): 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"]}) 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") text = n.build_keyfile(req, uuid="11111111-2222-3333-4444-555555555555")
kf = n.read_keyfile(text) 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", "type") == "ethernet"
assert kf.get("connection", "autoconnect-priority") == "100" assert kf.get("connection", "autoconnect-priority") == "100"
assert kf.get("ipv4", "method") == "manual" assert kf.get("ipv4", "method") == "manual"
+1 -1
View File
@@ -1,4 +1,4 @@
from voxip_kiosk import services, sysinfo from daynivo_kiosk import services, sysinfo
def test_parse_throttled(): def test_parse_throttled():
+2 -2
View File
@@ -22,8 +22,8 @@ def settings(unit):
def test_manager_can_use_sudo_for_the_helper(): def test_manager_can_use_sudo_for_the_helper():
unit = settings("voxip-kiosk-manager.service") unit = settings("daynivo-kiosk-manager.service")
assert unit["User"] == "voxip" assert unit["User"] == "daynivo"
for key in IMPLY_NO_NEW_PRIVILEGES: for key in IMPLY_NO_NEW_PRIVILEGES:
assert unit.get(key, "no") in ("no", "false", ""), key assert unit.get(key, "no") in ("no", "false", ""), key
for key in ("ProtectSystem", "ProtectHome", "PrivateTmp"): for key in ("ProtectSystem", "ProtectHome", "PrivateTmp"):
Loaded 100 of 106 files, more files were not shown because too many files have changed in this diff. Show more