Написание сервиса, который зависит от Xorg
Я пытаюсь написать сервис уровня пользователя для redshift
и нужно подождать, пока Xorg не заработает. Мой текущий служебный файл выглядит так:
[Unit]
Description=Redshift
After=graphical.target
[Service]
Environment=DISPLAY=:0
ExecStart=/bin/redshift -l 28:-13 -t 5300:3300 -b 0.80:0.91 -m randr
Restart=always
[Install]
WantedBy=default.target
Однако, похоже, что он пытается запуститься до того, как Xorg будет запущен, и я должен вручную запустить службу после этого. Я думаю, что я использую неправильно After=
цель. Есть намеки?
6 ответов
Я исследовал это, и ответ Гравити кажется устаревшим. Теперь вы можете настроить пользовательские сервисы с помощью systemd, которые выполняются как часть сеанса пользователя. Для них могут быть установлены DISPLAY и XAUTHORITY (в настоящее время это Arch и Debian Stretch).
Это имеет смысл по сравнению с предыдущими рекомендациями по использованию файлов автозапуска на рабочем столе, поскольку вы получаете управление процессами так же, как и приложение системного уровня (перезагрузка и т. Д.).
Лучшие документы прямо сейчас - это Arch wiki; Systemd / Пользователь
Версия TLDR;
- Создайте нужный файл *.service в
~/.config/systemd/user/
- Бежать
systemctl --user enable [service]
(исключить суффикс.service) - При желании запустить
systemctl --user start [service]
начать сейчас - использование
systemctl --user status [service]
проверить как дела
Пара других полезных команд.
systemctl --user list-unit-files
- просмотреть все пользовательские блоки- s
ystemctl --user daemon-reload
- если вы редактируете файл.service
-- Потом...
Я обновил и преобразовал большинство моих сессионных демонов в файлы systemd.service. Так что я могу добавить пару дополнительных заметок.
По умолчанию не было подключений для запуска служб при входе в систему, поэтому вы должны запустить его самостоятельно. Я делаю это из моего файла ~/.xsession.
systemctl --user import-environment PATH DBUS_SESSION_BUS_ADDRESS
systemctl --no-block --user start xsession.target
Первая строка импортирует некоторые переменные окружения в сеанс пользователя systemd, а вторая запускает цель. Мой файл xsession.target;
[Unit]
Description=Xsession running
BindsTo=graphical-session.target
Мой xbindkeys.service в качестве примера.
[Unit]
Description=xbindkeys
PartOf=graphical-session.target
[Service]
ExecStart=/usr/bin/xbindkeys -n -f ${HOME}/projects/dotfiles/.xbindkeysrc
Restart=always
[Install]
WantedBy=xsession.target
Обычный намек - "не надо". redshift
это не общесистемная служба - у нее будет отдельный экземпляр для каждого сеанса, и ему нужно знать, как подключиться к Xorg этого конкретного сеанса.
(Xorg также не является системной службой - только диспетчер отображения, и он также запускает отдельный Xorg для каждого сеанса. // graphical.target
сообщит вам, когда менеджер дисплеев будет готов, но ничего не говорит о том, когда DM фактически запускает первый или все дисплеи.)
Просто запускаю его при загрузке с DISPLAY=:0
недостаточно, потому что нет гарантии, что в каждый момент времени есть только один дисплей, или что он всегда :0
(например, если Xorg падает, оставляя устаревший файл блокировки, следующий будет работать в :1
как бы подумать :0
все еще занят); вам также нужно указать путь к вашему XAUTHORITY
файл как X11 требует аутентификации; и убедитесь, redshift
перезапускается, если вы когда-либо выходите из системы и снова входите в систему.
Так с чего начать? Почти всегда в среде рабочего стола есть несколько способов запуска собственных сервисов сессий. Смотрите более старый пост, который уже описывает два обычных; ~/.xprofile
сценарий и ~/.config/autostart/*.desktop
место нахождения.
Если вы используете startx, вы можете использовать ~/.xinitrc
начать такие вещи. Автономные оконные менеджеры часто имеют свои собственные сценарии запуска / инициализации; например ~/.config/openbox/autostart
для Openbox.
Общим для всех этих методов является то, что программа запускается изнутри сессии, избегая всех проблем, перечисленных выше.
Вот то, что я только что создал как обходной путь для еще не доступных graphical-session.target
(На моей системе Kubuntu 16.04):
- Создайте псевдосистемный пользовательский модуль, который поднимает и опускает графическую цель.
Создайте ~/.config/systemd/user/xsession.target
со следующим содержанием:
[Единица измерения] Описание = Xsession запущен и работает BindsTo = graphical-session.target
Расскажите systemd об этом новом модуле:
$> systemctl --user daemon-reload
- Создайте сценарии автозапуска и выключения, которые управляют
xsession.target
через имеющуюся на данный момент механику рабочего стола Ubuntu 16.04.
Создайте ~/.config/autostart-scripts/xsession.target-login.sh
со следующим содержанием:
#! / Bin / Баш если ! systemctl --user is-active xsession.target &> / dev / null затем / bin / systemctl - пользовательская среда импорта DISPLAY XAUTHORITY / bin / systemctl --user start xsession.target фи
Создайте ~/.config/plasma-workspace/shutdown/xsession.target-logout.sh
со следующим содержанием:
#! / Bin / Баш если systemctl --user is-active xsession.target &> / dev / null затем / bin / systemctl --user stop xsession.target фи
Сделайте скрипты исполняемыми:
$> chmod + x ~ / .config / autostart-scripts / xsession.target-login.sh $> chmod + x ~ / .config / plasma-workspace / shutdown / xsession.target-logout.sh
Примечание: эти два файла размещены там, где KDE подберет их для автоматического запуска и завершения работы. Файлы могут быть размещены где-то еще для других сред рабочего стола (например, Gnome) - но я не знаю об этих средах.
Примечание. В этом обходном пути отсутствует поддержка нескольких рабочих столов. Это только обрабатывает graphical-session.target
правильно, если на компьютере запущен только один активный сеанс X11 (но это так для большинства пользователей Linux).
- Создайте свои собственные системные модули пользователя, которые зависят от
graphical-session.target
и пусть они будут работать без ошибок во время входа в систему на рабочем столе.
Например, модуль @ mkaito должен выглядеть так:
[Единица измерения] Описание = Redshift PartOf = graphical-session.target [Обслуживание] ExecStart = / bin / redshift -l 28: -13 -t 5300: 3300 -b 0,80: 0,91 -m randr Restart = всегда
(Не забудьте сделать daemon-reload
после редактирования ваших юнитов!)
- Перезагрузите компьютер, войдите в систему и убедитесь, что ваши устройства запущены, как и ожидалось
$> systemctl - пользовательский статус graphical-session.target ● graphical-session.target - текущий графический пользовательский сеанс Загружен: загружен (/usr/lib/systemd/user/graphical-session.target; статический; предустановка поставщика: включена) Активен: активен с Дона 2017-01-05 15:08:42 CET; 47 минут назад Документы: man: systemd.special (7) $> systemctl - статус пользователя your-unit ...
В какой-то будущий день (это будет Ubuntu 17.04?) Мой обходной путь устареет, так как система будет обрабатывать graphical-session.target
правильно сама. В этот день просто удалите скрипт автозапуска и выключения, а также xsession.target
- Ваши пользовательские пользовательские блоки могут оставаться нетронутыми и просто работать.
Это решение делает именно то, что задает автор вопроса:
нужно подождать, пока Xorg не заработает
Хотя могут быть более эффективные способы сделать это, как уже отвечали другие пользователи, это еще один подход к этой проблеме.
Это похоже на systemd -networkd-wait-online.service systemd, который блокирует, пока не будут выполнены определенные критерии. Другие сервисы, которые зависят от него, будут запущены, как только этот сервис запустится успешно или истечет время ожидания.
Согласно инструкции (раздел "Файлы"), X-сервер создаст сокет UNIX. /tmp/.X11-unix/Xn
(где n
это номер дисплея).
Контролируя наличие этого сокета, мы можем определить, что сервер для определенного дисплея запущен.
confirm_x_started.sh
:
#!/bin/bash
COUNTER=0
while [ 1 ]
do
# Check whether or not socket exists
if [ -S /tmp/.X11-unix/X0 ]
then
exit 0
fi
((++COUNTER))
if [ $COUNTER -gt 20 ]
then
exit 1
fi
sleep 0.5
done
x_server_started.service
:
[Unit]
Description=Monitor X server start
[Service]
Type=oneshot
ExecStart=/path/to/confirm_x_started.sh
[Install]
WantedBy=example.target
Теперь включите x_server_started.service
начать одновременно с X-сервером.
Сделайте так, чтобы другие службы (для которых требуется запуск X-сервера) зависели от x_server_started.service
зависимая единица:
[Unit]
Description=Service that needs to have the X server started
Requires=x_server_started.service
After=x_server_started.service
[Service]
ExecStart=/path/to/binary
[Install]
WantedBy=example.target
Если X-сервер запускается без проблем, x_server_started.service
начнется почти сразу, и systemd продолжит запускать все модули, которые зависят от x_server_started.service
,
Многие ответы подразумевают, что необходимы какие-то обходные пути или сценарии. По крайней мере, в недавнем сеансе GNOME мне больше не нужны такие обходные пути.
Если я добавлюWantedBy=graphical-session.target
затем необходимые переменные среды X11 (т. е.DISPLAY
иXAUTHORITY
) доступны службе, и я могу выполнить программу X11 в своем файле пользовательской службы:
[Unit]
...
PartOf=graphical-session.target
[Service]
...
[Install]
WantedBy=graphical-session.target
Насколько я понимаю,graphical-session.target
является пассивной целью. Его использование подразумевает, что фактический сеанс, который запускается, привязывается к этой цели черезBindsTo=graphical-session.target
. Дляgnome-session.target
Кажется, это так, но это может быть не так для старых или менее популярных сред рабочего стола.
Мне было полезно просмотреть журнал пользователяjournalctl --user -b
который печатает, какие цели в каком порядке достигнуты:
08 мая, 21:28:10 Earth systemd[2481]: достигнута цель уведомлений принтера GNOME. 08 мая, 21:28:10 Earth systemd[2481]: достигнут целевой сеанс GNOME. 08 мая, 21:28:10 Earth systemd[2481]: достигнут целевой сеанс GNOME X11 (сеанс: gnome-flashback-metacity). 08 мая 21:28:10 земля systemd[2481]: достигнута цель Текущий графический сеанс пользователя.
В журнал выводится описание цели, и мы можем использовать следующую команду для определения фактических имен целей:
systemctl --user list-units --type target
В «/usr/lib/systemd/user » разместите службу , которая включает в себя:
[Service]
Environment=DISPLAY=:0
Environment=XAUTHORITY=/home/<user>/.Xauthority
Если это само по себе не работает, создайте сценарий , который ожидает запуска оконного менеджера:
waitSuccess () {
local command="${*}"
while ! ${command} &> /dev/null; do
sleep 0.5
done
}
waitSuccess "xprop -root -notype _NET_SUPPORTING_WM_CHECK | grep --silent true"