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

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-подібний, то ця конструкція позбавляє нас від зайвого копіювання коду.
Секція User
Тут описуються загальні парамметры, застосовні до будь-якого типу юніта.
[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

Обмежуємо права
Якщо необхідно накласти обмеження на права, то зробити це, звичайно ж, потрібно.
Створюємо копії нашого сервісу і скрипта до нього:
$ 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-а — набору програм для вирішення різних завдань. Успіхів!

Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.