На эту статью меня натолкнула установка клиента qbittorrent, с доступом к нему через webgui. После интсталяции обнаружил что он почему-то не хочет стартовать в виде демона и поэтому встал вопрос о запуске.
Можно было конечно использовать screen или просто запустить с nohup, но я попробовал сделать с помощью systemd, т.к. решил рассмотреть поближе систему которую так и так приходится использовать каждый день и от которой все плюются :). Расмотренно будет только создание трех видов служб (services): simple, forking, oneshot (в этом обзоре только oneshot) и некоторых ключевых опций, короче только самое простое или необходимое. Cтатья не претендует на ховту или что-то в этом роде, т.к. сам простой любитель экспериментировать и линуха. Кто сочтет все написанное бредом не обижусь :). Тестировал на своей fedora 23, на этот момент версия systemd у меня:
$ systemctl --version systemd 222
Сначала основы, некоторые замечания или то, что многие у кого система c systemd и так знают.
В systemd модули (units) являются по сути просто конфигурационными файлами, аля ini-файл. Их месторасположение с точки зрения увеличения приоритета:
- $ pkg-config systemd --variable=systemdsystemunitdir # директория системных юнитов
- /run/systemd/ для временных, автоматически генерируетмых юнитов. Полезно иногда подсмотреть различные опции. Например в /run/systemd/generator/ лежат сгенерированные из fstab mount-юниты.
- $ pkg-config systemd --variable=systemdsystemconfdir # кастомные юниты и ссылки на системные, где мы расположим и наши
После создания юнита необходимо чтобы systemd перечитала его конфигурацию - если мы хотим немедленно (до перезагрузки) запустить службу, например для тестирования и активировать для последующего автостарта.
$ sudo systemctl daemon-reload $ sudo systemctl start [служба] $ sudo systemctl enable [служба]
Для контроля за статусом службы можно использовать например:
$ systemctl -l status [служба] # говорит само за себя, -l чтобы не обрезались сообщения $ journalctl -b -u [служба] # посмотреть журнал systemd c последней загрузки и сообщения только для данной службы
Конечно завистит от того, куда будет направлен стандартный вывод. По умолчанию в осях с systemd это journal
----------------- cut---------------
Юнит службы состоит из 3 секций которые имеют огромное колличество параметров (по англицки properties - свойства ): [Unit] - описание, порядок запуска, условия и зависимости службы, [Service] - тип службы, команда для запуска бинарника или скрипта, управление поведением службы и выводом информации, [Install] - свойства установки службы для автостарта
Тип службы oneshot.
При этом типе systemd ожидает что процесс будет завершен перед тем как systemd запустит следующие юниты. Таким образом можно выполнить определенные конечные действия, например: создать директорию, изменить права на файл, добавить правила в iptables, сделать backup и т.д. Напишем нашу первую службу с минимальным набором свойств: описанием (параметр Description), типом (параметр Type) oneshot, запуском комманды echo (параметр ExecStart). Этого достаточно, но так как все процессы у службы типа oneshot будут завершены, systemd будет думать что служба загружена, но неактивна. Так что если не посмотреть специально статус, можно подумать что она завершилась с ошибками. Поэтому напоминаем ей что мы живы (параметр RemainAfterExit).
$ sudo nano /etc/systemd/system/test.service [Unit] Description=test service [Service] Type=oneshot ExecStart=/usr/bin/echo "Привет мир" RemainAfterExit=yes
Для команды "echo" прописываем обязательно полный путь, почему - объясню ниже. Сохраняем, перечитывает конфигурацию, запускаем. Небольшое замечание на будущее: так как служба будет всегда считаться активной, делаем всегда рестарт, на всякий случай.
$ su -c "systemctl daemon-reload && systemctl restart test.service"
Смотрим статус, видим что наша служба и запущена и активна.
$ systemctl -l status test.service test.service - test service Loaded: loaded (/etc/systemd/system/test.service; static; vendor preset: disabled) Active: active (exited) since Mo 2016-01-11 12:48:54 CET; 3s ago Process: 6020 ExecStart=/usr/bin/echo Привет мир (code=exited, status=0/SUCCESS) Main PID: 6020 (code=exited, status=0/SUCCESS)
Что же произойдет если мы запустим команду, которая длится очень долго или как в случае qbittorent будет писать что-нибудь постоянно. Посмотрим это на примере. Кстати одна особенность oneshot службы - в отличии от других типов, опций ExecStart тут может быть несколько.
$ sudo nano /etc/systemd/system/longtime.service [Unit] Description=longtime service [Service] Type=oneshot ExecStart=/usr/bin/echo "Привет" ExecStart=/usr/bin/sleep 600 ExecStart=/usr/bin/echo "Пока" RemainAfterExit=yes
Сохраняем, перечитывает конфигурацию, запускаем longtime.service. Консоль висит, ожидая завершения процесса. Стартуем другой терминал, смотрим статус: activating - ожидает активации, другими словами - когда процесс завершиться. В ранних версиях systemd существовал по умолчанию таймаут при старте юнита, потом его убрали. Я видел в багтреке федоры поток негативных отзывов - действительно кто знает сколько будет длиться например полный бэкап? Откроем другой терминал или просто грубо прервем процесс Ctrl+C (это не остановит активацию службы, заключается в использовании cgroup, но об этом потом). И остановим службу.
$ sudo systemctl stop longtime.service
Я упомянул о значениях по умолчанию - действительно даже такой примитивный юнит как наш, имеет оказывается много других свойств, потому как собран из дефолтных конфигов которые являются шаблонами. Посмотреть все текущие (т.е. с уже измененными нами) значения можно командой
$ systemctl show longtime.service #Если интересует определенное свойсво, то так $ systemctl show --property=TimeoutStartUSec longtime.service
для меня непонятно почему свойство таймаута старта называется тут TimeoutStartUSec, а в юните должно называться TimeoutStartSec. Конечно мелочь, но из такого и складывается негативное впечатление :(. У вас может быть уже значение больше нуля, у меня 0, что означает ждать до бесконечности. Добавим таймаут в наш скрипт со значение 5 сек.:
[Unit] Description=longtime service [Service] Type=oneshot ExecStart=/usr/bin/echo "Привет" ExecStart=/usr/bin/sleep 60 ExecStart=/usr/bin/echo "Пока" RemainAfterExit=yes TimeoutStartSec=5
Сохраняем, перечитывает конфигурацию, запускаем, ждем. Через 5 сек. служба безвременно скончается, о чем systemd нам в панике и сообщит.
Теперь важное замечание - вы заметили что мы прописывали полные пути к командам. Действительно systemd не шелл и как, например, у crona при запуске службы отсутствуют переменные окружения. Точнее очень ограничены, поэтому в запускаемых скриптах пишем полные пути и экспортируем нужные переменные. Однако нам доступны некоторые специальные переменные - спецификаторы (man SYSTEMD.UNIT(5) раздел SPECIFIERS), о которых поговорим позже, а также мы можем добавить нужные переменные с помощью опций Environment и EnvironmentFile - путь к файлу в котором с новой строки определяется очередная переменная. Давайте напишем еще короткий юнит: добавим переменную TEXT и напишем полноценный шелл скрипт, где будем использовать ее и спецификатор %N, содержащий короткое имя юнита, который передадим ему в качестве аргумента. Для наглядности я использовал zenity, если у кого нет и не хочет устанавливать можно просто выдать echo. Скрипт в тестовых целях я просто поместил в директорию пользователя:
$ nano /home/testuser/output.sh #!/bin/bash export DISPLAY=:0.0 zenity --info --title "$1" --text "$TEXT" exit 0
сохраняем, выходим, даем права на выполнение
$ sudo chmod +x /home/testuser/output.sh
создадим службу имени Дарта Вейдера:
$ sudo nano /etc/systemd/system/DarthVader.service [Unit] Description=Дарт Вейдер [Service] Type=oneshot ExecStart=/home/testuser/output.sh %N Environment="TEXT=Люк, я твой отец." RemainAfterExit=yes
Сохраняем, делаем reload, запускаем DarthVader.service. Кто делал c zenity увидят диалог, в качестве титула имя службы, переменная TEXT тоже благополучно нашлась.
Пару слов о зависимостях: Они указываются в разделе [Unit]. Requires - указанный здесь юнит будет запущен до старта нашей службы. Вслучае неудачи и наша служба не запустится. Wants - наша служба желает чтобы эти юниты были запущены и ей не важен результат. Давайте создадим юнит Skywalker и поставим его папашу в зависимость Requires:
$ sudo nano /etc/systemd/system/Skywalker.service [Unit] Description=Скайуокер Requires=DarthVader.service [Service] Type=oneshot ExecStart=/home/testuser/output.sh %N Environment="TEXT=Охренеть!." RemainAfterExit=yes
Перед запуском Скайуокера остановим дарта вейдера (если он был запущен), потому как если зависимость успешно запущена, она больше не будет стартовать заново, это логично.
$ sudo systemctl stop DarthVader.service $ su -c "systemctl daemon-reload && systemctl restart Skywalker.service"
Мы увидим два диалоговых окна (скорее всего наложенных друг на друга), зависимость стартовала успешно.

http://liberatum.ru/blog/26717