Создаём простой киоск на Linux с помощью Yocto
Ранее мы рассмотрели сборку минимального Linux для разных аппаратных платформ и разработку эмулятора игры "Ну, погоди!". Теперь объединим эти направления и создадим компактный дистрибутив, который будет запускать эмулятор в режиме киоска.
Киоск на Linux - это система с максимально упрощённым пользовательским интерфейсом. Пользователь видит только нужное приложение, а доступ к рабочему столу, терминалу и большинству системных функций скрыт. Такой подход знаком по банкоматам, терминалам оплаты, кассам самообслуживания, информационным панелям и автоматам продажи билетов.
Собрать подобную систему можно вручную, начиная с загрузчика и минимального набора утилит. Однако для практического проекта удобнее использовать Yocto Project. Это промышленный стандарт для создания встраиваемых Linux-дистрибутивов. Yocto даёт широкие возможности настройки, хотя требует больше времени на освоение и сборку, чем готовые дистрибутивы.
В качестве аппаратной платформы подойдёт Raspberry Pi Zero 2 W. Несмотря на компактные размеры, его производительности достаточно для запуска простого графического эмулятора. Сборка выполняется на компьютере под управлением Linux. Первая Yocto сборка образа может занимать от нескольких десятков минут до нескольких часов - всё зависит от процессора, объёма оперативной памяти, скорости накопителя и интернет-соединения.
Как устроен Yocto Project
Основой экосистемы выступает OpenEmbedded - фреймворк для сборки Linux-систем под встраиваемые устройства. В его состав входит BitBake, обрабатывающий инструкции сборки, а также большое количество рецептов.
Рецепт описывает, как получить конкретный программный компонент: откуда скачать исходники, какие зависимости установить, какие команды выполнить и какие файлы добавить в итоговый образ. Наборы рецептов, конфигураций и дополнений объединяются в слои.
Yocto Project использует OpenEmbedded и дополняет его документацией, инструментами, эталонной сборкой Poky, дополнительными слоями, стандартами и средствами тестирования. На первый взгляд Yocto Linux может показаться чрезмерно сложным, но после освоения основных понятий структура проекта становится значительно понятнее. Более подробный разбор принципов и практическая последовательность действий представлены в материале о [создании киоска Linux](https://habr.com/ru/companies/ruvds/articles/1072714/?utm_campaign=1072714&utm_source=habrahabr&utm_medium=rss).
Для начала клонируем Poky и слои, необходимые для Raspberry Pi:
```bash
git clone -b scarthgap https://git.yoctoproject.org/poky
git clone -b scarthgap https://github.com/openembedded/meta-openembedded
git clone -b scarthgap https://github.com/agherzan/meta-raspberrypi
```
Затем переходим в каталог Poky и создаём окружение сборки:
```bash
cd poky
source oe-init-build-env build-rpi
```
В каталоге `build-rpi` появятся конфигурационные файлы. Подключим дополнительные слои:
```bash
bitbake-layers add-layer ../meta-openembedded/meta-oe
bitbake-layers add-layer ../meta-openembedded/meta-python
bitbake-layers add-layer ../meta-openembedded/meta-networking
bitbake-layers add-layer ../meta-raspberrypi
```
Проверить результат можно командой:
```bash
bitbake-layers show-layers
```
Выбор Raspberry Pi и первая сборка
Открываем `conf/local.conf` и указываем целевую платформу:
```conf
MACHINE = "raspberrypi0-2w-64"
```
Параметр `MACHINE` определяет аппаратную конфигурацию. В данном случае выбирается Raspberry Pi Zero 2 W с 64-битной архитектурой. На основании этого значения Yocto подбирает загрузчик, ядро, device tree, параметры процессора, графические драйверы и специфичные для платы пакеты.
Для проверки окружения можно собрать готовый образ:
```bash
bitbake core-image-sato
```
После завершения работы BitBake файл образа появится в каталоге:
```text
tmp/deploy/images/raspberrypi0-2w-64/
```
Нас интересует файл вида:
```text
core-image-sato-raspberrypi0-2w-64.rootfs-*.wic.bz2
```
Его можно записать на SD-карту при помощи Balena Etcher. После этого подключаем к Raspberry Pi клавиатуру, мышь, монитор и питание. Получится полноценная тестовая система, но пока ещё не киоск: в ней присутствуют лишние компоненты, а графическая оболочка не ограничена запуском одного приложения.
Собственный слой и приложение
Чтобы не изменять файлы Poky и сторонних слоёв напрямую, создадим собственный слой:
```bash
bitbake-layers create-layer ../meta-rpi-kiosk
bitbake-layers add-layer ../meta-rpi-kiosk
```
В нём будут храниться настройки дистрибутива, рецепты приложения и конфигурация графической сессии.
Для подавления лишнего вывода ядра создадим файл:
```text
meta-rpi-kiosk/recipes-bsp/bootfiles/rpi-cmdline.bbappend
```
Содержимое может выглядеть так:
```bitbake
CMDLINE_APPEND = " quiet loglevel=0 vt.global_cursor_default=0"
```
Это уменьшает объём диагностических сообщений, отображаемых во время загрузки. Пользователь киоска не должен видеть технический текст, который не относится к работе приложения.
Затем добавим конфигурацию собственного дистрибутива в файл:
```text
meta-rpi-kiosk/conf/distro/rpi-kiosk.conf
```
Пример базовых параметров:
```conf
DISTRO = "rpi-kiosk"
DISTRO_NAME = "Raspberry Pi Kiosk"
DISTRO_VERSION = "1.0"
LICENSE_FLAGS_ACCEPTED = "commercial"
```
Чтобы использовать этот дистрибутив, указываем его в `local.conf`:
```conf
DISTRO = "rpi-kiosk"
```
Приложение "Ну, погоди!" оформляем отдельным рецептом, например:
```text
meta-rpi-kiosk/recipes-games/nu-pogodi/nu-pogodi.bb
```
В рецепте описываются исходные файлы, зависимости, этап установки и команда запуска. Если эмулятор хранится локально, его можно добавить через `file://`, а если размещён в Git-репозитории - указать URL и конкретную ревизию.
```bitbake
SUMMARY = "Nu, Pogodi emulator"
LICENSE = "CLOSED"
SRC_URI = "file://nu-pogodi
file://nu-pogodi.desktop"
S = "${WORKDIR}"
do_install() {
install -d ${D}${bindir}
install -m 0755 ${S}/nu-pogodi ${D}${bindir}/nu-pogodi
install -d ${D}${datadir}/applications
install -m 0644 ${S}/nu-pogodi.desktop
${D}${datadir}/applications/nu-pogodi.desktop
}
FILES:${PN} += "${datadir}/applications"
```
После этого добавляем пакет в образ:
```conf
IMAGE_INSTALL:append = " nu-pogodi"
```
Здесь важно оставить пробел перед названием пакета: переменная расширяется текстовой строкой, и без пробела имена могут объединиться некорректно.
Запуск графической сессии
Для киоска требуется оконный менеджер, который не будет предоставлять пользователю обычный рабочий стол. Простым вариантом является Matchbox. Он подходит для маломощных устройств и позволяет запускать приложение в полноэкранном режиме.
В собственный слой добавляем файлы сессии и конфигурации Matchbox:
```text
meta-rpi-kiosk/recipes-graphics/matchbox/
```
Команда запуска может выглядеть следующим образом:
```sh
#!/bin/sh
xset -dpms
xset s off
xset s noblank
matchbox-window-manager -use_titlebar no &
exec /usr/bin/nu-pogodi
```
Команды `xset` отключают энергосбережение монитора и автоматическую заставку. Это особенно важно для терминалов, которые должны постоянно отображать интерфейс. Matchbox запускается без заголовков окон, а затем стартует единственное пользовательское приложение.
Для автоматического запуска X-сессии удобно использовать systemd-сервис или графический дисплейный менеджер. В простом варианте сервис запускает Xorg от специального пользователя, после чего передаёт управление скрипту сессии. Такой пользователь не должен иметь административных прав и доступа к конфиденциальным каталогам.
Сборка и проверка
После добавления всех рецептов собираем образ:
```bash
bitbake core-image-sato
```
Если требуется сделать систему компактнее, можно использовать собственный image-рецепт вместо `core-image-sato`. В него включают только нужные пакеты: Xorg, Matchbox, драйверы ввода, графическую библиотеку, приложение и минимальный набор системных утилит. Так получается минимальный Linux Yocto, ориентированный на одну задачу.
Готовый файл снова появится в `tmp/deploy/images/raspberrypi0-2w-64/`. После записи на SD-карту Raspberry Pi загрузится, автоматически запустит графическую сессию и откроет эмулятор без показа рабочего стола.
Для отладки полезно временно оставить SSH-сервер, включить журналирование и не скрывать сообщения ядра. После завершения настройки эти компоненты можно удалить, чтобы уменьшить образ и сократить поверхность атаки.
Киоск желательно дополнить автоматическим перезапуском приложения. Если программа завершилась из-за ошибки, systemd может поднять её снова:
```ini
[Service]
ExecStart=/usr/bin/nu-pogodi
Restart=always
RestartSec=2
```
Также стоит отключить сочетания клавиш, позволяющие выйти из полноэкранного режима, ограничить доступ к виртуальным терминалам и настроить корректное завершение работы при нажатии кнопки питания.
В результате получается специализированный киоск на Linux, который загружается на Raspberry Pi Zero 2 W и запускает только нужное приложение. Yocto требует заметных затрат времени на первоначальное освоение и сборку, зато позволяет повторяемо получать образы для разных устройств, контролировать состав системы и хранить все настройки в виде кода. Для учебного проекта это хороший способ познакомиться с встраиваемой разработкой, а для промышленного решения - основа масштабируемой сборочной инфраструктуры.


