Ivan Shapovalov, Like-all, Pavlo Rudyi
Part 1 • Part 2 • Part 3Приветствуем всех.
В #3 нашего затяжного обзора возможностей systemd мы разбирались с
systemd-journald
— подсистемой сбора системных логов (называть его заменой syslogd не совсем корректно, хотя вести свой лог в виде бинарной БД он также умеет). Однако, часть аспектов работы с journald осталась неохваченной.В связи с этим очередную часть systemd In Action мы начнём с двух вещей, с ним связанных. А именно:
- оценить устойчивость нативного формата БД к произвольным повреждениям;
- рассмотреть возможности journald (точнее, трёх вспомогательных утилит) по передаче логов по сети.
The Journal, продолжение.
Устойчивость к повреждениям
Проверим, насколько нативный формат файлов journald устойчив к повреждениям. Для этого запишем 1 КиБ мусора в случайное место в файле системного лога и посмотрим, что изменится в выводеjournalctl
.Для начала удостоверимся, что исходно файл лога не нарушен. Для этого воспользуемся штатным средством проверки целостности —
journalctl --verify
. По умолчанию этот режим (так же, как и вывод данных) производится над всеми лог-файлами в стандартных расположениях (/{var,run}/log/journal
).# journalctl --verify
PASS: /run/log/journal/fe39ba83b9244251b1704fc655fbff2f/system.journal
0015d0: invalid data hash table item (290/233016) head_hash_offset: b5d9a2492d2da8f7
0015d0: invalid object contents: Bad message
File corruption detected at /var/log/journal/fe39ba83b9244251b1704fc655fbff2f/system.journal:0015d0 (of 8388608 bytes, 0%).
FAIL: /var/log/journal/fe39ba83b9244251b1704fc655fbff2f/system.journal (Bad message)
В нашем случае логфайл уже был повреждён (в ходе предыдущих экспериментов). Простейший способ получить неповреждённый логфайл — удаление существующего и перезапуск машины, при котором тот пересоздастся и будет заполнен новыми записями. Собственно, делаем это.# cd /var/log/journal/$(< /etc/machine-id )
# l
total 8.1M
drwxr-sr-x+ 2 root systemd-journal 4.0K Feb 21 17:50 .
drwxr-sr-x+ 4 root systemd-journal 4.0K Feb 19 19:40 ..
-rw-r-x---+ 1 root systemd-journal 8.0M Feb 21 17:50 system.journal
Имеем один файл минимального размера (изменение размера логфайлов производится дискретно, шагами по 8 МиБ). Проверяем целостность ещё раз:# journalctl --verify
PASS: /var/log/journal/fe39ba83b9244251b1704fc655fbff2f/system.journal
Проверка успешна, как и ожидалось. Теперь экспортируем бинарный файл в подробный текстовый формат (KEY=VALUE
по всем полям всех записей по очереди):# journalctl -o export > uncorrupted
Дальше намеренно повреждаем файл system.journal
, записав в него 1 КиБ случайных данных:# dd if=/dev/urandom of=/var/log/journal/fe39ba83b9244251b1704fc655fbff2f/system.journal bs=1K count=1 seek=10 conv=notrunc
1+0 records in
1+0 records out
1024 bytes (1.0 kB) copied, 0.0022644 s, 452 kB/s
Собственно, так делать было неправильно. За то время, пока мы его повреждали, в файл успела добавиться ещё одна запись (о подгрузке в ядро модуля ГПСЧ).Экспортируем данные еще раз:
# journalctl -o export > corrupted
# l corrupted
-rw-r--r--. 1 root root 871K Feb 21 17:53 corrupted
Как видим, экспорт повреждённого файла прошел успешно и на выходе мы получили файл ненулевой длины. Теперь сравниваем полученные текстовые файлы:# diff -u uncorrupted corrupted
--- uncorrupted 2015-02-21 17:52:12.701000000 +0000
+++ corrupted 2015-02-21 17:53:30.576000000 +0000
@@ -25526,3 +25526,16 @@
_SYSTEMD_UNIT=session-3.scope
_SOURCE_REALTIME_TIMESTAMP=1424541030255833
+__CURSOR=s=00000000000000000000000000000000;i=560f;b=ca178527e7014a6c868084dac3960dd2;m=8d4be3a;t=50f9cd5ee7c96;x=fef0f211c7d6b5b0
+__REALTIME_TIMESTAMP=1424541162044566
+__MONOTONIC_TIMESTAMP=148160058
+_BOOT_ID=ca178527e7014a6c868084dac3960dd2
+_MACHINE_ID=fe39ba83b9244251b1704fc655fbff2f
+_HOSTNAME=systemd.cf
+_TRANSPORT=kernel
+PRIORITY=5
+SYSLOG_FACILITY=0
+SYSLOG_IDENTIFIER=kernel
+_SOURCE_MONOTONIC_TIMESTAMP=148159245
+MESSAGE=random: nonblocking pool is initialized
И вот этот результат мы интерпретировали неправильно, посчитав, что одна запись из повреждённого файла исчезла. На самом деле нет — одна запись была добавлена1.На самом деле…
…всё не так радужно. При fuzz-тестировании произвольно взятого лог-файла достаточно простым скриптом, с некоторой степенью точности имитирующим посекторное повреждение, выясняется, что в некоторых случаях повреждение приводит к нечитаемости штатными средствами всех данных после места повреждения.Тем не менее, необходимо подчеркнуть, что речь здесь идёт только о штатных средствах просмотра: утилите
journalctl
, которая принудительно прекращает чтение после первой полученной ошибки. Вполне вероятно, что ситуацию можно улучшить, исправив утилиту просмотра (или реализовав её аналог, ориентированный именно на восстановление).Наконец, отметим, что больше информации о том, как работать с журналом, можно найти в journalctl(1), а список основных полей – в systemd.journal-fields(7). Ну и сам формат
.journal
-файлов тоже вполне документирован.Возможности сетевого транспорта логов
Наверняка многие считают, что journald, в отличие от многих реализаций syslogd, не умеет передавать логи по сети и ограничен исключительно локальной их обработкой. Однако, это не совсем так. Сам по себеsystemd-journald
, действительно, ничего не знает про сеть и пишет либо в syslog-совместимый сокет, либо в файлы нативного формата. Тем не менее, в комплекте поставки systemd существуют три вспомогательные утилиты, с помощью которых и реализуется передача логов по сети в реальном времени “своими силами”.Важное замечание: эти утилиты работают исключительно с логфайлами на диске (т. е. в нулевом приближении они похожи на
tail -f | netcat
). Поэтому для того, чтобы передавать логи по сети средствами journald, необходимо включить запись файлов в нативном формате — хотя бы в режиме Storage=volatile
.Итак, в systemd/journald существуют два способа передачи логов через сеть:
- достаточно необычная pull-модель, при которой лог-сервер инициирует соединение и запрашивает данные, в то время как на самом деле сервер (
systemd-journal-gatewayd
) работает на источнике; - и более традиционная push-модель, когда лог-сервер в действительности является сервером, а открывает соединение и отправляет данные машина-источник (а именно утилита
systemd-journal-upload
).
systemd-journal-remote
.Данные передаются по сети поверх протокола HTTP(S) в подробном почти текстовом формате вида
KEY=VALUE
. Именно в этом формате логи отображаются командой journalctl -o export
.Способ первый — “pull”
В этом режиме на источнике логов мы запускаем самый настоящий специализированный HTTP-серверsystemd-journal-gatewayd
(реализованный с помощью libmicrohttpd).Вообще, у этого сервера нет даже конфигурационного файла: всё достигается прямым редактированием юнитов. Так, например, он представляет собой обычный сокет-активируемый сервис, поэтому чтобы изменить порт, на котором он будет принимать соединения, следует обратиться к юниту
systemd-journal-gatewayd.socket
и изменить в нём значение директивы ListenStream=
. Или, например, для того, чтобы задать сертификат и секретный ключ HTTPS, достаточно изменить юнит systemd-journal-gatewayd.service
, дописав в командную строку демона параметры --cert=
и --key=
.Нас вполне устраивает стандартный порт 19531 (а настройку HTTPS для краткости мы опустим), поэтому сразу перейдём к запуску этого сервера на отдельной машине:
# systemctl start systemd-journal-gatewayd.socket
HTTP API этого сервера достаточно прост и описан в документации. В частности:Адрес | Ответ сервера |
---|---|
/browse | интерактивная веб-консоль |
/entries | (основной метод) дамп журнала |
/machine | JSON-структура, описывающая систему (machine-id, boot-id, …) |
/fields/<field> | список всех значений, которые принимает поле <field> на данном участке журнала |
/entries
может принимать несколько URL-параметров, управляющих фильтрацией.URL-параметр | Значение |
---|---|
?boot | эквивалент journalctl --this-boot |
?follow | эквивалент journalctl --follow |
?discrete | вернуть только ту запись, на которую указывает заголовок Range: (об этом чуть дальше) |
?<field>=<value> | добавить фильтр по значению поля <field> |
HTTP-заголовок | Значение |
---|---|
Range: entries=<cursor>[[:<skip>]:<count>] | выбор диапазона отображения: начать с курсора <cursor> , пропустить <skip> записей и вывести не более, чем <count> записей (по умолчанию — все или одну, в зависимости от параметра ?discrete ) |
Accept: <MIME-тип> | выбор формата данных: например, text/plain (простой текст) или application.vnd.fdo.journal (journalctl -o export -подобный формат) |
# curl -H"Accept: text/plain" "http://77.41.63.43:19531/entries?boot" > remote-current-boot-export
# curl -H"Accept: application/vnd.fdo.journal" "http://77.41.63.43:19531/entries?boot" > remote-current-boot-export
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 15.1M 0 15.1M 0 0 918k 0 --:--:-- 0:00:16 --:--:-- 930k
Как видим, это вполне себе нормальный протокол HTTP, с которым можно работать привычными средствами.Теперь попробуем сделать то же самое, но уже с помощью утилиты
systemd-journal-remote
. “Комплектный” юнит запускает её в push-конфигурации, поэтому сейчас мы будем делать это вручную из консоли.# lib/systemd/systemd-journal-remote --url=http://77.41.63.43:19531
Received 0 descriptors
Spawning curl http://77.41.63.43:19531/entries...
/var/log/journal/remote/remote-77.41.63.43:19531.journal: Successfully rotated journal
/var/log/journal/remote/remote-77.41.63.43:19531.journal: Successfully rotated journal
Собственно, внутри это тот же самый curl. Да, у этой утилиты конфигурационный файл уже есть: /etc/systemd/journal-remote.conf
. С его помощью можно задать сертификат и секретный ключ HTTPS, а также (директивой SplitMode=
) выбрать способ разбиения получаемых данных на отдельные файлы. Мы вернёмся к этой настройке при разборе push-конфигурации.Помимо этих настроек,
systemd-journal-remote
позволяет указать в командной строке (параметром --output=
) директорию или файл назначения, куда будут сохраняться получаемые от удалённой машины логи. Если этот параметр не указан, в качестве назначения по умолчанию будет взята директория /var/log/journal/remote
, а выходной файл будет назван согласно hostname-части указанного URL.На остальных параметрах этой утилиты мы останавливаться не будем — они все документированы в man-странице systemd-journal-remote(8). Но отдельно отметим, что просматривать лог-файлы в нестандартной директории можно с помощью команды
journalctl -D <директория>
. Обратите внимание, точно таким же способом можно получить доступ к логам не запущенной в данной момент системы (например, при восстановлении).Способ второй — “push”
Теперь перейдём к рассмотрению более привычной модели взаимодействия, в которой соединение с лог-сервером инициируется машиной-источником логов.Сначала запустим лог-сервер, представленный всё той же утилитой
systemd-journal-remote
. На этот раз мы будем запускать её с помощью комплектных юнитов, как обычный сокет-активируемый демон. Как и в предыдущем случае, для изменения порта или адреса, на котором будет создан сокет, предлагается напрямую править юнит systemd-journal-remote.socket
.Вернёмся к конфигурационному файлу этой утилиты.
[Remote]
# SplitMode=host
# ServerKeyFile=/etc/ssl/private/journal-remote.pem
# ServerCertificateFile=/etc/ssl/certs/journal-remote.pem
# TrustedCertificateFile=/etc/ssl/ca/trusted.pem
Директива SplitMode=
(и параметр командной строки --split-mode=
) позволяет указать, как сохранять данные, получаемые с разных хостов. Допустимы всего два значения:Значение | Описание |
---|---|
none | записывать все получаемые логи сплошным потоком |
host | распределять логи по файлам в зависимости от hostname источника2 |
host
) и стандартный порт (на этот раз 19532), поэтому просто активируем .socket
-юнит:# systemctl start systemd-journal-remote.socket
После этого настроим и запустим на второй машине утилиту systemd-journal-upload
. Для неё уже есть готовый юнит systemd-journal-upload.service
, не требующий изменений, а также конфигурационный файл /etc/systemd/system/journal-upload.conf
.Нам остаётся всего лишь указать в этом файле (директивой
URL=
) адрес сервера, на который мы собственно хотим передавать данные:[Upload]
URL= http://systemd.cf:19532
# ServerKeyFile=/etc/ssl/private/journal-upload.pem
# ServerCertificateFile=/etc/ssl/certs/journal-upload.pem
# TrustedCertificateFile=/etc/ssl/ca/trusted.pem
После чего запускаем systemd-journal-upload
и проверяем работу сервиса.# systemctl start systemd-journal-upload
Логи машины-источника:<...>
фев 21 21:35:36 server-9-20 systemd[1]: Starting Journal Remote Upload Service...
<...>
Логи машины-сервера:<...>
Feb 21 18:30:10 systemd.cf systemd[1]: Listening on Journal Remote Sink Socket.
Feb 21 18:30:10 systemd.cf systemd[1]: Starting Journal Remote Sink Socket.
<...>
Как видим — всё работает.3. Настройка сети c помощью systemd
Дальше мы перейдем кsystemd-networkd
— настройке сети в формате и духе systemd. Конфигурационные файлы networkd
расположены в директории /etc/systemd/network
. Networkd
имеет три типа конфигурационных файлов: *.link,
*.network
и *.netdev
. Файлы настройки с расширением .link
описывают физические параметры интерфейсов — каждый файл описывает один интерфейс: MAC-адресс, имя интерфейса, MTU, и прочие параметры, которые не относятся к сетевым. Эти файлы считываются каждый раз одним из обработчиков udev при запуске или перенастройке системы. Файлы с расширением .network
считываются непосредственно демоном networkd
и содержат сетевые параметры интерфейсов: IP-адреса, маршруты, шлюзы, DNS-сервера и прочее. И, наконец, файлы *.netdev
служат для описания виртуальных интерфейсов.Для демонстрации работы
networkd
мы выполним перевод конфигурации нашей машины с legacy sysv-initscript /etc/init.d/network
на networkd
. Напомним, что мы используем Fedora 21, systemd 219 и статическую конфигурацию сети:$ ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
link/ether 04:01:40:23:1f:01 brd ff:ff:ff:ff:ff:ff
inet 188.166.46.238/18 brd 188.166.63.255 scope global eth0
valid_lft forever preferred_lft forever
inet6 2a03:b0c0:2:d0::69:7001/64 scope global
valid_lft forever preferred_lft forever
inet6 fe80::601:40ff:fe23:1f01/64 scope link
valid_lft forever preferred_lft forever
3: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
link/ether 04:01:40:23:1f:02 brd ff:ff:ff:ff:ff:ff
inet 10.133.248.54/16 brd 10.133.255.255 scope global eth1
valid_lft forever preferred_lft forever
inet6 fe80::601:40ff:fe23:1f02/64 scope link
valid_lft forever preferred_lft forever
Выведем список интерфейсов, которые находятся под управлением networkd
с помощью утилиты networkctl
:$ networkctl
IDX LINK TYPE OPERATIONAL SETUP
1 lo loopback n/a n/a
2 eth0 ether n/a n/a
3 eth1 ether n/a n/a
Мы увидели только список интерфейсов и их типов, так как сервис networkd
у нас пока не запущен. После запуска перед нами предстанет немного другая картина:# systemctl start systemd-networkd
# networkctl
IDX LINK TYPE OPERATIONAL SETUP
1 lo loopback carrier unmanaged
2 eth0 ether routable unmanaged
3 eth1 ether routable unmanaged
3 links listed.
Unmanaged
говорит нам, что данный интерфейс пока не находится под управлением networkd, который вполне свободно допускает подобную практику. Посмотрим сетевые параметры интерфейсов, чтобы перенести их в конфигурацию networkd:# cat /etc/sysconfig/network-scripts/ifcfg-eth0
DEVICE='eth0'
TYPE=Ethernet
BOOTPROTO=none
ONBOOT='yes'
HWADDR=04:01:40:23:1f:01
IPADDR=188.166.46.238
NETMASK=255.255.192.0
GATEWAY=188.166.0.1
NM_CONTROLLED='yes'
IPV6INIT=yes
IPV6ADDR=2A03:B0C0:0002:00D0:0000:0000:0069:7001/64
IPV6_DEFAULTGW=2A03:B0C0:0002:00D0:0000:0000:0000:0001
IPV6_AUTOCONF=no
DNS1=2001:4860:4860::8844
DNS2=2001:4860:4860::8888
DNS3=8.8.8.8
Для дистрибутивов, отличных от Red Hat-based эти конфигурационные файлы будут выглядеть иначе, но скорее всего нужную информацию можно получить без проблем. В текущей конфигурации интерфейс eth0
используется для доступа в интернет, а eth1
- в локальную сеть. Также рекомендуем перед запуском networkd переместить эти конфигурационные файлы в другую директорию, так как некоторые правила udev cчитывают их.Перейдем к созданию link-файлов. У нас уже есть дефолтный link-файл
/lib/systemd/network/99-default.link
, в котором настраивается политики назначения MAC-адреса и именования сетевых интерфейсов.[Link]
NamePolicy=kernel database onboard slot path
MACAddressPolicy=persistent
Создадим link-файлы для каждого интерфейса, где укажем интерфейсы и MAC-адреса:cat /etc/systemd/network/90-external.link
[Match]
MACAddress=04:01:40:23:1f:01
[Link]
Name=eth-external
cat /etc/systemd/network/90-internal.link
[Match]
MACAddress=04:01:40:23:1f:02
[Link]
Name=eth-inner
Чтобы сетевые интерфейсы работали под управлением networkd
, нам также необходимы конфигурационные файлы *.network
:cat eth-external.network
[Match]
Name= eth-outer
[Network]
DHCP=no
Adress=188.166.46.238/18
Adress=2A03:B0C0:0002:00D0:0000:0000:0000:0069:7001/64
Gateway=188.166.0.1
Gateway= 2A03:B0C0:0002:00D0:0000:0000:0000:0000:0001
DNS=2001:4860:4860:8844
DNS=2001:4860:4860:8888
DNS=8.8.8.8
cat eth-internal.network
[Match]
Name=eth-inner
[Network]
Address=10.133.248.54/16
Конфигурация готова, дальше остается отключить дефолтный сервис network
и включить автозагрузку сервиса networkd
:# systemctl disable network && systemclt enable networkd.
Для уверенности, что данная конфигурация будет стабильно работать, перезапустим машину и посмотрим работает ли сеть:$ ping systemd.cf
PING systemd.cf (188.166.46.238) 56(84) bytes of data.
64 bytes from systemd.cf (188.166.46.238): icmp_seq=6 ttl=56 time=48.7 ms
64 bytes from systemd.cf (188.166.46.238): icmp_seq=7 ttl=56 time=48.7 ms
64 bytes from systemd.cf (188.166.46.238): icmp_seq=8 ttl=56 time=48.4 ms
Ура, мы успешно настроили сеть с помощью networkd!$ networkctl
IDX LINK TYPE OPERATIONAL SETUP
1 lo loopback n/a n/a
2 eth-outer ether routable configured
3 eth-inner ether routable configured
Теперь пришла пора обратить внимание на systemd-resolved
- кеширующий DNS-сервер, точнее прослойка между glibc и DNS-серверами. Его конфигурационный файл /run/systemd/resolve/resolve.conf
можно использовать только как симлинк к /etc/resolv.conf
, так что изначально он не влияет на сетевую подсистему. Запустим resolved
и покажем как его можно использовать в качестве кеширующего резолвера:# systemctl enable systemd-resolvd && systemctl start systemd-resolvd
Зделаем симлинк на /etc/resolv.conf
:# ln -sf /run/systemd/resolve/resolve.conf /etc/resolve.conf
В отличие от таких программ как dnsmasq
, resolved
не является DNS-proxy - он не представляет виртуальный DNS-сервер, к которому можно отправлять запросы. Вместо этого он предоставляет nss-модуль, который встраивается в glibc, и позволяет любой программе использующей glibc, использовать кеш запросов. Управление осуществляется с помощью файла nssswitch.conf
в секции hosts:hosts: files dns myhostname mymashines
Краткое пояснение: Files
- считывание с /etc/hosts
, dns
- встроеный glibc-резолвер c resolv.conf
, myhostname
&mymashine
- nss модули поставляемые вместе с systemd.myhostname
- резолвит строку localhost
и имя хоста, а mymachines
ресолвит имена контейнеров и их адреса виртуальных интерфейсов.Для использования
resolved
мы должны заменить модуль dns
на resolve
. Давайте проверим:$ getenv hosts goo.gl
2a00:1450:4001:80f::1009 goo.gl
Мы получили IP-адрес с ~1с задержкой. При повторном запуске она должна исчезнуть - это означает, что кеш работает и адрес получается без помощи DNS-запроса. Более подробную информацию вы можете получить используя man systemd-networkd
, man systemd.link
, man systemd.network
, man systemd.netdev
, man systemd-resolved
.4. Busctl и базовая работа с dbus.
Дальше мы переходим к обзору небольших дополнительных утилит в systemd. У многих людей возникает мысль: “Зачем нужно столько утилит:timedated
, hostnamed
, localed
и прочих? Они же написаны явно от нечего делать.” На самом деле подобные демоны раньше присутствовали в KDE: там был свой особый демон с поддержкой dbus и которому передавались различные параметры. Сейчас есть множество приложений, которые используют dbus в своей работе, также не можем не упомянуть policykit
- менеджер прав доступа, который работает с dbus-вызовами и может принимать или отклонять их. Зачем все это нужно? Дело в том, что традиционный подход в виде suid-обертки или вообще без нее, требующий указания пароля суперпользователя - далеко не идеал. Например, для того чтобы сменить hostname DE просто делало форк скорее всего kdesudo hostname
, этот процесс спрашивал пароль суперпользователя и в успешном случае все работало. Не слишком удобно, не так ли? Эта проблема решается с помощью микродемонодов, которые вы наблюдали раньше в составе systemd. Точнее не совсем демоны, потому что они активируются с помощью dbus и не работают все время.Для начала мы продемонстрируем как обращаться к dbus-объектам и как устроена иерархия методов на dbus-шине. Для упрощения жизни у нас есть удобная утилита
busctl
, которая выгодно отличается от страшных на вид существующих аналогов предоставляющих доступ к dbus. В наших примерах мы будем также использовать утилиту dbus-send, которая есть почти везде. Для начала давайте получим список методов и свойств, предоставляемых главным обьектом systemd — org.freedesktop.systemd1
:$ busctl introspect org.freedesktop.systemd1 /org/freedesktop/systemd1
NAME TYPE SIGNATURE RESULT/VALUE
org.freedesktop.DBus.Introspectable interface - -
.Introspect method - s
org.freedesktop.DBus.Peer interface - -
.GetMachineId method - s
.Ping method - -
org.freedesktop.DBus.Properties interface - -
.Get method ss v
.GetAll method s a{sv}
.Set method ssv -
.PropertiesChanged signal sa{sv}as -
org.freedesktop.systemd1.Manager interface - -
.AddDependencyUnitFiles method asssbb a(sss)
.CancelJob method u -
.ClearJobs method - -
.CreateSnapshot method sb o
.DisableUnitFiles method asb a(sss)
.Dump method - s
.EnableUnitFiles method asbb ba(sss)
.Exit method - -
.GetDefaultTarget method - s
.GetJob method u o
.GetUnit method s o
Аналогичная операция с помощью утилиты dbus-send
будет выглядеть так:# dbus-send --system --type=method_call --print-reply --dest=org.freedesktop.systemd1 /org/freedesktop/systemd1 /org/freedesktop.DBus.Introspectable.Introspect
Мы обращаемся к системной шине(–system), дальше производим вызов метода(--type=method_call
) и желаем получить ответ; с помощью флага --dest
мы указываем обьект, к которому отправляем этот вызов. Дальше мы указываем путь внутри этого обьекта(/org/freedesktop.DBus.Introspectable
) и интерфейс с методом в последнем флаге. Продемонстрируем часть вывода:...
<signal name="StartupFinished">
<arg type="t"/>
<arg type="t"/>
<arg type="t"/>
<arg type="t"/>
<arg type="t"/>
<arg type="t"/>
</signal>
<signal name="UnitFilesChanged">
</signal>
<signal name="Reloading">
<arg type="b"/>
</signal>
</interface>
</node>
Какой вариант более лаконичен и удобен судить вам, но думаю что это совершенно очевидно. Мы видим четыре интерфейса, которые реализуются этим обьектом. Каждый интерфейс является совокупностью методов, свойств и сигналов, также у них есть сигнатуры и возвращаемый результат. Для понимания практического применения давайте сменим дату в нашей операционной системе с помощью dbus-интерфейса timedated1.$ busctl introspect org.freedesktop.timedate1 /org/freedesktop/timedate1
NAME TYPE SIGNATURE RESULT/VALUE FLAGS
org.freedesktop.DBus.Introspectable interface - - -
.Introspect method - s -
org.freedesktop.DBus.Peer interface - - -
.GetMachineId method - s -
.Ping method - - -
org.freedesktop.DBus.Properties interface - - -
.Get method ss v -
.GetAll method s a{sv} -
.Set method ssv - -
.PropertiesChanged signal sa{sv}as - -
org.freedesktop.timedate1 interface - - -
.SetLocalRTC method bbb - -
.SetNTP method bb - -
.SetTime method xbb - -
.SetTimezone method sb - -
.CanNTP property b true -
.LocalRTC property b false emits-change
.NTP property b true emits-change
.NTPSynchronized property b true -
.RTCTimeUSec property t 1432108225000000 -
.TimeUSec property t 1432108225986680 -
.Timezone property s "Europe/Kiev" emits-change
Обратите внимание на свойства обьекта: текущее время, временная зона, параметры NTP-синхронизации и прочее. Нам нужен метод .SetTime
, который принимает три параметра, описанные в сигнатуре: время в микросекундах, на которое мы должны сдвинуться; нужность авторизации; сдвиг во времени или установка времени начиная от начала эпохи.Давайте сдвинем время на 1 час вперед. Для этого воспользуемся утилитой
busctl
, а для возвращения к текущему времени — dbus-send
.# busctl call org.freedesktop.timedate1 /org/freedesktop/timedate1 org.freedesktop.timedate1 SetTime xbb $((3600*1000*1000)) true false
Алгоритм тот же: обьект на шине, путь внутри этого обьекта, интерфейс(совпадает с именем обьекта), имя метода и параметры. Первый параметр - сдвиг времени в микросекундах, дальше мы устанавливаем время относительно текущего и не используем относительную авторизацию относительно policykit, так как выполняем запрос от суперпользователя.Посмотрим изменилось ли время c помощью dbus для единства стиля:
$ busctl call org.freedesktop.timedate1 /org/freedesktop/timedate1
...
.RTCTimeUSec property t 1424552366000000 -
.TimeUSec property t 1424552366243943 -
...
Как видим, все работает. Тепер вернем время вспять с помощью dbus-send
:# dbus-send --print-reply --system --type=method_call --dest=org.freedesktop.timedate1 /org/freedesktop/timedate1 org.freedesktop.timedate1.SetTime int64:$((-3600*1000*1000)) boolean:true boolean:false
Прочитать больше про D-Bus API systemd можно здесь. Будут полезными также man D-Bus
, man kdbus
, man sd-bus
, man busctl
.Примечания
- При оценке устойчивости
.journal
-файлов к повреждениям мы неверно интерпретировали полученный дифф из одной записи. Вообще говоря, он был не в ту сторону (т. е. в повреждённом журнале оказалось на одну запись больше, причём в самом конце, что вполне объяснимо).Повреждение, судя по всему, попало в область вспомогательных хэш-таблиц, которые при простом итерировании не используются.↩ - Было неверно сказано, что
.journal
-файлы распределяются по поддиректориям согласно machine-id источников. На самом деле такая иерархия используется только для хранения логов локальных контейнеров.↩
Part 1 • Part 2 • Part 3
Комментариев нет:
Отправить комментарий