Написание сервиса, который зависит от 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;

  1. Создайте нужный файл *.service в ~/.config/systemd/user/
  2. Бежать systemctl --user enable [service] (исключить суффикс.service)
  3. При желании запустить systemctl --user start [service] начать сейчас
  4. использование systemctl --user status [service] проверить как дела

Пара других полезных команд.

  • systemctl --user list-unit-files - просмотреть все пользовательские блоки
  • systemctl --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):

  1. Создайте псевдосистемный пользовательский модуль, который поднимает и опускает графическую цель.

Создайте ~/.config/systemd/user/xsession.target со следующим содержанием:

[Единица измерения]
Описание = Xsession запущен и работает
BindsTo = graphical-session.target

Расскажите systemd об этом новом модуле:

$> systemctl --user daemon-reload
  1. Создайте сценарии автозапуска и выключения, которые управляют 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).

  1. Создайте свои собственные системные модули пользователя, которые зависят от 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 после редактирования ваших юнитов!)

  1. Перезагрузите компьютер, войдите в систему и убедитесь, что ваши устройства запущены, как и ожидалось
$> 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

См. также: https://www.freedesktop.org/software/systemd/man/systemd.special.html#Special%20Passive%20User%20Units .

В «/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"
Другие вопросы по тегам