# Systemd service

В этой статье мы подробнее посмотрим на юниты **SystemD** с типом **Service**. Разберём параметры из юнита **ssh.service** и не только.

## <span id="bkmrk-%D0%AE%D0%BD%D0%B8%D1%82%D1%8B-systemd-%D1%82%D0%B8%D0%BF%D0%B0-s-1">Юниты SystemD типа service</span>

В **SystemD** все сервисы которые можно запускать или останавливать описываются в специальных юнитах **service**. Это значит, что в таких юнитах описывается вся информация, которая поможет запустить сервис (службу).

Наверно юниты SystemD следовало изучать после изучения процессов. И скорее всего в будущем я поменяю очерёдность в оглавлении. Но сейчас я постарался описать службы (юниты типа service) с минимальным объяснением процессов.

Как вам известно, юниты — это обычные файлы. В юнитах типа **service** описывается способ запуска исполняемых файлов (бинарных или скриптов).

В **SystemD** есть специальная утилита, которая позволяет управлять службами — **systemctl**. Вот некоторые её подкоманды:

- **systemctl start &lt;unit.service&gt;** — запуск службы. При этом происходит какая-то подготовка и запуск одного или нескольких исполняемых файлов. А когда запускаются исполняемые файлы, то в системе стартуют соответствующие процессы. Из этого следует, что после запуска службы в системе запустится один или несколько процессов, которые будут работать в одной службе.
- **systemctl stop &lt;unit.service&gt;** — остановка службы. Эта команда должна остановить службу. При этом должны остановиться все связанные со службой процессы. Хотя процессы не обязательно должны завершиться, это зависит от того, что написано в юните.
- **systemctl status &lt;unit.service&gt;** — статус службы. С помощью этой команды можно узнать статус службы. А именно запущена ли она или нет, какие в неё работают процессы и какой из них главный процесс. А также можно посмотреть последние логи службы.
- **systemctl reload &lt;unit.service&gt;** — перечитывание конфигурации. Эта команда заставит работающие процессы перечитать свои конфиги. Хотя на самом деле, она выполнит то, что написано в юните.
- **systemctl restart &lt;unit.service&gt;** — перезапуск службы. То есть остановка и последующее включение службы.
- **systemctl enable &lt;unit.service&gt;** — включение автозапуска. Эта команда включает автозапуск для службы, а вот когда будет срабатывать автозапуск, зависит от написанного в юните.
- **systemctl disable &lt;unit.service&gt;** — выключение автозапуска. А эта команда выключает автозапуск у службы.
- **systemctl cat &lt;unit.service&gt;** — вывод содержимого файла юнита. То есть делает тоже самое, что и команда **cat /путь/unit.service**.

Мне кажется лучше всего изучать написание юнитов с помощью просмотра уже подготовленных юнитов. Давайте посмотрим на один такой юнит — **ssh.service**.

## <span id="bkmrk-%D0%AE%D0%BD%D0%B8%D1%82-systemd-%E2%80%94-ssh.s-1">Юнит SystemD — ssh.service</span>

### <span id="bkmrk-%D0%A1%D1%82%D0%B0%D1%82%D1%83%D1%81-%D1%81%D0%BB%D1%83%D0%B6%D0%B1%D1%8B-1">Статус службы</span>

Для начала с помощью команды **systemctl status ssh** посмотрим статус этой службы:

```bash
alex@deb:~$ sudo systemctl status ssh
● ssh.service - OpenBSD Secure Shell server
Loaded: loaded (/lib/systemd/system/ssh.service; enabled; vendor preset: enabled)
Active: active (running) since Thu 2022-07-21 13:59:59 MSK; 5min ago
Docs: man:sshd(8)
man:sshd_config(5)
Process: 10614 ExecStartPre=/usr/sbin/sshd -t (code=exited, status=0/SUCCESS)
Main PID: 10615 (sshd)
Tasks: 1 (limit: 2340)
Memory: 1.1M
CPU: 76ms
CGroup: /system.slice/ssh.service
└─10615 sshd: /usr/sbin/sshd -D [listener] 0 of 10-100 startups
```

<div class="enlighter-default enlighter-v-standard enlighter-t-atomic enlighter-l-generic enlighter-hover " id="bkmrk-"><div class="enlighter-code"><div class="enlighter"><div class=""><div><span class="enlighter-text">  
</span></div></div></div></div></div>В этом выводе можно увидеть файл юнита — **/lib/systemd/system/ssh.service**. Также видно что включена автозагрузка службы — **enabled**. И фраза **vendor preset: enabled** — означает что служба поставляется со включенной автозагрузкой. Другими словами, при установке пакета ssh, сразу включится автозапуск этой службы.

### <span id="bkmrk-%D0%A1%D0%BE%D0%B4%D0%B5%D1%80%D0%B6%D0%B8%D0%BC%D0%BE%D0%B5-%D1%8E%D0%BD%D0%B8%D1%82%D0%B0-1">Содержимое юнита</span>

Давайте теперь посмотрим на содержимое юнита. Это можно сделать с помощью двух разных команд:

```bash
alex@s-deb:~$ cat /lib/systemd/system/ssh.service
*** Этот вывод я пропущу, так как он аналогичен выводу следующей команды ***


alex@s-deb:~$ systemctl cat ssh.service
# /lib/systemd/system/ssh.service
[Unit]
Description=OpenBSD Secure Shell server
Documentation=man:sshd(8) man:sshd_config(5)
After=network.target auditd.service
ConditionPathExists=!/etc/ssh/sshd_not_to_be_run


[Service]
EnvironmentFile=-/etc/default/ssh
ExecStartPre=/usr/sbin/sshd -t
ExecStart=/usr/sbin/sshd -D $SSHD_OPTS
ExecReload=/usr/sbin/sshd -t
ExecReload=/bin/kill -HUP $MAINPID
KillMode=process
Restart=on-failure
RestartPreventExitStatus=255
Type=notify
RuntimeDirectory=sshd
RuntimeDirectoryMode=0755


[Install]
WantedBy=multi-user.target
Alias=sshd.service
```

Здесь мы видим 3 блока:

- **\[Unit\]** — здесь можно описать юнит, и указать ограничения на его запуск;
- **\[Service\]** — сюда добавляется информация, которая используется для запуска, работы и остановки службы;
- **\[Install\]** — в этом блоке описываются дополнительные условия для запуска или автозапуска службы.

#### <span id="bkmrk-%D0%91%D0%BB%D0%BE%D0%BA-%5Bunit%5D-1">Блок \[Unit\]</span>

Параметры:

- **Description** — описание юнита;
- **Documentation** — источники документации;
- **After** — запускать юнит после запуска перечисленных юнитов, при этом не требуется чтобы эти юниты были запущены, этот параметр влияет только на очерёдность;
- **Requires** — в нашем примере нет этой опции, но про неё следует знать. Эта опция требует чтобы перечисленные юниты работали. Если они не будут работать, то и наш юнит не запустится;
- **ConditionPathExists** — эта опция проверяет наличия указанного файла. Восклицательный знак перед именем файла означает, что юнит запустится только в том случае, если этого файла в системе нет. Получается чтобы запретить запуск службы **ssh** достаточно создать файл **/etc/ssh/sshd\_not\_to\_be\_run**.

#### <span id="bkmrk-%D0%91%D0%BB%D0%BE%D0%BA-%5Bservice%5D-1">Блок \[Service\]</span>

Параметры:

- **EnvironmentFile** — переменные окружения для службы и её процессов будут браться из указанного файла. Минус перед файлом (как в нашем случае **-/etc/default/ssh**) означает что файл может отсутствовать и это не приведёт к ошибке при запуске службы. Это можно использовать как файл с дополнительными настройками.
- **ExecStartPre** — здесь указывается дополнительная команда, которая выполняется перед запуском службы. Это можно использовать, например для подготовки окружения перед запуском.
- **ExecStart** — команда, которая запускает службу. Именно в этом параметре нужно указать главный исполняемый файл (утилиту или скрипт), ради которого мы создаём службу.
- **ExecReload** — здесь нужно указать которая выполнится при выполнении **systemctl reload &lt;unit.service&gt;**. А если утилита не умеет перечитывать свою конфигурацию, без перезагрузки, то можно опустить этот параметр. Параметров **ExecStartPre**, **ExecStart**, **ExecReload** может быть несколько, если нужно выполнить несколько команд.
- **KillMode** — указывает, как процессы службы должны завершаться. Существует несколько вариантов: 
    - **control-group** — сигнал остановки будет послан всем процессам службы;
    - **mixed** — первый сигнал остановки будет послан главному процессу, а второй всем остальным процессам;
    - **process** — сигнал остановки будет послан только главному процессу, все остальные процессы (если они есть) останутся работать;
    - **none** — никому не будет отправлен сигнал остановки, служба будет выключена, но все процессы продолжат работать (настоятельно не рекомендуется);
- **Restart** — в этом параметре можно указать, в каких случаях нужно перезагружать службу: 
    - **on-failure** — при ошибке;
    - **on-success** — при успехе (при корректном завершении службы);
    - **no** — никогда (по умолчанию);
    - **always** — всегда, то есть если ваша служба по каким-то причинам останавливается корректно, то этот параметр её перезапустит. Но это не сработает, если вы выполните (**systemctl stop &lt;unit.service&gt;**).
- **RestartPreventExitStatus** — если служба вылетит с указанным кодом завершения (в нашем случае 255), то она больше не перезапускается, даже если установлен **Restart=on-failure**. Про коды завершения я напишу позже.
- **Type** — тип юнита — очень важный параметр. Отвечает за способ запуска службы и её процессов. Бывают следующие типы: 
    - **simple** или **exec** — они используются для запуска программы как службы. Этот тип стоит использовать, когда запускаемая программа не умеет сама запускаться в фоновом режиме. То есть запускается на переднем плане. Прервать выполнение такой программы можно с помощью комбинации клавиш **Ctrl+C**. При этом **simple** отрабатывает быстрее, но не следит за запуском исполняемого файла, указанного в **ExecStart**. А **exec** дожидается запуска исполнимого файла, и только после этого служба считается запущенной. Желательно использовать **exec** вместо **simple**, так как в **simple** служба может оказаться успешно запущенной, даже если исполнимый файл не смог запустится;
    
    
    - **forking** — этот тип следует использовать если запускаемое приложение умеет само запускаться в фоновом режиме. Можно использовать этот тип, если вы запускаете с помощью скрипта несколько процессов, которые умеют запускаться в фоне. То есть в **ExecStart** указан скрипт, а в скрипте запускаются фоновые процессы. В этом случае сам скрипт выполнится и завершится, а служба будет управлять запущенными фоновыми процессами;
    
    
    - **oneshot** — если подразумевается разовый запуск утилиты или скрипта, то подойдет этот тип. Служба такого типа никогда не будет запущенной. Вы запускаете службу, скрипт выполняется и служба останавливается;
    
    
    - **dbus** — если служба использует соединение D-Bus, то можно использовать этот тип службы. Когда служба запустится, то получит имя на шине D-Bus. Если это имя освободится, то служба будет считаться неисправной и все процессы службы начнут завершатся. Про D-Bus также будет написана отдельная статья;
    
    
    - **notify** — поведение похоже на **exec**, но дополнительно ожидается, что служба отправит сигнал готовности после своего запуска;
    - **idle** — система отложит выполнение двоичного файла службы до окончания запуска остальных (более срочных) задач, максимум на 5 секунд. В остальном поведение аналогично **simple**.
- **RuntimeDirectory** — после запуска службы будет создана указанная директория. В нашем случае — **/run/sshd/**;
- **RuntimeDirectoryMode** — директория будет создана с указанными правами. В нашем случае — **755 (rwx r-x r-x)**. Про систему прав в Linux я уже писал [здесь](https://sysadminium.ru/standard_linux_file_permissions/).

#### <span id="bkmrk-%D0%91%D0%BB%D0%BE%D0%BA-%5Binstall%5D-1">Блок \[Install\]</span>

Параметры:

- **WantedBy** — если мы включим автозагрузку этой службы (с помощью команды **systemctl enable &lt;имя службы&gt;**), то она должна запуститься при загрузке мультипользовательского режима (**multi-user.target**). Про таргеты и загрузочные таргеты будет следующая статья.
- **Alias** — псевдоним службы. Указание псевдонима приведёт к тому, что к службе можно будет обратиться ещё и по имени псевдонима. Например, вместо **ssh.service** можно использовать **sshd.service** (**systemctl status sshd.service**).

## <span id="bkmrk-%D0%94%D0%BE%D0%BF%D0%BE%D0%BB%D0%BD%D0%B8%D1%82%D0%B5%D0%BB%D1%8C%D0%BD%D1%8B%D0%B5-%D0%BE%D0%BF%D1%86%D0%B8%D0%B8-1">Дополнительные опции</span>

В юните **SystemD** — **ssh.service** использовались далеко не все из возможных опций. Вот некоторые опции, которые можно использовать для создания своих служб (в блоке **\[Service\]**):

- **User** — с помощью этого параметра можно указать пользователя (username или uid), от чьего имени будут запущены процессы службы. То есть этот пользователь будет запускать исполняемый файл, указанный в **ExecStart**.
- **ExecStop** — команда, которую нужно выполнить при остановке службы (systemctl stop &lt;unit.service&gt;).
- **KillSignal** — здесь можно указать сигнал остановки процессов. А если этот параметр не указать, то будет использован сигнал **SIGTERM**. Про сигналы, которые можно посылать процессам, я напишу позже.
- **LimitNOFILE** — указывается максимальное число открытых файлов, которые смогут открыть процессы службы.
- **LimitCPU** — можем задать максимальное количество процессорного времени в секундах. Когда лимит будет израсходован, служба завершится с ошибкой.
- **LimitRSS** — лимит потребления памяти в байтах. При достижении лимита служба завершится с ошибкой.
- **LimitNPROC** — максимальное число процессов в службе.
- **Nice** — уровень любезности запускаемых процессов службой. Любезность (**Nice**) — это параметр обратный приоритету. Чем выше Nice, тем ниже приоритет у процесса, и тем меньше он нагружает процессор. Может быть от -20 (самый приоритетный) до 19 (наименьший приоритет).

## <span id="bkmrk-%D0%98%D1%82%D0%BE%D0%B3-1">Итог</span>

Это не все опции, на самом деле их очень много. Для тех кто хочет во всём этом разобраться дам несколько ссылок на официальную документацию **SystemD** (на английском):

- [Настройка юнитов](https://www.freedesktop.org/software/systemd/man/systemd.unit.html) — описывают сами юниты, их типы, их расположение и приоритет. А также здесь описываются параметры, которые можно использовать в блоке **\[Unit\]** и **\[Install\]**.
- [Управление выполнением юнитов (не только служб)](https://www.freedesktop.org/software/systemd/man/systemd.exec.html) — описывают параметры, с помощью которых мы можем контролировать запускаемые процессы. Эти параметры принадлежат блоку **\[Service\]**. Это лимиты, пользователи, параметры безопасности, переменные окружения и тому подобное.