Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d0f3206ff4 | ||
|
|
b0840d240e |
No files matched your search
@@ -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
|
||||||
@@ -191,14 +191,14 @@ 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 voxip-kiosk-browser -u voxip-kiosk-agent -f
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -221,7 +221,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 +231,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 +270,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 `voxip-kiosk-x`/`voxip-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/voxip-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. |
|
||||||
File renamed without changes.
@@ -12,7 +12,7 @@ samenwerken en waarom bepaalde keuzes zijn gemaakt.
|
|||||||
- 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/voxip-kiosk/config.json` en kan bij uitrol via de bootpartitie
|
||||||
worden meegegeven (`voxip-config.json`).
|
worden meegegeven (`daynivo-config.json`).
|
||||||
|
|
||||||
## Overzicht
|
## Overzicht
|
||||||
|
|
||||||
@@ -104,7 +104,7 @@ 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
|
||||||
@@ -119,7 +119,7 @@ draait) kan het apparaat dus claimen, iemand anders op het netwerk niet.
|
|||||||
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
|
`voxip-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).
|
||||||
|
|
||||||
@@ -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'`).
|
||||||
|
|
||||||
@@ -192,7 +192,7 @@ 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
|
||||||
|
|||||||
@@ -47,14 +47,14 @@ 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 voxip-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.
|
||||||
@@ -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,11 +112,11 @@ 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
|
||||||
|
|||||||
@@ -4,10 +4,10 @@ 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/voxip-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 (voxipadmin 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.
|
||||||
|
|||||||
File renamed without changes.
@@ -20,4 +20,4 @@ EOF_CHROOT
|
|||||||
|
|
||||||
# Instructions next to config.txt for whoever opens the boot partition.
|
# 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/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"
|
install -m 0644 files/voxip-kiosk/image/daynivo-config.example.json "${ROOTFS_DIR}/boot/firmware/daynivo-config.example.json"
|
||||||
+4
-2
@@ -138,7 +138,9 @@ 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 voxip-kiosk-agent voxip-kiosk-browser voxip-kiosk-healthcheck voxip-kiosk-helper voxip-kiosk-manager voxip-kiosk-updater voxip-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"
|
||||||
@@ -340,4 +342,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,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:
|
||||||
@@ -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")
|
||||||
|
|||||||
+28
-21
@@ -33,9 +33,11 @@ UPDATE_UNIT = "voxip-kiosk-update-now"
|
|||||||
UPDATER_PATH = "/usr/lib/voxip-kiosk/voxip-kiosk-updater"
|
UPDATER_PATH = "/usr/lib/voxip-kiosk/voxip-kiosk-updater"
|
||||||
ACTIVATE_UNIT = "voxip-network-activate"
|
ACTIVATE_UNIT = "voxip-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 voxip-* 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
|
||||||
@@ -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
|
||||||
@@ -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:
|
||||||
|
|||||||
@@ -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() }}">
|
||||||
|
|||||||
@@ -3,6 +3,10 @@ Description=Daynivo configuratie en SSH-sleutels importeren vanaf de bootpartiti
|
|||||||
Documentation=file:///usr/share/doc/voxip-kiosk/README.md
|
Documentation=file:///usr/share/doc/voxip-kiosk/README.md
|
||||||
After=local-fs.target
|
After=local-fs.target
|
||||||
Before=voxip-kiosk-manager.service voxip-kiosk-browser.service ssh.service
|
Before=voxip-kiosk-manager.service voxip-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-config.json
|
||||||
ConditionPathExistsGlob=|/boot/firmware/voxip-authorized_keys
|
ConditionPathExistsGlob=|/boot/firmware/voxip-authorized_keys
|
||||||
ConditionPathExistsGlob=|/boot/voxip-config.json
|
ConditionPathExistsGlob=|/boot/voxip-config.json
|
||||||
|
|||||||
+28
-9
@@ -169,35 +169,54 @@ def test_boot_guard_rolls_back_and_honours_reset(helper, paths, boot):
|
|||||||
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 / "voxip-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 / "voxip-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 / "voxip-eth0.nmconnection").write_text("x")
|
||||||
|
(boot / "voxip-network-reset").write_text("")
|
||||||
|
assert helper.network_boot_guard() == {"reset": True}
|
||||||
|
assert not (paths.nm_connections_dir / "voxip-eth0.nmconnection").exists()
|
||||||
|
assert not (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()
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -33,7 +33,7 @@ systemctl daemon-reload
|
|||||||
|
|
||||||
echo "==> Bestanden verwijderen"
|
echo "==> Bestanden verwijderen"
|
||||||
rm -rf /usr/lib/voxip-kiosk /usr/share/voxip-kiosk /usr/share/doc/voxip-kiosk
|
rm -rf /usr/lib/voxip-kiosk /usr/share/voxip-kiosk /usr/share/doc/voxip-kiosk
|
||||||
rm -f /usr/local/sbin/voxip-kiosk-ctl
|
rm -f /usr/local/sbin/daynivo-kiosk-ctl /usr/local/sbin/voxip-kiosk-ctl
|
||||||
rm -f /etc/sudoers.d/voxip-kiosk
|
rm -f /etc/sudoers.d/voxip-kiosk
|
||||||
rm -f /etc/systemd/journald.conf.d/voxip-kiosk.conf /etc/systemd/system.conf.d/voxip-watchdog.conf
|
rm -f /etc/systemd/journald.conf.d/voxip-kiosk.conf /etc/systemd/system.conf.d/voxip-watchdog.conf
|
||||||
rm -f /etc/logrotate.d/voxip-kiosk /etc/avahi/services/voxip-kiosk.service
|
rm -f /etc/logrotate.d/voxip-kiosk /etc/avahi/services/voxip-kiosk.service
|
||||||
|
|||||||
+1
-1
@@ -44,7 +44,7 @@
|
|||||||
li.textContent = u;
|
li.textContent = u;
|
||||||
list.appendChild(li);
|
list.appendChild(li);
|
||||||
});
|
});
|
||||||
document.getElementById("code").textContent = params.get("code") || "zie: sudo voxip-kiosk-ctl setup-code";
|
document.getElementById("code").textContent = params.get("code") || "zie: sudo daynivo-kiosk-ctl setup-code";
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
Reference in new issue
Block a user