systemd: getty-подобный сервис для htop

Опубликовано lynx - пн, 07/04/2016 - 15:10

Источник.

htop — это интерактивная программа для наблюдения за процессами; она — альтернатива программы top. Каждый, кто работает за машиной с линуксом на борту, хоть раз использовал её: будь то поиск процесса (и его последующее убийство) или тщательный мониторинг используемых ресурсов.

 

 

Для удобства это программу можно держать всегда запущенной: в отдельном окне терминала, в его вкладках или на каком-нибудь рабочем столе. Но есть ещё одно решение: запускать его на фиксированном VT, на который можно в любой момент переключиться. Преимущество такого подхода заключается в чистом окружении и независимости от иксов/терминала.

 

Это возможно (и правильно) сделать с помощью системы инициализации, потому что мы фактичесски хотим получить специальный getty-подобный сервис для htop.

 

Как запускаются VT1, ..., VT6?

 

agetty — это такая программа, которая открывает порт tty, выдаёт prompt для аутентификации и передаёт последующее управление другой программе — login.

 

$ which agetty login | xargs ls -l -rwxr-xr-x 1 root root 44104 Sep 29 05:21 /usr/bin/agetty -rwxr-xr-x 1 root root 35968 Sep 29 05:21 /usr/bin/login

 

Традиционные системы инициализации Linux конфигурируются на запуск фиксированного количества agetty при загрузке. В большинстве случаев рождаются шесть инстансов для шести VT: от tty1 до tty6 соответственно. В systemd используется другой подход.

 

  • Первый — динамический. Инстанс сервиса getty@.service запускается по требованию. То есть только в том случае, если нам нужен какой-то конкретный VT. За это отвечает logind, который при переключении на ttyN запускает сервис autovt@ttyN.service, который является симлинком на getty@.service. Такая логика работает для tty2-tty6.
  • Второй — статический. Конкретный инстанс сервиса getty@.service втягивается автоматически через getty.target, что даёт нам всегда запущенный getty на tty1.

 

systemctl cat getty@.service покажет содержимое этого сервиса. Рассматривать подробно мы его не собираемся, поскольку это для нас не столь важно.

 

Соответственно, если предположить, что у нас есть некий htop@.service, то и добавить его в автозагрузку можно двумя путями: либо сделать симлинк под именем autovt@ttyN.service — тогда при переключении на выбранный VT htop будет запускаться вместо getty, либо отключитьgetty@ttyN.service и вместо него включить htop@ttyN.service — это даёт нам всегда запущенный htop на фиксированном VT.

 

Пишем собственный getty-подобный юнит

 

Теперь переходим в /etc/systemd/system — одна из директорий, где располагаются юниты, — и создаём собственный сервис:

 

$ "$EDITOR" htop@.service

 

Наличие суффикса (@) означает, что стартует не сам по себе сервис, а один из его инстансов. А суффикс передаётся в него парамметром (%i и %I).

 

Выше уже было отмечено, что содержимое getty@.service для нас не столь важно. Всё так, потому что его можно заинклюдить в наш сервис:

 

.include /usr/lib/systemd/system/getty@.service

 

Если учесть, что наш сервис getty-подобный, то эта конструкция избавляет нас от лишнего копирования кода.

 

Секция Unit

 

Здесь описываются общие парамметры, применимые к любому типу юнита.

 

[Unit] Description=htop on %I Documentation=man:htop(1)

 

Здесь всё прозрачно: директива Description задаёт краткое описание юнита, а Documentation путь к документации. %I — имя инстанса. Важно заметить, что обе переменные, задающие имя инстанса, различны по значению: %I — тоже самое, что и %i, но она не экранирует escape-последовательности.

 

Секция Service

 

Эта секция задаёт конфигурацию конкретно сервиса. Иными словами, описывает способ запуска процесса.

 

[Service] Environment= Environment=TERM=linux HOME=/root ExecStart= ExecStart=/usr/bin/htop StandardInput=tty-fail StandardOutput=tty

 

Необходимые унаследованные значения директив мы оставим в покое, а некоторые нам необходимо сбросить (задаём для них пустое значение) и определить самостоятельно. К таковым относятся Environment — задание переменных, и ExecStart, — собственно, запуск процесса.

 

StandardInput=tty-fail StandardOutput=tty

 

— это указание systemd запускать htop подсоединённым напрямую к терминалу.

 

Можно, кстати, добавить не мгновенный запуск, а с ожиданием ввода. Для этого создаём простой скрипт на баше:

 

#!/bin/bash echo "Press a key to launch $(basename "$1")" read exec "$@"

 

Всё что он делает — ожидает ввода и запускает какую-то программу (которой будет являться htop в нашем случае). Помещаем куда угодно, называем как угодно, делаем его исполняемым (chmod +x) и правим ExecStart в нашем сервисе:

 

ExecStart=/etc/systemd/scripts/run_wait /usr/bin/htop

 

Ограничиваем права

 

Если необходимо наложить какие-либо ограничения на права, то сделать это, конечно же, нужно. Для этого мы создадим ещё один сервис и ещё один скрипт, теперь — htop_secure@.service и run_wait_su. Их мы переконфигурируем так, чтобы htop запускался с правами конкретного пользователя и конкретной группы, а также требовал пароль администратора.

 

Итак, создаём новый сервис и новый скрипт на основе двух предыдущих:

 

$ cd /etc/systemd $ cp system/htop@.service system/htop_secure@.service $ cp scripts/run_wait scripts/run_wait_su

 

И редактируем каждый из них. Для сервиса в секции Service изменяем значение Environment и задаём имя пользователя с его группой:

 

User=kalterfive Group=users Environment=TERM=linux

 

А в скрипте обращаемся к su(1):

 

#!/bin/bash echo "Press a key to launch $(basename "$1")" read exec su -c "$@"

 

Установка сервиса

 

Теперь наш сервис готов, осталось только добавить его в автозагрузку:

 

$ systemctl daemon-reload $ systemctl disable getty@tty2.service $ systemctl enable htop@tty2.service

 

Первая команда обновляет менеджер конфигурации systemd, а вторая создаёт симлинк на наш сервис в getty.target.wants.

 

Заключение

 

Теперь перезагружаемся (либо вручную убиваем getty@ и включаем htop@ для инстанса tty2), переключаемся на второй VT и наблюдаем успешно запущенный htop. Продемонстрированный трюк задевает лишь малую часть systemd, как системы инициализации, от всего простора его возможностей, как универсального plumbing layer-а — набора программ для решения совершенно разных задач. Успехов!