Оболочка ChromeOS Ash. На обычном Linux. Это плохо.
Этот репозиторий берёт готовый бинарник ChromiumOS Chrome, ставит его перед X11 и учит обычный Linux отвечать на ChromeOS D-Bus API, которые ждёт Ash.
На выходе — десктопная оболочка ChromeOS: шельф, лаунчер, шторка быстрых настроек, системный трей, Terminal SWA — в качестве вашего Linux-десктопа.
Всё здесь — обычные shell-скрипты, Go и Meson: Nix не нужен (старая Nix-упаковка удалена). Целевые системы — x86_64 Ubuntu/Debian и Arch; ничего ультра-Ubuntu-specific в коде нет, требуются рабочие glibc, D-Bus и X.
- Как это устроено
- Требования
- Структура репозитория
- Быстрый старт
- Что делает
setup-system.sh - Сессия: что происходит при запуске
- Переменные окружения
- Патчи бинарника
- Режим ChromeOS: lsb-release и mojo
- Что работает / сырые места / что не работает
- Лок-экран и разлочка
- Проверка и диагностика
- Обновление ревизии Chrome и хакинг
- Ограничения безопасности
Три независимых куска, которые вместе притворяются Chromebook'ом:
┌─────────────────── ваш X-дисплей (:0) ───────────────────┐
│ Ash Chrome (dist/chrome/chromeos-ash) │
│ --ozone-platform=x11 --x11-display=:0 │
│ │ │
│ └── хостит Wayland-композитор │
│ $XDG_RUNTIME_DIR/wayland-0 │
│ └── sommelier (-X) → wayland-1 │
│ └── Xwayland :10 → Linux-приложения │
└──────────────────────────────────────────────────────────┘
- Ash — это X11-клиент: он рисует себя на
:0(настоящий X-сервер: LightDM/Xorg или поднятый самим скриптом). - Ash же — Wayland-сервер: Chrome запускается с
--enable-wayland-server --wayland-server-socket=wayland-0, то есть сам принимает Wayland-соединения. - sommelier кладёт перед ним Xwayland на
:10, поэтому «управляемые» Linux-приложения (Crostini-обвязка) попадают внутрь рабочего стола Ash, а не наружу. Запускаемые из Ash Linux-приложения обязаны ходить в:10, а не в хостовый Xorg — иначе их окна уходят наружу и в сессии не видны. За это отвечаетsession/stubs/shell-stub.sh: он форситDISPLAY=:10для запускаемых приложений (хост у Ash на:0, поэтому коллизии нет).
Второй кусок — Go-бриджи. Ash уверен, что живёт в ChromeOS и общается с системными демонами по D-Bus системной шины. Бриджи занимают эти имена и переводят вызовы на Linux:
| Имя на шине | Бинарник | Куда уходит |
|---|---|---|
org.chromium.flimflam |
shill-bridge |
NetworkManager (состояние сети, подключение Ethernet/Wi-Fi) |
org.chromium.cras |
cras-bridge |
PulseAudio/PipeWire через pactl (громкость, узлы Speaker/Headphone/Mic) |
org.chromium.PowerManager |
power-bridge |
яркость через sysfs (+ fallback на xrandr), UPower, systemctl suspend/reboot |
org.chromium.SessionManagerorg.chromium.UserDataAuth |
session-bridge |
«запущенная сессия», состояние входа, гостевой профиль /home/chronos/user |
org.chromium.VmConcierge, VmCicerone,DlcService, ImageLoader, debugd |
crostini-bridge |
локальные .desktop-файлы: регистрация в лаунчере и запуск приложений на :10 |
org.chromium.Rmad |
rmad-bridge |
заглушка RMA-демона |
/run/mojo/service_manager.sock |
mojo-stub |
Mojo service manager (ACCEPT_INVITEE + слив сообщений) |
| — | register-apps |
разовый helper: пишет crostini.registry в Preferences, чтобы .desktop-файлы появились в лаунчере |
Третий кусок — сессионные скрипты в session/: поднимают бриджи, готовят профиль, инжектируют UI терминала и крутят Ash в цикле перезапуска.
- x86_64 (снапшот ChromiumOS собирается только под это).
- Рабочий D-Bus (системная шина) и X-сервер (или готовность дать скрипту поднять свой).
./scripts/doctor.shпечатает, чего именно не хватает на вашей машине — запускай его первым делом.
Ubuntu / Debian
# сборка
sudo apt install --no-install-recommends \
golang-go meson ninja-build gawk pkg-config python3 python3-jinja2 gcc make \
libwayland-dev libwayland-bin wayland-protocols libxkbcommon-dev libdrm-dev \
libgbm-dev libpixman-1-dev libxcb1-dev libxcb-composite0-dev libxcb-shape0-dev \
libxcb-xfixes0-dev curl unzip tar perl libc-dev build-essential
# рантайм (libXcomposite — самая частая пропущенная библиотека у Chrome)
sudo apt install --no-install-recommends \
xwayland x11-xserver-utils xauth dbus \
libnss3 libnspr4 libcups2 libdbus-1-3 libexpat1 libxkbcommon0 libxcomposite1 \
libasound2t64 pulseaudio-utils librsvg2-bin \
network-manager gnome-keyring polkit-gnome xdg-desktop-portal-gtk
# опционально: только если сессии нужен собственный X-сервер (запуск с TTY)
sudo apt install xserver-xorgНа Debian 12 пакет называется libcups2, на Ubuntu 24.04 — libcups2t64; libasound2/libasound2t64 аналогично.
Arch
sudo pacman -S --needed go meson ninja gawk pkgconf python python-jinja gcc make \
wayland libxkbcommon libdrm libgbm pixman libxcb libxcomposite \
xorg-xwayland xorg-xrandr xorg-xrdb xorg-xhost xorg-xauth dbus \
nss nspr cups alsa-lib libpulse pipewire-pulse librsvg \
networkmanager gnome-keyring polkit-gnome xdg-desktop-portal-gtk unzipЗачем что-то из рантайма:
xwayland— без него Linux-приложения внутри Ash не рисуются;x11-xserver-utils—xrandr(границы окна Ash) иxrdb(цвета для xterm);pulseaudio-utils—pactl, через которыйcras-bridgeкрутит громкость;librsvg2-bin—rsvg-convertдля SVG-иконок в лаунчере;network-manager—shill-bridgeбез NM просто пишет в лог «NM not available»;gnome-keyring,polkit-gnome,xdg-desktop-portal-gtk— «сантехника» Linux-приложений (выбор файлов, polkit-промпты, ключница).
chromeos-bin/REVISION пин ревизии снапшота ChromiumOS (1670871)
chromeos-bin/fetch.sh скачивание + проверка контрольных сумм
chromeos-bin/chrome/ распакованный снапшот (после make fetch, ~870 МБ)
scripts/common.sh общие хелперы ВСЕХ скриптов: ASH_ROOT, die/log/note (с префиксом вызывающего), need/have
scripts/build-chrome.sh патчинг бинарника → dist/chrome/
scripts/build-sommelier.sh сборка sommelier → dist/sommelier/
scripts/build-bridges.sh сборка Go-бриджей → dist/bin/
scripts/displayfix.c C-шим libdisplayfix.so
scripts/setup-system.sh разовая установка под root
scripts/doctor.sh проверка зависимостей + преflight сессии
scripts/verify.sh проверка артефактов сборки
scripts/smoke-test.sh проверка бриджей на живой системной шине
session/start-session.sh точка входа: бриджи + сессия
session/chromeos-linux-session основной сессионный цикл
session/terminal-inject.pl инжект UI терминала по CDP
session/chromeos-ash-open-url ручной helper: открывает URL в запущенном Ash через CDP (обработчиком по умолчанию НЕ регистрируется)
session/dbus-policies/ D-Bus политика (исходник для setup-system.sh)
session/tmpfiles.d/ шаблон tmpfiles (исходник для setup-system.sh)
session/stubs/shell-stub.sh заглушки vsh / crosh
dbus-bridges/cmd/*/ исходники бриджей (Go)
terminal/terminal-ui.js UI терминала, который инжектируется в SWA
Makefile цели сборки
dist/ всё собранное (~900 МБ, не коммитится)
.cache/ временные файлы сборки (не коммитится)
make # скачать снапшот, пропатчить, собрать sommelier и бриджи
sudo ./scripts/setup-system.sh --user "$USER"
./session/start-session.shЭто всё: интеграции с дисплейным менеджером нет и не будет, systemd-юнитов нет. start-session.sh сам поднимает бриджи (каждый под watchdog-ом перезапуска), затем сессию, и по своему выходу всё убивает.
Запускать ./session/start-session.sh из обычного терминала (TTY) либо из любого окна — отдельно startx запускать не нужно: если DISPLAY не задан, скрипт сам поднимает X через startx (первый свободный :N) и становится его клиентом; если DISPLAY уже есть (X-сеанс LightDM/GDM/SDDM), используется он. Внутри уже поднятого X-сеанса start-session.sh просто работает поверх него.
Пересборка с нуля: make clean && make. Время сборки — около 5 минут, из них 4 — патчинг 603-мегабайтного бинарника.
| Цель | Что делает |
|---|---|
make (= all) |
chrome + sommelier + bridges |
make fetch |
скачать и проверить снапшот ChromiumOS |
make chrome |
патчинг бинарника → dist/chrome/ |
make sommelier |
сборка sommelier → dist/sommelier/ |
make bridges |
сборка Go-бриджей → dist/bin/ |
make vendor |
залить Go-зависимости в dbus-bridges/vendor/ для оффлайн-сборки |
make verify |
проверить артефакты: библиотеки, интерпретатор, патчи, парсинг D-Bus-политики |
make test |
прогнать бриджи на реальной системной шине (нужна политика из setup-system.sh) |
make clean |
удалить dist/ и кэш сборки sommelier/go |
make distclean |
+ удалить скачанный снапшот |
- Проверить
./scripts/doctor.sh— все пунктыok(включая== session preflight ==). make— завершается строкойbridges -> .../dist/bin/.sudo ./scripts/setup-system.sh --user "$USER"— печатаетsetup-system: done../scripts/smoke-test.sh— 11 passed / 0 failed (можно и без X, это только D-Bus)../session/start-session.sh— появляется окно Ash;ls /tmp/.X11-unix/показывает два сокета: хост X (X0либоX1, какой свободный взял startx) и XwaylandX10.
Один шаг требует root: D-Bus-политика, системные каталоги и «личность» ОС живут в /etc. Скрипт безопасно перезапускается и знает, как всё откатить.
| Шаг | Что появляется |
|---|---|
| D-Bus-политика | /etc/dbus-1/system.d/chromeos-bridges.conf — копия session/dbus-policies/chromeos-bridges.conf с подставленным пользователем; после изменения шина перезапускается |
| Системные каталоги | /etc/tmpfiles.d/chromeos-linux.conf + systemd-tmpfiles --create: /var/lib/ash-profile, /run/mojo, /home/chronos/user, заглушки termina-DLC и VM-диска, /var/log/chrome, /var/lib/metrics/..., а также заглушка /dev/kvm — только если KVM на хосте нет (без неё Chrome считает, что виртуализации нет, и молча не открывает Linux-приложения; при наличии /sys/class/misc/kvm строка из конфига выбрасывается, а уже установленная заглушка снимается — она мешала бы udev создать настоящий device-node) |
| Личность ОС | в /etc/lsb-release дописываются DEVICETYPE=CHROMEBOOK, CHROMEOS_RELEASE_NAME=Chrome OS и CHROMEOS_RELEASE_DESCRIPTION=Chrome OS (chromeos-linux) (исходный файл сохраняется в /etc/lsb-release.chromeos-linux.bak) |
| Часовой пояс | создаётся /var/lib/timezone/localtime → $(readlink -f /etc/localtime): без него Ash читает TZ из ChromeOS-пути, не находит его и молча ставит America/Los_Angeles на часах шельфа. /var переживает перезагрузку, поэтому достаточно одного запуска |
| Заглушки шелла | симлинки vsh и crosh → session/stubs/shell-stub.sh и в /usr/local/bin, и в /usr/bin (Ash терминала зовёт их по абсолютному пути /usr/bin/vsh, PATH не ищет — см. преflight в doctor.sh) |
Опции:
--user NAME— под каким пользователем крутится Ash (по умолчанию$SUDO_USER);--undo— убрать политику, tmpfiles и заглушки, восстановитьlsb-release, снять симлинк часового пояса и заменённыеvsh/crosh;--no-mojo— не прописыватьCHROMEOS_RELEASE_NAME(см. раздел про режим ChromeOS);--force— заменить существующие заглушкиvsh/crosh(в/usr/local/binи/usr/bin; старые уходят в.bak).
Группа
users. Скрипт делаетchown "$USER:users"профиля, а все каталоги из tmpfiles создаются с группойusers. На Debian/Ubuntu она есть по умолчанию (USERS_GROUP=users), на части minimal-установок — нет. Если её не окажется,setup-systemупадёт наchown/systemd-tmpfilesсgroup 'users' does not exist: создай её (sudo groupadd users) либо заранее поменяй группу во всех строкахsession/tmpfiles.d/chromeos-linux.conf(и вchownвнутриscripts/setup-system.sh).
Почему без systemd не обойтись «вручную»: политика D-Bus подхватывается только при старте демона. На systemd-less системе (или в контейнере) скрипт предупредит, что dbus надо перезапустить или перезагрузиться.
Что не делает setup-system.sh и не требует: он не ставит юнитов, не трогает PATH, не регистрирует сессию у дисплейного менеджера. В /usr/bin он пишет только две заглушки vsh/crosh (туда же их ждёт абсолютный путь из бинарника) и ничего больше.
./session/start-session.sh:
- Проверяет системную шину и что профиль (
/var/lib/ash-profile) записываем — иначе вежливо просит прогнатьsetup-system.sh. - Поднимает бриджи:
shill,cras,session,crostini,rmad(сPULSE_SERVER=unix:$XDG_RUNTIME_DIR/pulse/native),powerиmojo-stub. Каждый — под watchdog-ом: падение → перезапуск через 2 с, пять падений подряд → пауза 30 с (типичная причина — политика D-Bus не применена). - Запускает
session/chromeos-linux-sessionи ждёт его; при выходе убивает бриджи и пробрасывает код возврата.
chromeos-linux-session (цикл):
- Готовит профиль:
mkdir,CHROMEOS_SESSION_LOG_DIR, метки «OOBE пройдено» (.oobe_completed,First Run,Local State), чистка токенов входа, JSON-правкиPreferences(включён Crostini,exit_type=Normal). - Прогоняет
register-apps— пишет Linux-приложения в лаунчер Ash. - Запускает в фоне
terminal-inject.pl: ждёт CDP наlocalhost:9222(до 60 с), открываетchrome-untrusted://terminal/, кладётterminal/terminal-ui.jsвlocalStorage.tи перезагружает страницу. Это срабатывает потому, что в бинарникеterminalPrivate.openVmshellProcess(...)заменён наeval(localStorage.t||""). - Поднимает в фоне sommelier + Xwayland на
:10(ждёт появленияwayland-0и ответа CDP, выставляет чёрно-белые цвета xterm черезxrdb). - Определяет границы окна Ash через
xrandrи запускает Chrome с закреплённым набором флагов (--enable-wayland-server,--ozone-platform=x11,--no-sandbox,--enable-features=Crostini, отключённые sync/гаия/аккаунты,--remote-debugging-port=9222,--user-data-dir=/var/lib/ash-profile,--login-user/--login-profile, GPU-блок--ignore-gpu-blocklist+--disable-gpu-sandbox+--enable-logging=stderr— последний вместе с no-opRedirectChromeLoggingи есть то, что кладёт ERROR'ы вash.log). - Когда Chrome вышел — убивает sommelier/Xwayland, ждёт остаточных процессов
chromeи начинает заново.
| Как запустил | Что произойдёт |
|---|---|
| Из терминала в X-сеансе | используется ваш DISPLAY и ваш XAUTHORITY (копируется в /tmp/ash-xauth) |
С TTY (нет DISPLAY) |
start-session.sh сам поднимает X через startx (первый свободный :N), становясь его клиентом; X убивается при выходе |
XAUTHORITY недоступен (как у LightDM без копии cookie) |
делается xhost +local: (фолбэк) |
Если на машине уже есть wayland-0 (например, вы внутри другого Ash или Wayland-сессии) — старые сокеты удаляются, --enable-wayland-server Ash займёт этот слот.
Перед подъёмом бриджей start-session.sh отцепляет всё дерево сессии от управляющего терминала (exec setsid --wait), чтобы чтение /dev/tty кем-либо из процессов сессии не могло остановить группой всю оболочку — подробности и разбор инцидента во врезке в «Типовых проблемах».
| Переменная | По умолчанию | Что делает |
|---|---|---|
ASH_PROFILE_DIR |
/var/lib/ash-profile |
профиль и все логи; можно указать каталог, которым вы владеете, если не хотите давать права через setup-system.sh |
ASH_LOGIN_USER |
linux@local |
аккаунт, под которым входит Ash (--login-user) |
ASH_LOGIN_PROFILE |
test-user |
подкаталог профиля (--login-profile) |
ASH_DIRECT_LOGIN |
1 |
0 — не передавать флаги входа вообще |
ASH_LOGIN_MANAGER |
0 |
1 — вместо прямого входа --login-manager --oobe-skip-to-login |
ASH_DISPLAY_SIZE |
1920x1080 |
запасные границы окна, если xrandr ничего не сообщает |
ASH_DISPLAY_BOUNDS |
— | явные границы ШxВ (без промеров) |
ASH_XRANDR_OUTPUT |
основной вывод | принудительно указать, какой xrandr-вывод менять по размеру |
ASH_MOJO |
1 |
0 — не запускать mojo-stub в start-session.sh |
ASH_SMOKE_TIMEOUT |
6 |
сколько секунд make test ждёт появления имени на шине |
ASH_TERMINAL_UI |
— | путь к terminal-ui.js для terminal-inject.pl (по умолчанию аргумент из сессии) |
ASH_LOG_DIR |
${TMPDIR:-/tmp}/chromeos-linux |
куда пишут заглушки vsh/crosh |
Снапшот патчится в scripts/build-chrome.sh. Каждый патч сверяет ожидаемые байты и падает с ошибкой, если бинарник не совпал — молчаливо «починить» чужой бинарник нельзя.
| Патч | Что делает | Без него |
|---|---|---|
| Терминальный стаб | chrome.terminalPrivate.openVmshellProcess(...) → `eval(localStorage.t |
|
| Расслабление CSP | 'wasm-unsafe-eval' → 'unsafe-eval' (10 мест) |
инжектированный UI терминала не выполняется |
DisplayConfigurator::Init |
прыжок через нативную настройку дисплея | крэш на X11 (нет NativeDisplayDelegate) |
RunPendingConfiguration |
ret (no-op) |
крэш при смене конфигурации дисплея |
RedirectChromeLogging |
ret (no-op) |
Chrome уводит логи в ChromeOS-путь |
OnDisconnect |
ret (no-op) |
крэш при таймауте mojo-канала |
HasInternalDisplay |
всегда true |
нет ползунка яркости |
IsSystemBlurEnabled |
всегда true |
blur панели/шторки гейтится не на GPU, а на ОЗУ: chromeos::features::IsSystemBlurEnabled() — это буквально AmountOfTotalPhysicalMemory() > 4GiB из /proc/meminfo, без FeatureList и без флага. На хостах ≤4 ГБ (гости VirtualBox, типовые Haswell-ноутбуки) панель и шторка рисуются непрозрачными, хотя композитор blur умеет рисовать на чём угодно (viz::SoftwareRenderer::GetBackdropFilterShader есть). С патчем — blur при любом объёме RAM; на малорамных хостах это стоит части FPS в анимациях |
| Раскладка RU/EN | OzonePlatformX11::InitializeUI вместо StubKeyboardLayoutEngine собирает пару XkbEvdevCodes + XkbKeyboardLayoutEngine (как DRM-платформа) и регистрирует её в KeyboardLayoutEngineManager, а thunk на месте мёртвого Stub::Lookup перенаправляет StubInputController::SetCurrentLayoutByName в зарегистрированный движок |
смена RU/EN в шторке двигает только индикатор в трее: набор текста остаётся US (нужны xkb-данные из xkb-data / xorg-xkbdata) |
IsSharedImageFormatSupported |
всегда true |
exo отдаёт BGRX_8888, а проверка пропускает только BGRA_8888 → CanImportNativePixmap говорит «нет» и OzoneImageBackingFactory не регистрируется: Could not find SharedImageBackingFactory |
GetNativePixmapSupportType |
всегда kX11Pixmap (2) |
выбирается ветка kDMABuf, требующая EGL_EXT_image_dma_buf_import, которой у ANGLE здесь нет → импорт не создаётся |
NativePixmapEGLX11Binding::Create |
вырезан встроенный чек format == kBGRA_8888 (NOP на je) |
тот же чек, но вызываемый уже внутри Create: отказ приходит как ProduceSkiaGanesh failed to create GL representation (в release это только VLOG, в логе тишина) → белые окна |
PopulateMappableDrmFormatsForExo |
из набора кандидатов убраны RGB-порядочные форматы: в rodata-таблице RGBA_8888→BGRA_8888, RGBX_8888→BGRX_8888, плюс jne→jmp пропускает условные RGBA_F16 и RGBA_1010102 |
exo рекламирует в dmabuf-фидбеке AB24/XB24/AB30/AB4H — память R,G,B,A, а импорт идёт через X11-визуал depth-32 (память B,G,R,A), где fourcc теряется → красный уходит в синий во всех wayland-клиентах, рисующих через dmabuf (helium, Qt). shm-клиенты (GTK, Xwayland) не страдают — их память и так B,G,R,A |
| crashpad-wrapper | всегда добавляет --database |
падение на старте crashpad-handler'а |
Чего патч не делает: он не трогает интерпретатор и RPATH. Снапшот уже использует /lib64/ld-linux-x86-64.so.2 и RUNPATH=$ORIGIN, поэтому patchelf не нужен вообще — ldd на обычном дистрибутиве всё резолвит, а libminigbm.so подхватывается из каталога бинарника.
Отдельно от патчей бинарника собирается шим libminigbm.so (scripts/minigbm_shim.c) — Chrome слинкован против ChromeOS-овского minigbm, а на хосте есть только Mesa libgbm, и они не сходятся в двух местах: биты usage (у minigbm есть биты, которых нет у Mesa) и коды типа импорта (GBM_BO_IMPORT_FD_MODIFIER = 0x5505 у minigbm против 0x5504 у Mesa → default: return NULL). Шим кладётся вместо оригинала (он остаётся рядом как libminigbm.so.orig), резолвит 19 точек входа в libgbm.so.1 через dlopen, транслирует тип импорта, маскирует usage, при неудаче падает обратно на modifier=0, затем на legacy FD-импорт и пишет трассировку в /tmp/gbm-shim.log. Без шима GPU-процесс не находит backing'и для SharedImage — см. таблицу GPU-патчей выше, они дополняют его, а не заменяют.
Отдельно собирается libdisplayfix.so (scripts/displayfix.c) — крошечный shim, который лаунчер кладёт в LD_PRELOAD. Он всегда ставит XAUTHORITY=/tmp/ash-xauth, а если DISPLAY пуст — ставит :0 и пишет диагностику в /tmp/xwrap.log. Shim молчит, если чинить нечего (иначе лог раздулся бы от каждой дочерней процессии Chrome).
CHROMEOS_RELEASE_NAME=Chrome OS в /etc/lsb-release переключает Ash с встроенных fallback-клиентов на настоящие D-Bus-клиенты — то есть на наши бриджи. Побочный эффект: включается режим Mojo IPC ChromeOS, которому нужен собеседник /run/mojo/service_manager.sock. Именно поэтому:
setup-system.shпо умолчанию пишет оба ключа (DEVICETYPE=CHROMEBOOK+CHROMEOS_RELEASE_NAME=Chrome OS);start-session.shпо умолчанию запускаетmojo-stub, который делает ACCEPT_INVITEE и сливает сообщения (заглушка, но рабочая).
Отключить: sudo ./scripts/setup-system.sh --no-mojo (и при желании ASH_MOJO=0 перед запуском сессии). Тогда Ash вернётся на встроенные fallback-клиенты, а мосты станут почти не нужны.
Без DEVICETYPE=CHROMEBOOK Ash считает машину «обычным Linux» и ведёт себя заметно скучнее.
Рабочий стол поднимается и вполне пригоден как очень странный Linux-шелл:
- Ash стартует в постоянный ChromeOS-подобный десктоп, OOBE пропущен.
- Шельф, лаунчер, шторка быстрых настроек и системный трей открываются.
- Ставятся все 24 System Web App, включая Terminal, Files, Settings и Camera.
- Terminal SWA поднимает настоящий bash через инжектированный UI.
- Chrome переживает перелогин и ре-экзекуции сессии, не вылетая из дисплейного менеджера.
- Работают бриджи shill, CRAS, powerd, session_manager, UserDataAuth, Crostini, rmad и совместимость Mojo service manager.
- NetworkManager отдаёт шилл-бриджу реальное состояние сети.
- Громкость работает через CRAS-бридж (PulseAudio/PipeWire).
- UPower кормит бридж питания состоянием батареи.
- Яркость работает через sysfs с фолбэком на
xrandr. - Работает Linux-сантехника: выбор файлов, xdg-desktop-portal-gtk, polkit-промпты, gnome-keyring.
- Работает нативный лок-экран Ash (
Ctrl+Shift+L) и разлочка любым паролем (см. «Лок-экран и разлочка»). - Переключение RU/EN в шторке быстрых настроек меняет раскладку и для набора текста (адресная строка, оконный менеджер, экранная клавиатура), а не только для индикатора в трее — см. патч «Раскладка RU/EN».
- Linux-приложения рендерятся через GPU: sommelier запускает Xwayland с
--force-drm-deviceи--enable-linux-dmabuf, поэтому клиенты получаютzwp_linux_dmabuf_v1, а путь dmabuf → exo →OzoneImageBackingзакрыт тремя GPU-патчами бинарника вместе с шимомlibminigbm.so(см. «Патчи бинарника»).glxgearsна:10держит ~60 FPS, и сессия при этом не подвисает.--glamorнамеренно НЕ передаётся — почему, см. «Сырые места». Сейчас временно включен, так как похоже проблема устарела
Нативного UI у Terminal SWA в этом снапшоте нет (bootstrap на 94 байта), поэтому UI живёт в terminal/terminal-ui.js и инжектируется в приложение. Это не однострочное поле, а самостоятельный VT/xterm-класс эмулятор на canvas, работающий локально через chrome.terminalPrivate (без инета, без VM):
- Цвета: 16/256/truecolor SGR, bold/dim/italic/underline/inverse/strike.
- Управляющие последовательности: курсор, стирание, вставка/удаление строк и символов, scroll-регионы, alt screen (
?1049— htop/mc работают), DECCKM, автоперенос, табуляции, DEC special graphics, ответы DSR/DA, OSC-заголовки. - Скроллбэк 5000 строк (колесо мыши), широкие символы (CJK/эмодзи), комбинируемые знаки.
- Выделение мышью (клик — слово, тройной — строка), копирование, вставка, bracketed paste, mouse tracking (X10/SGR) для TUI-приложений.
- Шрифт:
Ctrl +/Ctrl -/Ctrl 0(запоминается).
Шорткаты: Ctrl+Shift+C — копировать, Ctrl+Shift+V / Shift+Insert — вставить, Ctrl+/-/0 — размер шрифта.
Работает настолько, чтобы быть полезным, но до Chromebook-grade далеко:
| Область | Состояние |
|---|---|
| Данные сети/аудио/батареи в трее | Реальные данные есть, но очень ограничены |
| Интеграция Linux-приложений в лаунчер | .desktop-файлы пишутся в настройки Crostini, но запускаться могут не все (большинство — да) |
| Эмуляция терминала | Полноценный VT/xterm-класс эмулятор на canvas (см. раздел «Терминал») |
| Аудио-узлы | Громкость работает, но узлы захардкожены (Speaker/Headphone/Mic), реального discovery PipeWire нет |
| Яркость / SUSPEND | power-bridge работает от вашего пользователя: на запись в /sys/class/backlight/*/brightness нужны права (добавьте себя в группу video), suspend зависит от polkit |
| Гостевой профиль | /home/chronos/user — обычная директория, а не cryptohome |
| Лок-экран | Настоящий, но разлочивается любым паролем (в т.ч. пустым): session-bridge отдаёт фиктивный StartAuthSessionReply с password-фактором local-password и всегда успешный AuthenticateAuthFactor. Системная соль — константа из моста. ListAuthFactors возвращает пусто, поэтому в логе безвредный warning lock_contents_view.cc:937 (поле ввода пароля показывается дефолтно) |
| Трей-иконки приложений | StatusNotifierItem не реализован |
--glamor у Xwayland (ускорение самого X-сервера) |
Намеренно выключен. Бисекция по одному флагу: без glamor превью OBS стабильно, с ним — мерцание (chrome композитит поверхность с альфой=0: xterm просвечивает насквозь и кадры накапливаются ×1→×2→×3), а железный OBS вообще не показывает картинку; патч opaque-региона в sommelier не помог, триггер лежит внутри glamor. Без glamor Xwayland работает с -shm и отдаёт непрозрачные XRGB8888-буферы — поэтому и стабильно. Теряется только ускорение внутренних 2D-операций самого X-сервера (скроллинг/копии гигантских окон); клиентский GPU-рендер (OBS, glxgears, видео) от glamor не зависит и остаётся железным |
Реализован нативный лок-экран Ash с разлочкой без пароля (вариант A). Это настоящий lock-screen ChromeOS (Ctrl+Shift+L), а не заглушка: шторка, поле ввода пароля, анимации — всё рисует сам Ash.
Как это устроено:
session-bridgeобслуживает D-Bus-интерфейсorg.chromium.UserDataAuth(CryptohomeMiscInterface) — тот же сервис/путь/интерфейс, куда Ash ходит за cryptohome.- Пустой proto-ответ = успех.
HasErrorпроверяет наличие поляerror; ответ без него Ash читает как «ошибки нет». На этом построеныAuthenticateAuthFactor,ExtendAuthSessionи прочие методы. StartAuthSessionотвечаетuser_exists=trueи одним password-фактором (type=1, labellocal-password), чтобыOnStartAuthSessionнаполнилSessionAuthFactors. Метка должна быть ровноlocal-password(kCryptohomeLocalPasswordKeyLabel), иначеFindAnyPasswordFactor()→ nullptr → «Could not find Password key» (auth_performer.cc:225) и разлочка не проходит.- Фактор кладётся в field 9 (
tag 0x4a), а не field 5:StartAuthSessionReplyсодержит два повторныхAuthFactorWithStatus, и итерирует Ash именно field 9 (у field 5 счётчик в offset0x20, у field 9 — в0x58; циклOnStartAuthSessionчитает0x58). GetSystemSaltвозвращает фиксированную 20-байтную соль (salt = field 1). Ключ хешируется (SALTED_SHA256_TOP_HALF), но проверки пароля нет: любой введённый пароль (в т.ч. пустой) разлочивает.- Страховка от «застревания» на локе:
pkill -f "dist/chrome/chrome"(делается руками — такого pkill в коде нет) убивает Chrome; он выходит с ненулевым кодом, и цикл перезапуска вsession/chromeos-linux-sessionподнимает его заново через пару секунд (start-session.shпри этом просто ждёт сессию и бриджи не трогает). Лок-экран никогда не оставит вас запертым.
ListAuthFactors возвращает пусто, поэтому при показе лок-экрана в ash.log попадает безвредный warning lock_contents_view.cc:937 Unable to update auth factors… — поле ввода пароля рисуется дефолтно, на разлочку он не влияет.
- Выбор, подключение и отключение Wi-Fi из интерфейса ChromeOS. Вообще, почти все настройки.
- SUSPEND.
- Уведомления в центре уведомлений Ash.
- Вход в аккаунт Google, синк, Phone Hub, Nearby Share и прочие сервисы, привязанные к аккаунту.
./scripts/doctor.sh # зависимости + преflight сессии: чего не хватает прямо сейчас
make verify # артефакты: библиотеки, интерпретатор, патчи, парсинг D-Bus-политики
make test # бриджи на реальной системной шине + реальные вызовы методовБлок == session preflight == в doctor.sh ловит «тихие» поломки, которые иначе выглядят как «всё собралось, но почему-то не работает»:
/dev/kvmотсутствует → Chrome при старте сессии видит, что виртуализации нет, и молча отказывается открывать любые Linux-приложения (в UI нет ошибки — просто не открывается). Заглушки-файлы создаётsetup-system.sh(tmpfiles), пересоздаются при каждой перезагрузке — но только на хостах без KVM; если KVM есть,setup-system.shзаглушку не ставит (а заодно снимает уже установленную — она перекрывает настоящий device-node, и udev не может его создать), иdoctor.shэто отдельно проверяет./usr/bin/vsh//usr/bin/croshотсутствуют → терминал падает сfailed to execvp /usr/bin/vsh. Ash вызывает их по абсолютному пути,PATHне ищет, поэтому заглушка обязана лежать именно в/usr/bin.- D-Bus-политика не установлена → бриджи не могут занять имена
org.chromium.*, и шина молча отклоняет всё. - systemd-logind неактивен → suspend/reboot из трея мертвы. Обычно за этим стоит мёртвый polkit: polkitd выходит в момент падения системной шины (теряет
org.freedesktop.PolicyKit1, аpolkit.service— static, безRestart=), после чего logind зависает, дожидаясь polkit-авторизации. Раньшеsetup-system.shпри каждом изменении политики делалrestart dbusи ронял polkit вместе с шиной — теперь он применяет политикуreload-ом (без разрыва соединений) и отдельно поднимает polkit. - logind «active», но без D-Bus-имени → Xorg не стартует:
Cannot open virtual console 1 (Permission denied). Тонкость в том, чтоis-activeтут врёт — перезапуск шины рвёт соединение logind, а юнит остаётся «active»; имяorg.freedesktop.login1logind занимает только при (пере)подключении. Xorg просит logind взять VT (TakeControl/TakeDevice), ответа нет, и он падает в ветку прямого открытия/dev/tty1, на которое у обычного пользователя прав нет. Лечение:sudo ./scripts/setup-system.sh(он сам перезапустит logind) или вручнуюsudo systemctl restart systemd-logind— после чего имя возвращается иstartxподнимается. Проверка:busctl status org.freedesktop.login1(должен быть владелец, а не(activatable)).
make test — самый полезный предварительный прогон: он поднимает каждый бридж, ждёт, пока его имя появится на шине (это возможно только при корректной политике), дёргает GetVolumeState и RetrieveSessionState, проверяет mojo-сокет и register-apps. При провале логи не удаляются, а печатаются.
| Файл | Что внутри |
|---|---|
/var/lib/ash-profile/ash.log |
stderr Chrome/Ash — первое место, куда смотреть |
/var/lib/ash-profile/sommelier.log |
sommelier и Xwayland |
/var/lib/ash-profile/*-bridge.log |
по одному на бридж |
/var/lib/ash-profile/bridges.log |
записи watchdog-ов о перезапусках |
/var/lib/ash-profile/xorg.log |
свой Xorg, если поднимал скрипт |
/var/lib/ash-profile/test-user/log/ash |
собственный сессионный лог Chrome |
/tmp/xwrap.log |
случаи, когда libdisplayfix.so чинил DISPLAY |
/tmp/chromeos-linux/shell-stub.log |
вызовы заглушек vsh / crosh |
| Симптом | Причина | Действие |
|---|---|---|
Бриджи пишут not allowed to own … due to security policies |
политика не установлена или dbus её отбросил | sudo ./scripts/setup-system.sh --user "$USER", перезапуск dbus/перезагрузка; make verify ловит это заранее |
no DISPLAY and no Xorg to start |
запущено не из X-сеанса и нет xserver-xorg |
запусти из терминала в X или поставь xserver-xorg |
| Linux-приложения не рисуются | нет Xwayland в $PATH |
поставь пакет xwayland |
| Chrome вылетает сразу | смотри ash.log; либо снапшот не тот (только x86_64, только пин из chromeos-bin/REVISION) |
сверь пины (./chromeos-bin/fetch.sh --force перескачает и напечатает sums; make fetch аргументов не принимает) |
Pactl not found / громкость не регулируется |
нет pulseaudio-utils |
поставь пакет |
| Яркость не меняется | нет прав на sysfs | sudo usermod -aG video $USER и перелогинься |
setup-system падает: chown … : users / group 'users' does not exist |
в системе нет группы users |
sudo groupadd users (или поменяй группу в session/tmpfiles.d/chromeos-linux.conf) |
Linux-приложения не открываются, а /dev/kvm есть, но это обычный файл |
заглушка перекрыла настоящий device-node KVM (модуль уже загружен) | sudo ./scripts/setup-system.sh — снимет заглушку и дёрнет udev; doctor.sh показывает это как placeholder hides the real KVM device |
Всё умерло после правки session/dbus-policies/*.conf |
dbus < 1.15 молча отбрасывает весь файл при неизвестном атрибуте | см. врезку ниже |
Вся сессия зависла в состоянии T сразу после запуска GUI-приложения от root (synaptic через pkexec) |
в сессии нет polkit-агента: pkexec спрашивает пароль текстовым агентом на управляющем терминале, а цепочка сессии — фоновая группа tty1, и ядро шлёт SIGTSTP/SIGTTIN во всю группу | см. врезку ниже |
Врезка про D-Bus-политику. Debian/Ubuntu поставляют dbus 1.14, который не знает, например,
receive_sender_prefix. Ошибка разбора через<includedir>— это не предупреждение, а тихое удаление файла из конфигурации, после чего ни одно имяorg.chromium.*занять нельзя. Поэтому: проверяйте правки политики черезmake verify(он реально поднимаетdbus-daemonна файле) и держите в голове, что поддерживаются только классические атрибутыown,own_prefix,send_destination,send_destination_prefix,send_interface,receive_sender.
Врезка про зависание всей сессии (состояние
T). Сессия, запущенная черезstartx, наследует управляющий терминал/dev/tty1, оставаясь при этом фоновой группой tty1 (foreground-группа — у самого xinit). Читать/писать/dev/ttyиз фоновой группы нельзя: ядро шлёт SIGTTIN/SIGTSTP всей группе. Типичный триггер —pkexecбез запущенного polkit-агента: его внутренний текстовый агент лезет на терминал за паролем, и целиком встаёт всё — Chrome, бриджи, скрипты. Поэтомуsession/start-session.shтеперь делаетexec setsid --wait "$0"сразу после startx-блока: всё дерево сессии уходит в отдельную сессию без управляющего терминала, чтение/dev/ttyвозвращаетENXIO, и посторонний читатель роняет только себя.--waitтут не косметика: без него, если процесс уже лидер группы,setsidфоркается, родитель выходит, и xinit считает клиент мёртвым — с--waitродитель дожидается ребёнка и teardown startx работает как прежде. Чего это не лечит: без агентаpkexecтеперь быстро откажется с ошибкой вместо запроса пароля — агента запускай сам (polkit-gnome-authentication-agent-1,lxpolkit, что угодно своё; в скрипты он не добавлен). Восстановление после зависания (для запусков до этого фикса):kill -KILLна pkexec-цепочку (напримерpkill -x synaptic-pkexec), затемkill -CONT -- -<PGID>.
chromeos-bin/REVISION задаёт ревизию снапшота, а chromeos-bin/fetch.sh — контрольные суммы (size, md5 из метаданных GCS, sha256) для неё.
Чтобы перейти на другой снапшот:
- измени
chromeos-bin/REVISION; - выполни
./chromeos-bin/fetch.sh --force— он напечатает новые md5/sha256; - впиши их в пины в начале
fetch.sh; - прогони
make— патчи бинарника зашумят, если новый бинарник структурно отличается (это ожидаемо: смещения ищутся по символам, но проверочные байты должны совпасть).
Архив sommelier пинится отдельно в scripts/build-sommelier.sh (тоже sha256, т.к. Nix-хеш того репозитория — это NAR-хеш распакованного дерева и вне Nix непригоден).
- Поменять патч — правь
scripts/build-chrome.sh; там же хелперыsym_va,verify_and_patch,va_to_offset. Новая проверка байтов обязательна, иначе патч применится к неожиданному месту. - Добавить бридж — положи
dbus-bridges/cmd/<name>-bridge/main.go, добавь имя вBRIDGESвsession/start-session.shи блок<policy>вsession/dbus-policies/chromeos-bridges.conf, затемsudo ./scripts/setup-system.sh --user "$USER". - Оффлайн-сборка —
make vendorкладёт зависимости вdbus-bridges/vendor/, после чегоgo buildидёт без сети. - Свой профиль —
ASH_PROFILE_DIR=$HOME/.local/share/chromeos-ash ./session/start-session.sh; учти, что путь/home/chronos/user, сокет/run/mojoи D-Bus-политика всё равно системные. - Отладка терминала —
ASH_TERMINAL_UI=/путь/к/ui.jsили просто правьterminal/terminal-ui.js(он читается при каждой инжекции, то есть после перезапуска сессии).
Это игрушка для отладки, а не продукт:
- Chrome запускается с
--no-sandbox(и--disable-*-sandbox). - D-Bus-политика разрешает любому локальному пользователю занять имена
org.chromium.*— то есть любому юзеру машины доступны методы, которые Ash считает «системными». - Фолбэк
xhost +local:открывает X-дисплей для всех локальных клиентов (срабатывает только если cookie скопировать не удалось). - CDP-порт
9222открыт на localhost на всё время работы сессии. /tmp/ash-xauthсоздаётся с правами 0644.
Запускайте это в VM или на машине, которую не жалко.
