19

Я пытаюсь написать сервис уровня пользователя для 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= target. Есть намеки?

4 ответа4

11

Обычный намек - «не надо». 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.

Общим для всех этих методов является то, что программа запускается изнутри сессии, избегая всех проблем, перечисленных выше.

10

Я исследовал это, и ответ Гравити кажется устаревшим. Теперь вы можете настроить пользовательские сервисы с помощью 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 - просмотреть все пользовательские модули
  • 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
4

Вот то , что я только что создал , как обходной путь к еще не доступно graphical-session.target (На моей системе Kubuntu 16.04):

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

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

[Unit]
Description = Xsession up and running
BindsTo=graphical-session.target

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

$> systemctl --user daemon-reload
  1. Создайте сценарии автозапуска и завершения работы, которые управляют xsession.target помощью доступной в настоящее время механики рабочего стола Ubuntu 16.04.

Создайте ~/.config/autostart-scripts/xsession.target-login.sh со следующим содержимым:

#!/bin/bash

if ! systemctl --user is-active xsession.target &> /dev/null
then
  /bin/systemctl --user import-environment DISPLAY XAUTHORITY
  /bin/systemctl --user start xsession.target
fi

Создайте ~/.config/plasma-workspace/shutdown/xsession.target-logout.sh со следующим содержимым:

#!/bin/bash

if systemctl --user is-active xsession.target &> /dev/null
then
  /bin/systemctl --user stop xsession.target
fi

Сделайте скрипты исполняемыми:

$> chmod +x ~/.config/autostart-scripts/xsession.target-login.sh
$> chmod +x ~/.config/plasma-workspace/shutdown/xsession.target-logout.sh

Примечание: эти два файла размещены там, где KDE подберет их для автоматического запуска и завершения работы. Файлы могут быть размещены где-то еще для других сред рабочего стола (например, Gnome), но я не знаю об этих средах.

Примечание. В этом обходном пути отсутствует поддержка нескольких рабочих столов. Он корректно обрабатывает graphical-session.target сессию.target только до тех пор, пока на машине запущен только один активный сеанс X11 (но это относится к большинству из нас, пользователей Linux).

  1. Создайте свои собственные пользовательские модули systemd, которые зависят от graphical-session.target и пусть они будут работать корректно при входе в систему на рабочем столе.

В качестве примера модуль @ mkaito должен выглядеть так:

[Unit]
Description=Redshift
PartOf=graphical-session.target

[Service]
ExecStart=/bin/redshift -l 28:-13 -t 5300:3300 -b 0.80:0.91 -m randr
Restart=always

(Не забудьте daemon-reload после редактирования ваших юнитов!)

  1. Перезагрузите компьютер, войдите в систему и убедитесь, что ваши устройства запущены, как и ожидалось
$> systemctl --user status graphical-session.target
● graphical-session.target - Current graphical user session
   Loaded: loaded (/usr/lib/systemd/user/graphical-session.target; static; vendor preset: enabled)
   Active: active since Don 2017-01-05 15:08:42 CET; 47min ago
     Docs: man:systemd.special(7)
$> systemctl --user status your-unit...

В какой-то будущий день (это будет Ubuntu 17.04?) мой обходной путь устарел, так как система сама будет корректно обрабатывать graphical-session.target сессию. В этот день просто удалите сценарий автозапуска и завершения работы, а также xsession.target - ваши пользовательские пользовательские блоки могут остаться нетронутыми и просто работать.

1

Это решение делает именно то, что задает автор вопроса:

нужно подождать, пока 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 .

Всё ещё ищете ответ? Посмотрите другие вопросы с метками .