RFC HOWTO: автологин в иксовую сессию с помощью systemd
Добрый вечер, господа. Это тред-howto о том, как сделать корректный автологин в иксы «на чистом systemd». В вики мне писать влом, да и никто её не читает, а тут и теги указать можно, и людей скастовать. Собственно, да: border-radius, ecko.
В чём вообще проблема? Проблема в том, что значительное количество людей делают это через банальнейшие костыли, настраивая автологин в текстовую консоль и запуская иксы из bashrc (или, чего хуже, из bash_profile). Это плохо по трём причинам:
- не залогиниться в другую физическую консоль в текстовом режиме
- оверхед на проделывание цепочки такого вида:
systemd /bin/agetty /bin/login PAM /bin/bash ~/.bashrc /bin/startx
- в конце концов, это само по себе костыль.
Я предлагаю написать getty-подобный юнит, который будет запускать иксы от фиксированного пользователя с фиксированным номером дисплея на произвольном VT. (Почему так много хардкода? Потому что systemd — не дисплейный менеджер.)
Это тоже неидеальное решение. Например, нафиг идёт мультисит и возникают гонки между запуском иксов и обнаружением видеоустройств. Но этими недостатками мы пренебрежём.
Параграф один. logind, autovt и getty-подобные юниты. Getty могут запускаться двумя способами.
- Первый — по требованию, через logind. При переключении на ttyN logind запускает юнит
autovt@ttyN.service
, который засимлинкен наgetty@.service
. Эта логика работает для tty2-tty6. - Второй — статически. Юнит
getty@tty1.service
включен по умолчанию и втягивается черезgetty.target
. Это даёт нам всегда запущенный getty на tty1.
Соответственно, допустим, у нас есть юнит xorg@.service, который запускает иксы на указанном VT.
Его нужно либо симлинкнуть под именем autovt@ttyN.service
, переопределив шаблонный юнит (тогда при переключении на выбранный VT иксы будут запускаться вместо getty — первый способ), либо отключить getty@tty1.service
и включить вместо него xorg@tty1.service
(тогда мы вместо всегда запущенного getty будем иметь всегда запущенные иксы — второй способ).
Параграф два. Xorg вместо getty. Итак, имеем юнит для иксов, написанный по аналогии с getty@.service: /etc/systemd/system/xorg@.service.
User=<впишитеюзера>
PAMName=login
-- это аналог su.
Conflicts=getty@%i.service
After=getty@%i.service
-- это некоторая защита от одновременного запуска getty на том же терминале.
StandardOutput=tty
StandardInput=tty-fail
-- это указание systemd запускать иксы подсоединёнными напрямую к терминалу, а не к логгеру (нужно для того, чтобы иксы можно было запускать не от рута... ах да, работает только с 1.16 и выше).
ExecStart=/etc/systemd/scripts/startx -D :0
-- это мой велосипед вместо startx с нескучным синтаксисом и exec xinit
в конце, что важнее.
Дело в том, что systemd из-за вероятного бага при остановке юнита отправляет SIGTERM/SIGKILL не всем процессам в дереве, начиная с startx, а только самому startx. А поскольку он написан на шелле, то он радостно игнорирует SIGTERM и ждёт завершения xinit, которому никакого сигнала не приходит. Следовательно, проблему решаем переписыванием startx так, чтобы он в конце не запускал xinit подпроцессом, а делал exec xinit
, заменяя им собственный процесс. Тогда сигнал приходит xinit'у, а он его корректно ловит и убивает иксы.
Всё остальное скопипащено из getty@.service.
Да, дисплей захардкожен в :0. Пара слов о назначении VT: процесс startx получает номер VT в переменной $XDG_VTNR
(её устанавливает pam_systemd), а из startx запускается /etc/X11/xinit/xserverrc
, который об этой переменной знает и передаёт X-серверу параметр vt$XDG_VTNR
.
Параграф три. Запускаем.
Итак, помещаем юнит в /etc/systemd/system/xorg@.service
, startx в /etc/systemd/scripts/startx
(можно куда угодно) и делаем:
systemctl daemon-reload
systemctl disable getty@tty1
systemctl enable xorg@tty1
После этого можно ребутиться и надеяться, что запустится. Ах да, дисплейный менеджер тоже стоит отключить, потому что он запустит свой X-сервер и произойдёт адъ и израиль.
Как-то так. Сейчас три часа семнадцать минут по московскому времени, поэтому прошу меня извинить за упрт стиль изложения, краткость, неконсистентное использование форматирования и так далее.