commit e53c015f761a6a3a2e95773681b7dc6887c213fc Author: Przemysław Kiełkowski Date: Wed May 27 22:00:48 2026 +0200 first commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e43b0f9 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.DS_Store diff --git a/README.md b/README.md new file mode 100644 index 0000000..139ac53 --- /dev/null +++ b/README.md @@ -0,0 +1,498 @@ +# Kiosk Dwuekranowy — Chromium + Prezentacja PowerPoint — Debian 13 + +Kiosk na dwa monitory. Lewy ekran wyświetla `http://diluals31/terminal/ift/5/DE/index` w Chromium; prawy ekran wyświetla w pętli prezentację PowerPoint z zamontowanego udziału SMB. Używa **X11** (xinit + openbox) i **LibreOffice Impress** w trybie pokazu (`--show`) — bez konwersji, z zachowanymi animacjami i przejściami. + +--- + +## Architektura + +``` +┌──────────────────────────────────────────────────────────────────┐ +│ kiosk.service │ +│ (xinit + openbox — tty7) │ +│ │ +│ Wirtualny desktop 3840×1080 (xrandr --right-of) │ +│ ┌──────────────────────────┐ ┌──────────────────────────────┐ │ +│ │ Chromium (fullscreen) │ │ LibreOffice Impress --show │ │ +│ │ DP-1 (0,0–1920×1080) │ │ HDMI-1 (1920,0–3840×1080) │ │ +│ │ http://diluals31/... │ │ terminal_live.pptx │ │ +│ └──────────────────────────┘ └──────────────────────────────┘ │ +│ ▲ above (wmctrl) ▲ │ +│ kiosk-manager.sh presentation-manager.sh │ +└──────────────────────────────────────────────────────────────────┘ + ▲ + /mnt/presentation (CIFS/SMB) + montowany przez skrypt (fstab user) + ▲ + //192.168.40.201/DatyFirmowe/ALS +``` + +**Przepływ — lewy ekran (kiosk-manager.sh):** +1. Konfiguruje X11: xrandr dual-screen, wyłącza DPMS, uruchamia openbox +2. Zapisuje `registrymodifications.xcu` — wyłącza presenter console LO +3. Uruchamia watchdog sesji (co 0.4 s chowa okna LO z lewego ekranu) +4. Uruchamia `presentation-manager.sh` w tle +5. Otwiera `waiting.html` fullscreen + always-on-top +6. Czeka na: sieć (`ping diluals31`) **oraz** sygnał od `presentation-manager.sh` (plik `/tmp/kiosk-smb-ready`) +7. Po otrzymaniu obu sygnałów: `sleep 10` — daje LO czas na otwarcie i pozycjonowanie okna na prawym ekranie +8. Przełącza Chromium na docelowy URL + +> `sleep 10` jest kluczowe: zapewnia, że LibreOffice zdąży otworzyć i ustawić okno pokazu zanim Chromium wejdzie w tryb fullscreen+above na lewym ekranie. Bez tego opóźnienia konsola prezentacji była widoczna po reboot. + +**Przepływ — prawy ekran (presentation-manager.sh):** +1. Montuje udział SMB (`mount /mnt/presentation`) z retry co 5 s +2. Synchronizuje czcionki z `$SMB_MOUNT/fonts/` do `~/.local/share/fonts/smb/` +3. Tworzy plik `/tmp/kiosk-smb-ready` — sygnał dla `kiosk-manager.sh` +4. Czeka na plik `terminal_live.pptx` +5. Uruchamia persistentny watchdog konsoli (co 0.3 s) +6. Uruchamia `soffice --show terminal_live.pptx` +7. Przesuwa okno pokazu na prawy ekran i stosuje fullscreen +8. Co 5 s: sprawdza mtime pliku i restartuje po zmianie + +--- + +## Wymagania + +- Debian 13 netinst — instalacja minimalna bez środowiska graficznego +- Dwa monitory: `DP-1` (lewy, primary) i `HDMI-1` (prawy) +- Dostęp do udziału SMB/CIFS z plikiem prezentacji + +--- + +## Instalacja krok po kroku + +### 1. Utwórz użytkownika kiosk + +```bash +useradd -m -s /usr/sbin/nologin kiosk +usermod -aG tty,input,video,render,dialout kiosk +``` + +### 2. Zainstaluj pakiety + +```bash +apt update && apt install -y \ + xorg xinit openbox \ + chromium \ + libreoffice-impress \ + cifs-utils \ + xdotool wmctrl \ + x11-xserver-utils \ + iputils-ping \ + ethtool \ + wakeonlan +``` + +> Nie są już potrzebne: + +### 2a. Zainstaluj czcionki + +LibreOffice podstawia czcionkę zastępczą gdy oryginalna nie jest zainstalowana w systemie — może to zmienić układ tekstu na slajdach. + +#### Automatyczna instalacja przez udział SMB (zalecane) + +`presentation-manager.sh` automatycznie synchronizuje czcionki z folderu `fonts/` na udziale SMB do `/home/kiosk/.local/share/fonts/smb/`. Wystarczy wrzucić pliki `.ttf`/`.otf` na udział — zostaną zainstalowane przy najbliższym restarcie kiosku. + +**Na maszynie Windows** — skopiuj pliki czcionek do `\\192.168.40.201\DatyFirmowe\ALS\fonts\`: + +```powershell +# Calibri (C:\Windows\Fonts\ lub %LOCALAPPDATA%\Microsoft\Windows\Fonts\) +Copy-Item C:\Windows\Fonts\calibri*.ttf \\192.168.40.201\DatyFirmowe\ALS\fonts\ + +# Aptos — zazwyczaj w cache czcionek chmurowych Microsoft 365: +$src = "$env:LOCALAPPDATA\Microsoft\FontCache\4\CloudFonts\Aptos" +Copy-Item "$src\*" \\192.168.40.201\DatyFirmowe\ALS\fonts\ +``` + +> Nazwy plików Aptos w cache mogą być numeryczne — to normalne, fontconfig czyta metadane wewnętrzne, nie nazwę pliku. + +**Restart kiosku** — czcionki zostaną zainstalowane automatycznie: + +```bash +systemctl restart kiosk.service +# Sprawdź w logach: +journalctl -u kiosk.service -b | grep "czcionka\|Cache czcionek" +``` + +#### Carlito — open-source'owy zamiennik Calibri (opcjonalnie) + +Jeśli nie masz dostępu do oryginalnych plików Calibri, `Carlito` ma identyczne metryki (ten sam układ tekstu i łamania linii): + +```bash +apt install fonts-crosextra-carlito fonts-crosextra-caladea +``` + +`fonts-crosextra-caladea` to odpowiednik Cambrii. Nie wymaga wrzucania plików na SMB — instalowany systemowo. + +#### Inne czcionki Microsoft (Arial, Times New Roman, Verdana) + +```bash +apt install ttf-mscorefonts-installer +``` + +> Nie są już potrzebne: `sway`, `swayidle`, `wlopm`, `wlr-randr`, `seatd`, `mpv`, `poppler-utils`. + +### 3. Skonfiguruj montowanie udziału SMB + +#### 3a. Utwórz plik poświadczeń + +```bash +mkdir -p /etc/samba +cat > /etc/samba/kiosk-credentials << 'EOF' +username=NAZWA_UZYTKOWNIKA +password=HASLO +domain=WORKGROUP +EOF +chmod 600 /etc/samba/kiosk-credentials +chown root:root /etc/samba/kiosk-credentials +``` + +#### 3b. Utwórz punkt montowania + +```bash +mkdir -p /mnt/presentation +``` + +#### 3c. Dodaj wpis w /etc/fstab (montowanie przez użytkownika kiosk) + +```bash +echo '//192.168.40.201/DatyFirmowe/ALS /mnt/presentation cifs credentials=/etc/samba/kiosk-credentials,uid=kiosk,gid=kiosk,vers=3.0,user,noauto,_netdev 0 0' >> /etc/fstab +``` + +Opcja `user` pozwala użytkownikowi `kiosk` wykonać `mount /mnt/presentation` bez uprawnień roota. Opcja `noauto` wyłącza automatyczne montowanie przy starcie — skrypt sam zarządza montem z retry. + +> **Uwaga:** Systemd unit `mnt-presentation.mount` jest **wyłączony** — skrypt `presentation-manager.sh` przejął odpowiedzialność za montowanie. + +```bash +systemctl disable mnt-presentation.mount +systemctl stop mnt-presentation.mount +``` + +### 4. Wykryj nazwy wyjść wideo + +```bash +for p in /sys/class/drm/card*-*; do + echo "$(basename $p | sed 's/card[0-9]*-//'): $(cat $p/status)" +done +``` + +Przykładowe wyjście: +``` +DP-1: connected +HDMI-1: connected +``` + +Ustaw właściwe nazwy w `kiosk-manager.sh`: +```bash +OUTPUT_LEFT="DP-1" # lewy ekran — primary +OUTPUT_RIGHT="HDMI-1" # prawy ekran +``` + +### 5. Skopiuj pliki konfiguracyjne + +```bash +cp kiosk-manager.sh /home/kiosk/kiosk-manager.sh +cp presentation-manager.sh /home/kiosk/presentation-manager.sh +cp waiting.html /home/kiosk/waiting.html +chmod +x /home/kiosk/kiosk-manager.sh /home/kiosk/presentation-manager.sh +chown -R kiosk:kiosk /home/kiosk/ + +cp kiosk.service /etc/systemd/system/kiosk.service +``` + +### 6. Rozwiązanie problemu z domenami .local + +Jeśli `diluals31` jest rozwiązywany przez DNS firmowy (nie mDNS): + +- Otwórz `/etc/nsswitch.conf` +- Zmień linię `hosts:` na: `hosts: files dns mdns4_minimal [NOTFOUND=return]` + +### 7. Włącz i uruchom serwis + +```bash +systemctl daemon-reload +systemctl enable kiosk.service +systemctl start kiosk.service +``` + +### 8. Skonfiguruj GRUB — natychmiastowy start + +```bash +# /etc/default/grub +GRUB_DEFAULT=0 +GRUB_TIMEOUT=0 +``` + +```bash +/usr/sbin/grub-mkconfig -o /boot/grub/grub.cfg +``` + +--- + +## Harmonogram — Automatyczne wyłączanie i Wake on LAN + +Kiosk wyłącza się automatycznie w **sobotę o 7:00** i jest budzony przez Magic Packet (Wake on LAN) w **niedzielę o 21:00**. + +### Na kiosku + +#### 9a. Cron — automatyczne wyłączanie + +```bash +cp kiosk-schedule /etc/cron.d/kiosk-schedule +chmod 644 /etc/cron.d/kiosk-schedule +``` + +#### 9b. Wake on LAN — włączanie i utrwalanie + +```bash +cp wol-enable.service /etc/systemd/system/ +systemctl daemon-reload +systemctl enable --now wol-enable.service +``` + +Sprawdź, czy WoL jest aktywne: + +```bash +ip link show | grep -A1 "state UP" | grep ether # MAC adres kiosku +ethtool enp2s0 | grep -i wake # zamień enp2s0 na właściwy interfejs +``` + +> **Wymagane ręcznie: BIOS kiosku** — wejdź w ustawienia firmware i włącz opcję **Wake on LAN** lub **Power on by PCI-E/PCI**. Bez tego ustawienia Magic Packet jest ignorowany przez sprzęt. + +#### 9c. Pobierz MAC adres kiosku + +```bash +ip link show | grep -A1 "state UP" | grep ether | awk '{print $2}' +``` + +Zanotuj ten adres — potrzebny do konfiguracji serwera (krok poniżej). + +### Na serwerze (192.168.40.201) + +Zainstaluj narzędzie do wysyłania Magic Packet: + +```bash +apt install wakeonlan +``` + +Utwórz plik crona wysyłającego WoL w niedzielę o 21:00 (wstaw MAC kiosku): + +```bash +cat > /etc/cron.d/kiosk-wol << 'EOF' +SHELL=/bin/sh +PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin +# Obuć kiosk WS-VK016 w niedzielę o 21:00 +0 21 * * 0 root wakeonlan XX:XX:XX:XX:XX:XX +EOF +chmod 644 /etc/cron.d/kiosk-wol +``` + +Zastąp `XX:XX:XX:XX:XX:XX` adresem MAC pobranym w kroku 9c. + +> WoL działa w obrębie jednej podsieci (broadcast). Kiosk i serwer są w sieci `192.168.40.x` — brak potrzeby konfiguracji routera. + +--- + +### `kiosk.service` → `/etc/systemd/system/kiosk.service` + +Jednostka systemd uruchamiająca sesję X11 jako użytkownik `kiosk`. + +| Opcja | Wartość | Opis | +|---|---|---| +| `ExecStart` | `xinit kiosk-manager.sh -- :0 vt7` | Uruchamia X11 na tty7 | +| `ExecStartPre` | `chvt 7` | Przełącza konsolę na tty7 | +| `PAMName` | `login` | Wymagane do bezrootowego X11 (xinit jako user) | +| `TTYPath` | `/dev/tty7` | Przypisuje serwis do tty7 | +| `SLIDE_DURATION` | `10` | Czas slajdu (s) — aktualnie nieużywany, LO używa timingów z PPTX | +| `Restart=always` | — | Automatyczny restart po awarii | + +> Serwis startuje **bez** `network-online.target` — celowo, aby `waiting.html` było widoczne od razu po uruchomieniu systemu. Montowanie SMB obsługuje skrypt z retry. + +--- + +### `kiosk-manager.sh` → `/home/kiosk/kiosk-manager.sh` + +Uruchamiany przez xinit. Konfiguruje X11 i obsługuje lewy ekran. + +**Co robi:** +1. `xrandr` — tryb rozszerzony: `DP-1` primary (0,0), `HDMI-1` po prawej (1920,0) +2. `xsetroot` + `xset` — czarne tło, wyłączony wygaszacz i DPMS +3. `openbox --sm-disable` — lekki WM wymagany przez `wmctrl fullscreen` +4. Zapisuje `registrymodifications.xcu` — wyłącza presenter console LO (`StartWithPresenterScreen=false`) +5. Uruchamia watchdog sesji w tle — co 0.4 s chowa okna soffice z lewego ekranu (x < 1920) +6. Uruchamia `presentation-manager.sh` w tle +7. Chromium otwiera `waiting.html` z `fullscreen` + `above` +8. Czeka na sieć (`ping diluals31`) **i** na plik `/tmp/kiosk-smb-ready` +9. `sleep 10` — LO ma czas na pełny start i pozycjonowanie okna przed Chromium +10. Kill waiting Chromium → exec Chromium z docelowym URL + +**Flagi Chromium (`CHROMIUM_OPTS`):** + +| Flaga | Opis | +|---|---| +| `--app=URL` | Tryb aplikacji — brak paska adresu, brak kart | +| `--window-position=0,0 --window-size=1920,1080` | Pozycja na lewym ekranie (X11: bez `--kiosk`, który rozciąga na cały wirtualny desktop) | +| `--no-first-run` | Pomija ekran powitalny | +| `--noerrdialogs` | Ukrywa okna dialogowe błędów | +| `--disable-gpu-suspend` | Zapobiega zawieszaniu GPU przy wygaszaniu | +| `--force-device-scale-factor=1` | Skalowanie 1:1 | +| `--disable-features=Translate,MediaRouter` | Wyłącza tłumaczenie i Chromecast | +| `--disable-sync` | Wyłącza synchronizację Google | +| `--disable-extensions` | Wyłącza rozszerzenia | +| `--password-store=basic` | Nie używa systemowego portfela haseł | +| `--disable-pinch` | Wyłącza pinch-to-zoom | + +> **Uwaga:** `--kiosk` na X11 rozciąga okno na cały wirtualny desktop 3840×1080 (oba ekrany). Używamy `--app` + `--window-position/size` + `wmctrl fullscreen`. + +**Zmiana hosta / URL kiosku:** +```bash +# kiosk-manager.sh +KIOSK_HOST="diluals31" +KIOSK_URL="http://diluals31/terminal/ift/5/DE/index" +``` + +--- + +### `presentation-manager.sh` → `/home/kiosk/presentation-manager.sh` + +Obsługuje prawy ekran — montowanie SMB i wyświetlanie prezentacji. + +**Montowanie SMB:** +```bash +until mountpoint -q "$SMB_MOUNT"; do + mount "$SMB_MOUNT" # korzysta z wpisu fstab z opcją 'user' + sleep 5 # retry co 5 s +done +``` +Przy zakończeniu skryptu (trap EXIT/INT/TERM): `umount /mnt/presentation`. + +**Ustawianie okien LibreOffice (`start_lo`):** + +Iteruje wszystkie widoczne okna `soffice` wg pozycji X: +- `x ≥ 1920` → okno pokazu slajdów → `wmctrl fullscreen` +- `x < 1920` → konsola prezentacji → przesunięcie na `x=3840` (poza desktop) + `wmctrl below` +- Fallback po 5 s: jeśli nadal brak okna na prawym ekranie → przeniesienie pierwszego znalezionego + +Po ustawieniu LO: Chromium podniesiony jako `above` (always-on-top na lewym ekranie). + +**Watchdog (co 5 s w głównej pętli):** +- Sprawdza wszystkie widoczne okna `soffice` — jeśli któreś wróciło na `x < 1920`, wysyła je z powrotem na `x=3840` +- Sprawdza `mtime` pliku PPTX — po zmianie restartuje pokaz +- Sprawdza czy `soffice` żyje — jeśli zakończył (koniec pokazu), restartuje + +**Konfiguracja:** +```bash +SMB_MOUNT="/mnt/presentation" +SMB_SOURCE="//192.168.40.201/DatyFirmowe/ALS" +PRESENTATION_FILE="$SMB_MOUNT/terminal_live.pptx" +``` + +--- + +### `kiosk.service` (wyłączone) `mnt-presentation.mount` + +Systemd unit do montowania SMB jest **wyłączony**. Skrypt `presentation-manager.sh` przejął montowanie przez `mount /mnt/presentation` (wpis fstab z opcją `user,noauto`). + +--- + +### `sway-config` + +Nieużywany — zachowany jako archiwum poprzedniej architektury (Wayland/sway). + +--- + +### `kiosk-schedule` → `/etc/cron.d/kiosk-schedule` + +Plik crona systemu wyłączający kiosk w sobotę o 7:00 (`shutdown -h now` jako root). + +--- + +### `wol-enable.service` → `/etc/systemd/system/wol-enable.service` + +Jednostka systemd uruchamiana przy każdym starcie systemu. Wywołuje `ethtool -s IFACE wol g` na wszystkich interfejsach sieciowych — utrzymuje Wake on LAN włączone po rebootach (niektóre sterowniki resetują ustawienie WoL przy wyłączaniu). + +--- + +### `waiting.html` → `/home/kiosk/waiting.html` + +Lokalna strona HTML wyświetlana przez Chromium podczas oczekiwania na sieć. Ciemne tło, animowany spinner. Widoczna od startu systemu do momentu, gdy `diluals31` odpowie na ping. + +--- + +## Zarządzanie i diagnostyka + +### Logi + +```bash +# Logi kiosku na żywo +journalctl -u kiosk.service -f + +# Ostatnie 100 linii +journalctl -u kiosk.service -b -n 100 + +# Tylko logi prezentacji +journalctl -u kiosk.service -b | grep presentation-manager + +# Logi montowania SMB +journalctl -u kiosk.service -b | grep "mount:" +``` + +### Restart / zatrzymanie + +```bash +systemctl restart kiosk.service +systemctl stop kiosk.service +``` + +### Ręczne montowanie / odmontowanie SMB (jako root) + +```bash +mount /mnt/presentation +umount /mnt/presentation +mountpoint /mnt/presentation && echo "zamontowany" +``` + +### Wykrycie nazw wyjść wideo (bez uruchomionego kiosku) + +```bash +for p in /sys/class/drm/card*-*; do + echo "$(basename $p | sed 's/card[0-9]*-//'): $(cat $p/status)" +done +``` + +### Wymuszenie restartu pokazu (zmiana pliku) + +```bash +touch /mnt/presentation/terminal_live.pptx +# skrypt wykryje zmianę mtime w ciągu 5 s i zrestartuje LO +``` + +### Czyszczenie cache Chromium + +```bash +systemctl stop kiosk.service +rm -rf /home/kiosk/.cache/chromium +systemctl start kiosk.service +``` + +--- + +## Rozwiązywanie problemów + +| Objaw | Przyczyna | Rozwiązanie | +|---|---|---| +| `waiting.html` nie pojawia się | Sieć dostępna natychmiast, ping loop kończy się w <2 s | Normalne zachowanie — `sleep 2` zapewnia min. 2 s widoczności | +| Prezentacja się nie wyświetla | SMB nie zamontowany | `journalctl -u kiosk.service -b \| grep mount` — sprawdź błędy | +| `mount: Permission denied` | Opcja `user` brak w fstab lub kiosk nie jest właścicielem | Sprawdź wpis fstab: `grep presentation /etc/fstab` | +| `mount: not found in /etc/fstab` | Brak wpisu fstab | Dodaj wpis zgodnie z sekcją 3c | +| Konsola LibreOffice widoczna na lewym ekranie po reboot | LibreOffice startuje przed Chromium (kiosk URL) i konsola zdąży się pokazać | Zwiększ `sleep 10` w `kiosk-manager.sh` (linia po pętli `kiosk-smb-ready`) | +| Chromium rozciągnięty na oba ekrany | Użyto `--kiosk` zamiast `--app` | Sprawdź `kiosk-manager.sh` — nie używamy `--kiosk` | +| Lewy ekran czarny, tylko prawy działa | Chromium nie znalazł okna w 30 s | `journalctl \| grep "kiosk-manager"` — sprawdź błędy Chromium | +| `openbox: command not found` | Brak pakietu | `apt install openbox` | +| `wmctrl: command not found` | Brak pakietu | `apt install wmctrl` | +| `xdotool: command not found` | Brak pakietu | `apt install xdotool` | +| Animacje nie działają | LO konwertuje do PNG/PDF (stare podejście) | Aktualna wersja używa `soffice --show` bezpośrednio — animacje powinny działać | +| Tekst źle wyświetlany (zła czcionka, zły układ) | Brak czcionki | Skopiuj pliki `.ttf`/`.otf` do `\\serwer\DatyFirmowe\ALS\fonts\` i zrestartuj kiosk — skrypt zainstaluje je automatycznie | +| Udział SMB z protokołem SMB 1.0 | `vers=3.0` nieobsługiwany | Zmień na `vers=2.1` lub `vers=1.0` w `/etc/fstab` | +| Czarny ekran po restarcie | Chromium trzyma zablokowany profil | `rm -rf /home/kiosk/.cache/chromium` | +| X11 nie startuje (`cannot open display`) | Brak pakietu xorg | `apt install xorg` | diff --git a/kiosk-manager.sh b/kiosk-manager.sh new file mode 100644 index 0000000..cbd9971 --- /dev/null +++ b/kiosk-manager.sh @@ -0,0 +1,175 @@ +#!/bin/bash +# kiosk-manager.sh — konfiguracja X11 + lewy ekran: Chromium +# Plik docelowy: /home/kiosk/kiosk-manager.sh + +# Jawnie ustawiamy HOME — serwis może startować jako root (xinit bez User=kiosk), +# a LibreOffice szuka profilu w $HOME/.config/libreoffice/ (nie w /home/kiosk/). +# Wszystkie procesy potomne (soffice, chromium) dziedziczą tę wartość. +export HOME=/home/kiosk + +KIOSK_HOST="diluals31" +KIOSK_URL="http://diluals31/terminal/ift/5/DE/index" +WAITING_PAGE="file:///home/kiosk/waiting.html" + +OUTPUT_LEFT="DP-1" +OUTPUT_RIGHT="HDMI-1" +RES_W=1920 +RES_H=1080 + +# ─── Niewidoczny kursor ────────────────────────────────────────────────────── +# Tworzymy motyw XCursor (1×1 px, ARGB 0x00000000) i eksportujemy XCURSOR_THEME +# zanim uruchomią się Chromium i LibreOffice — dziedziczą środowisko procesu. +# Brak myszy w kiosku nie usuwa kursora X11; bez tego wskaźnik jest widoczny +# w środku między ekranami (domyślna pozycja X11 = centrum wirtualnego desktopa). +_BLANK_DIR="/home/kiosk/.icons/blank/cursors" +mkdir -p "$_BLANK_DIR" +# printf pisze binarnie bezpośrednio do pliku — nie przez zmienną (bash $() ucina null-byte'y) +for _n in left_ptr default pointer crosshair text watch wait move size_all fleur; do + printf '\x58\x63\x75\x72\x10\x00\x00\x00\x00\x00\x01\x00\x01\x00\x00\x00\x02\x00\xfd\xff\x01\x00\x00\x00\x1c\x00\x00\x00\x24\x00\x00\x00\x02\x00\xfd\xff\x01\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x32\x00\x00\x00\x00\x00\x00\x00' > "$_BLANK_DIR/$_n" +done +printf '[Icon Theme]\nName=blank\n' > /home/kiosk/.icons/blank/index.theme +export XCURSOR_THEME=blank +export XCURSOR_SIZE=1 + + + +# Dual-screen rozszerzony: lewy ekran primary, prawy po prawej +# --auto wykrywa tryb automatycznie (bezpieczniejsze niż --mode 1920x1080) +xrandr --output "$OUTPUT_LEFT" --auto --primary --pos 0x0 +xrandr --output "$OUTPUT_RIGHT" --auto --right-of "$OUTPUT_LEFT" +# Rozszerz wirtualny framebuffer 200 px poniżej ekranów — kursor trafi w ten obszar +# i nie będzie widoczny na żadnym fizycznym monitorze (bez potrzeby dodatkowych pakietów) +xrandr --fb "$((RES_W * 2))x$((RES_H + 200))" +echo "[kiosk-manager] Konfiguracja xrandr:" +xrandr | grep -E "connected|[0-9]+x[0-9]+\+" + +# Czarne tło na obu ekranach +xsetroot -solid black + +# Wyłącz wygaszacz ekranu i DPMS (kiosk działa non-stop) +xset s off +xset -dpms +xset s noblank + +# Przesuń kursor poniżej fizycznych ekranów — w obszar framebuffera bez outputu +# Kursor istnieje w X11, ale nie jest wyświetlany na żadnym monitorze +xdotool mousemove "$RES_W" "$((RES_H + 100))" + +# Uruchom lekki WM — wymagany do per-screen fullscreen przez wmctrl +openbox --sm-disable & +sleep 0.5 + +# ─── Start aplikacji ───────────────────────────────────────────────────────── +# Konfiguracja LO (wyłączenie konsoli prezentera) jest zarządzana przez +# presentation-manager.sh → start_lo() via -env:UserInstallation=/tmp/lo-kiosk-profile + +# Uruchom menedżera prezentacji w tle (prawy ekran) +/home/kiosk/presentation-manager.sh & + +# Watchdog sesji: ukrywa okna konsoli LibreOffice przez cały czas trwania sesji X. +# Uruchomiony tu — nie czeka na SMB, działa od startu X niezależnie od timing'u LO. +( + while sleep 0.4; do + for _wid in $(xdotool search --class soffice --onlyvisible 2>/dev/null); do + _wx=$(xdotool getwindowgeometry "$_wid" 2>/dev/null \ + | awk '/Position:/{split($2,a,","); print a[1]+0}') + if [ "${_wx:-0}" -lt 1920 ]; then + wmctrl -i -r "$_wid" -b remove,above 2>/dev/null + wmctrl -i -r "$_wid" -b add,below 2>/dev/null + xdotool windowmove --sync "$_wid" 4000 0 2>/dev/null + echo "[kiosk-manager/watchdog] Konsola LO ukryta (WID=$_wid, x=${_wx:-?})" + fi + done + done +) & + +CHROMIUM_OPTS=( + --no-first-run + --noerrdialogs + --disable-infobars + --disable-gpu-suspend + --force-device-scale-factor=1 + --user-data-dir=/home/kiosk/.cache/chromium + --disable-features=AutofillServerCommunication,AutofillKeyValueService,Translate,MediaRouter + --disable-save-password-bubble + --disable-sync + --disable-extensions + --disable-background-networking + --disable-notifications + --disable-component-update + --password-store=basic + --disable-pinch + --window-position=0,0 + --window-size=${RES_W},${RES_H} +) + +# Strona oczekiwania — lewy ekran +# Bez --kiosk: na X11 --kiosk rozciąga okno na cały wirtualny desktop (oba ekrany) +chromium --app="$WAITING_PAGE" "${CHROMIUM_OPTS[@]}" & +CHROMIUM_PID=$! + +# Czekaj na okno i zastosuj fullscreen (synchronicznie) +for _i in $(seq 1 40); do + sleep 0.5 + WID=$(xdotool search --pid "$CHROMIUM_PID" --onlyvisible 2>/dev/null | head -1) + if [ -n "$WID" ]; then + wmctrl -i -r "$WID" -b add,fullscreen + wmctrl -i -r "$WID" -b add,above + break + fi +done +sleep 2 # Minimalny czas wyświetlania strony oczekiwania + +# Czekaj na dostępność serwera kiosku +while ! ping -c 1 -W 1 "$KIOSK_HOST" > /dev/null 2>&1; do + sleep 1 +done + +# Czekaj na zamontowanie SMB (sygnał od presentation-manager.sh). +# waiting.html zostaje do czasu gdy oba ekrany są gotowe do startu. +while [ ! -f /tmp/kiosk-smb-ready ]; do + sleep 1 +done + +sleep 5 + +# Zamknij ekran oczekiwania i uruchom właściwy kiosk +kill "$CHROMIUM_PID" +wait "$CHROMIUM_PID" 2>/dev/null +rm -f /tmp/kiosk-chromium-restart + +launch_chromium() { + rm -f /tmp/kiosk-chromium-restart + chromium --app="$KIOSK_URL" "${CHROMIUM_OPTS[@]}" & + CHROMIUM_PID=$! + echo "[kiosk-manager] Chromium uruchomiony (PID=$CHROMIUM_PID)" + ( + for _i in $(seq 1 30); do + sleep 1 + WID=$(xdotool search --pid "$CHROMIUM_PID" --onlyvisible 2>/dev/null | head -1) + if [ -n "$WID" ]; then + wmctrl -i -r "$WID" -b add,fullscreen + wmctrl -i -r "$WID" -b add,above + echo "[kiosk-manager] Chromium na lewym ekranie (próba $_i)" + break + fi + done + ) & +} + +launch_chromium + +# Pętla: reaguje na sygnał restartu od presentation-manager.sh +# (zmiana pliku prezentacji) lub na awarię Chromium +while true; do + sleep 1 + if [ -f /tmp/kiosk-chromium-restart ]; then + echo "[kiosk-manager] Sygnał restart Chromium — restartuje" + kill "$CHROMIUM_PID" 2>/dev/null + wait "$CHROMIUM_PID" 2>/dev/null + launch_chromium + elif ! kill -0 "${CHROMIUM_PID:-0}" 2>/dev/null; then + echo "[kiosk-manager] Chromium zakończył się — restartuje" + launch_chromium + fi +done diff --git a/kiosk-schedule b/kiosk-schedule new file mode 100644 index 0000000..e0dfa99 --- /dev/null +++ b/kiosk-schedule @@ -0,0 +1,9 @@ +# /etc/cron.d/kiosk-schedule +# Harmonogram kiosku WS-VK016 +# Instalacja: cp kiosk-schedule /etc/cron.d/kiosk-schedule +# chmod 644 /etc/cron.d/kiosk-schedule +SHELL=/bin/sh +PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin + +# Wyłącz kiosk w sobotę o 7:00 +0 7 * * 6 root /sbin/shutdown -h now diff --git a/kiosk.service b/kiosk.service new file mode 100644 index 0000000..fc7ab22 --- /dev/null +++ b/kiosk.service @@ -0,0 +1,26 @@ +[Unit] +Description=Kiosk Dwuekranowy — Instrukcje + Prezentacja +After=systemd-user-sessions.service + +[Service] +User=kiosk +Group=kiosk +Type=simple +PAMName=login + +TTYPath=/dev/tty7 +StandardInput=tty +StandardOutput=journal +StandardError=journal + +# Czas wyświetlania każdego slajdu prezentacji (sekundy) +Environment=SLIDE_DURATION=10 + +ExecStartPre=/usr/bin/chvt 7 +ExecStart=/usr/bin/xinit /home/kiosk/kiosk-manager.sh -- :0 vt7 -nolisten tcp + +Restart=always +RestartSec=3 + +[Install] +WantedBy=multi-user.target diff --git a/logo.png b/logo.png new file mode 100644 index 0000000..a20bd2a Binary files /dev/null and b/logo.png differ diff --git a/mnt-presentation.mount b/mnt-presentation.mount new file mode 100644 index 0000000..8004936 --- /dev/null +++ b/mnt-presentation.mount @@ -0,0 +1,14 @@ +[Unit] +Description=SMB Share z prezentacją kiosku +After=network-online.target +Wants=network-online.target + +[Mount] +# Zmień na adres IP i nazwę udziału swojego serwera SMB: +What=//192.168.x.x/nazwa-udzialu +Where=/mnt/presentation +Type=cifs +Options=credentials=/etc/samba/kiosk-credentials,uid=kiosk,gid=kiosk,iocharset=utf8,vers=3.0,_netdev + +[Install] +WantedBy=multi-user.target diff --git a/presentation-manager.sh b/presentation-manager.sh new file mode 100644 index 0000000..062bcdd --- /dev/null +++ b/presentation-manager.sh @@ -0,0 +1,227 @@ +#!/bin/bash +# presentation-manager.sh — prawy ekran: prezentacja PowerPoint z udziału SMB +# +# Działanie: +# 1. Czeka na zamontowanie udziału SMB +# 2. Sprawdza obecność pliku $SMB_MOUNT/terminal_live.pptx +# 3. Wyświetla prezentację bezpośrednio przez LibreOffice Impress (--show) +# → zachowane animacje, przejścia i efekty z oryginalnego pliku PPTX +# 4. Po zakończeniu pokazu automatycznie restartuje (pętla) +# 5. Co 5 sekund sprawdza, czy plik się zmienił — jeśli tak, restartuje pokaz + +SMB_MOUNT="/mnt/presentation" +SMB_SOURCE="//192.168.40.201/DatyFirmowe/ALS" +SMB_OPTS="credentials=/etc/samba/kiosk-credentials,uid=kiosk,gid=kiosk,vers=3.0" +PRESENTATION_FILE="$SMB_MOUNT/terminal_live.pptx" +# Lokalna kopia PPTX — LibreOffice otwiera ten plik zamiast pliku z SMB. +# Dzięki temu SMB nie jest blokowany i można nadpisać terminal_live.pptx +# w dowolnej chwili z innego komputera (Windows/macOS/Linux). +LOCAL_PRESENTATION="/tmp/kiosk-live.pptx" + +CURRENT_MTIME="" +LO_PID="" +WATCHDOG_PID="" + +# Usuń stary plik sygnałowy (restart serwisu) +rm -f /tmp/kiosk-smb-ready + +# Odmontuj udział przy zamknięciu skryptu +trap 'stop_lo; kill "${WATCHDOG_PID:-0}" 2>/dev/null; umount "$SMB_MOUNT" 2>/dev/null' EXIT INT TERM + +stop_lo() { + if [ -n "${LO_PID:-}" ] && kill -0 "$LO_PID" 2>/dev/null; then + kill "$LO_PID" + wait "$LO_PID" 2>/dev/null + LO_PID="" + fi +} + +# Persistentny watchdog co 0.3s — przenosi konsolę LO poza ekran zanim stanie się widoczna. +# Działa przez cały czas życia skryptu. Fullscreen i Chromium above obsługuje start_lo(). +console_watchdog() { + while true; do + sleep 0.3 + for _wid in $(xdotool search --class soffice --onlyvisible 2>/dev/null); do + _wx=$(xdotool getwindowgeometry "$_wid" 2>/dev/null \ + | awk '/Position:/{split($2,a,","); print a[1]+0}') + if [ "${_wx:-0}" -lt 1920 ]; then + wmctrl -i -r "$_wid" -b remove,above 2>/dev/null + wmctrl -i -r "$_wid" -b add,below 2>/dev/null + xdotool windowmove --sync "$_wid" 4000 0 2>/dev/null + echo "[watchdog] Konsola ukryta (WID=$_wid, x=${_wx:-?})" + fi + done + done +} + +start_lo() { + stop_lo + # Usuń plik blokady LO — pozostaje po kill i blokuje kolejne uruchomienie + rm -f /tmp/.~lock.kiosk-live.pptx\# + + # Wyłącz konsolę prezentera — izolowany profil w /tmp eliminuje problemy + # z HOME=/root (usługa bez User=kiosk) i z cache LO z poprzednich sesji. + # -env:UserInstallation wskazuje LO dokładnie który profil ma użyć. + _LO_PROFILE_DIR="/tmp/lo-kiosk-profile" + _LO_PROFILE="$_LO_PROFILE_DIR/user" + rm -rf "$_LO_PROFILE_DIR" # wymuś świeży profil — bez żadnych zapisanych preferencji + mkdir -p "$_LO_PROFILE" + cat > "$_LO_PROFILE/registrymodifications.xcu" << 'XCUEOF' + + + + + false + + + +XCUEOF + + echo "[presentation-manager] Kopiowanie pliku z SMB do /tmp..." + cp "$PRESENTATION_FILE" "$LOCAL_PRESENTATION" || { + echo "[presentation-manager] Błąd kopiowania pliku — retry za 5s" + sleep 5 + return 1 + } + echo "[presentation-manager] Uruchamianie pokazu: $LOCAL_PRESENTATION" + # --show otwiera bezpośrednio pokaz (bez Start Center) + # -env:UserInstallation wymusza użycie naszego profilu (bez konsoli prezentera) + soffice \ + "-env:UserInstallation=file://$_LO_PROFILE_DIR" \ + --norestore \ + --nofirststartwizard \ + --show "$LOCAL_PRESENTATION" & + LO_PID=$! + echo "[presentation-manager] LibreOffice Impress uruchomiony (PID=$LO_PID)" + + # Obsłuż wszystkie okna LO wg pozycji: + # x >= 1920 (prawy ekran) → okno pokazu slajdów → fullscreen + # x < 1920 (lewy ekran) → konsola prezentacji → zminimalizuj + # Fallback po 5s: przenieś pierwsze znalezione okno na prawy ekran + ( + for _i in $(seq 1 30); do + sleep 1 + WIDS=$(xdotool search --pid "$LO_PID" --onlyvisible 2>/dev/null) + [ -z "$WIDS" ] && WIDS=$(xdotool search --class soffice --onlyvisible 2>/dev/null) + [ -z "$WIDS" ] && continue + + PRES_WID="" + for WID in $WIDS; do + WIN_X=$(xdotool getwindowgeometry "$WID" 2>/dev/null \ + | awk '/Position:/{split($2,a,","); print a[1]+0}') + if [ "${WIN_X:-0}" -ge 1920 ]; then + PRES_WID=$WID + fi + # Konsola (x<1920) obsługiwana przez console_watchdog() + done + + if [ -n "$PRES_WID" ]; then + wmctrl -i -r "$PRES_WID" -b add,fullscreen 2>/dev/null + echo "[presentation-manager] Pokaz fullscreen na prawym ekranie (próba $_i)" + break + fi + + # Fallback: po 5s nadal brak okna na prawym ekranie → przenieś pierwsze + if [ "$_i" -ge 5 ]; then + FIRST_WID=$(echo "$WIDS" | head -1) + if [ -n "$FIRST_WID" ]; then + wmctrl -i -r "$FIRST_WID" -b remove,fullscreen 2>/dev/null + sleep 0.3 + xdotool windowmove --sync "$FIRST_WID" 1920 0 + xdotool windowsize --sync "$FIRST_WID" 1920 1080 + wmctrl -i -r "$FIRST_WID" -b add,fullscreen 2>/dev/null + echo "[presentation-manager] Fallback: przeniesiono na prawy ekran (próba $_i)" + break + fi + fi + done + + # Podnieś Chromium na wierzch lewego ekranu (przykrywa konsolę jeśli się pojawiła) + sleep 1 + CHROMIUM_WID=$(xdotool search --class chromium --onlyvisible 2>/dev/null | head -1) + if [ -n "$CHROMIUM_WID" ]; then + xdotool windowraise "$CHROMIUM_WID" + wmctrl -i -r "$CHROMIUM_WID" -b add,above + echo "[presentation-manager] Chromium always-on-top (above)" + fi + ) & +} + +# ─── GŁÓWNA LOGIKA ─────────────────────────────────────────────────────────── + +echo "[presentation-manager] Montowanie udziału SMB: $SMB_SOURCE" +until mountpoint -q "$SMB_MOUNT" 2>/dev/null; do + mount "$SMB_MOUNT" 2>&1 \ + | sed 's/^/[presentation-manager] mount: /' + mountpoint -q "$SMB_MOUNT" 2>/dev/null || sleep 5 +done +echo "[presentation-manager] SMB zamontowany." + +# Synchronizuj czcionki z udziału SMB do katalogu użytkownika kiosk. +# Fontconfig i LibreOffice odczytują ~/.local/share/fonts/ bez uprawnień roota. +# Wystarczy wrzucić nowe pliki .ttf/.otf do $SMB_MOUNT/fonts/ — zostaną +# automatycznie zainstalowane przy następnym starcie kiosku. +FONTS_SRC="$SMB_MOUNT/fonts" +FONTS_DST="/home/kiosk/.local/share/fonts/smb" +if [ -d "$FONTS_SRC" ]; then + mkdir -p "$FONTS_DST" + FONTS_NEW=0 + for _f in "$FONTS_SRC"/*.ttf "$FONTS_SRC"/*.otf "$FONTS_SRC"/*.TTF "$FONTS_SRC"/*.OTF; do + [ -f "$_f" ] || continue + _dest="$FONTS_DST/$(basename "$_f")" + if [ ! -f "$_dest" ]; then + cp "$_f" "$_dest" 2>/dev/null && FONTS_NEW=1 + echo "[presentation-manager] Nowa czcionka: $(basename "$_f")" + fi + done + if [ "$FONTS_NEW" -eq 1 ]; then + fc-cache "$FONTS_DST" + echo "[presentation-manager] Cache czcionek zaktualizowany" + fi +fi + +# Sygnał dla kiosk-manager.sh: SMB zamontowany, można przełączyć Chromium +touch /tmp/kiosk-smb-ready +echo "[presentation-manager] SMB gotowy — sygnał wysłany" + +until [ -f "$PRESENTATION_FILE" ]; do + echo "[presentation-manager] Oczekiwanie na plik: $PRESENTATION_FILE" >&2 + sleep 5 +done + +# Uruchom persistentny watchdog przed pierwszym startem LO +console_watchdog & +WATCHDOG_PID=$! +echo "[presentation-manager] Watchdog uruchomiony (PID=$WATCHDOG_PID)" + +CURRENT_MTIME=$(stat -c %Y "$PRESENTATION_FILE" 2>/dev/null || echo "0") +start_lo + +# Pętla: restartuje pokaz po zakończeniu lub gdy wykryje zmianę pliku +while true; do + sleep 5 + + NEW_MTIME=$(stat -c %Y "$PRESENTATION_FILE" 2>/dev/null || echo "0") + + # Plik zmieniony — odśwież pokaz + if [ "$NEW_MTIME" != "$CURRENT_MTIME" ]; then + echo "[presentation-manager] Wykryto zmianę pliku — restart pokazu" + CURRENT_MTIME="$NEW_MTIME" + start_lo + # Po 5 s wyślij sygnał do kiosk-manager.sh — restartuje Chromium po tym jak LO + # zakończy ładowanie i ustawi okno pokazu na prawym ekranie + ( sleep 5 + touch /tmp/kiosk-chromium-restart + echo "[presentation-manager] Sygnał restart Chromium wysłany" + ) & + continue + fi + + # LibreOffice zakończył (koniec pokazu) — uruchom ponownie + if ! kill -0 "${LO_PID:-0}" 2>/dev/null; then + echo "[presentation-manager] Pokaz zakończony — restart" + start_lo + fi +done diff --git a/smb-credentials.template b/smb-credentials.template new file mode 100644 index 0000000..2023a46 --- /dev/null +++ b/smb-credentials.template @@ -0,0 +1,5 @@ +# Plik poświadczeń SMB dla kiosku + +username=NAZWA_UZYTKOWNIKA +password=HASLO +domain=WORKGROUP diff --git a/sway-config b/sway-config new file mode 100644 index 0000000..410ccef --- /dev/null +++ b/sway-config @@ -0,0 +1,65 @@ +# Konfiguracja sway dla kiosku dwuekranowego +# Plik docelowy: /home/kiosk/.config/sway/config +# +# Nazwy wyjść wideo wykryj BEZ uruchomionego sway (działa zawsze jako root): +# for p in /sys/class/drm/card*-*; do echo "$(basename $p | sed 's/card[0-9]*-//'): $(cat $p/status)"; done +# +# lub gdy sway/kiosk JUŻ działa: +# WAYLAND_DISPLAY=wayland-0 XDG_RUNTIME_DIR=/run/kiosk wlr-randr +# +# Wpisz właściwe nazwy poniżej: + +set $OUTPUT_LEFT DP-1 +set $OUTPUT_RIGHT HDMI-1 + +# --- Wyjścia wideo --- +output * bg #000000 solid_color + +# Opcjonalnie: rotacja per-wyjście (odkomentuj jeśli wymagana) +# output $OUTPUT_LEFT transform 270 +# output $OUTPUT_RIGHT transform 270 + +# Przypisanie workspace do ekranów +workspace 1 output $OUTPUT_LEFT +workspace 2 output $OUTPUT_RIGHT + +# --- Przypisanie aplikacji do workspaces --- +# Chromium (prawy ekran — workspace 2) +# --app=URL generuje app_id: "chrome--Default" — używamy regex +assign [app_id="~^chrome"] workspace 2 +for_window [app_id="~^chrome"] move to workspace 2, fullscreen enable + +# Prezentacja LibreOffice Impress (lewy ekran — workspace 1) +# LO zawsze otwiera się na workspace 1 — assign to potwierdza +assign [app_id="soffice"] workspace 1 +assign [class="libreoffice-impress"] workspace 1 +for_window [app_id="soffice"] fullscreen enable +for_window [class="libreoffice-impress"] fullscreen enable +# Jeśli powyższe nie działają, sprawdź rzeczywistą klasę: +# WAYLAND_DISPLAY=wayland-0 XDG_RUNTIME_DIR=/run/kiosk swaymsg -t get_tree | grep -E 'app_id|class' + +# --- Wygląd --- +default_border none +default_floating_border none +hide_edge_borders both + +# Ukryj kursor — niewidoczny motyw XCursor (1×1 px, całkowicie przezroczysty) +# Działa niezależnie od pozycji kursora i bez dodatkowych pakietów. +# Plik binarny XCursor tworzony przez printf przy każdym starcie sway. +# Jeśli motyw nie zdąży załadować się przed pierwszym rysowaniem → hide_cursor 1 jako fallback. +seat * hide_cursor 1 +seat * xcursor_theme blank 1 +exec_always bash -c ' +D=/home/kiosk/.icons/blank/cursors +mkdir -p "$D" +# 68-bajtowy XCursor: 1×1 px, hotspot 0,0, ARGB 0x00000000 +for n in left_ptr default pointer crosshair text watch wait move size_all fleur; do + printf "\x58\x63\x75\x72\x10\x00\x00\x00\x00\x00\x01\x00\x01\x00\x00\x00\x02\x00\xfd\xff\x01\x00\x00\x00\x1c\x00\x00\x00\x24\x00\x00\x00\x02\x00\xfd\xff\x01\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x32\x00\x00\x00\x00\x00\x00\x00" > "$D/$n" +done +printf "[Icon Theme]\nName=blank\n" > /home/kiosk/.icons/blank/index.theme' + +# Wygaszanie ekranów przy uśpieniu (bez -w: ignoruje inhibitory Chromium) +exec swayidle before-sleep "wlopm --off '*'" + +# Uruchom skrypt kiosku +exec /home/kiosk/kiosk-manager.sh diff --git a/waiting.html b/waiting.html new file mode 100644 index 0000000..6a3a4f5 --- /dev/null +++ b/waiting.html @@ -0,0 +1,105 @@ + + + + + + Oczekiwanie na sieć + + + +
+ +
+
Oczekiwanie na sieć
+
+ + diff --git a/wol-enable.service b/wol-enable.service new file mode 100644 index 0000000..df79186 --- /dev/null +++ b/wol-enable.service @@ -0,0 +1,17 @@ +[Unit] +Description=Enable Wake on LAN on all ethernet interfaces +After=network.target + +[Service] +Type=oneshot +# Włącza WoL (g = magic packet) na każdym interfejsie sieciowym +ExecStart=/bin/bash -c '\ + for iface in $(ls /sys/class/net | grep -v lo); do \ + ethtool -s "$iface" wol g 2>/dev/null \ + && echo "WoL enabled: $iface" \ + || true; \ + done' +RemainAfterExit=yes + +[Install] +WantedBy=multi-user.target