kkAyatakaのメモ帳。

誰かの役に立つかもしれない備忘録。

sd-loginでログインセッションのStateの監視をする

loginctlsystemd/sd-login.hAPIを用いてログインセッションなどの現在状態が取得できる。加えて、monitor APIが用意されていて、各種変更の検知/通知の受け取りが可能となっている。

  • sd_login_monitor_newで監視オブジェクトを生成
  • pollでイベントを待ち受ける

印象としてはかなり荒い単位で通知がくる。pollが返ったら自分が監視対象としたい値を取得し、現在どんな値か、変化があったかを自分で 判別する必要がある。

環境

  • CentOS 7.6.1810
  • systemd 219 (loginctl —version)
  • gcc 4.8.5

準備

systemdの開発用パッケージが必要なのでインストールする。

$ sudo yum install systemd-devel

ビルドするときはsystemdをリンクする。

$ g++ -Wall main.cpp -lsystemd

ログインStateの監視

sd_login_monitor_newの第一引数で監視する値を絞れる。ここではユーザーのログイン状態を監視するのに"uid"を渡している。

この値は他の関数群と関係していそうで、"uid"ならsd_uid_get_***系の関数で取れる値を監視できるんじゃなかろうかと思う(が確証なし)。

#include <systemd/sd-login.h>
#include <poll.h>
#include <unistd.h>

#include <cstdlib>
#include <iostream>

int main() {
    // create monitor instance
    sd_login_monitor *mon = 0;
    const int ret = sd_login_monitor_new("uid", &mon);
    if (ret >= 0 && mon) {
        // set up pollfd
        struct pollfd pfd = {};
        pfd.fd = sd_login_monitor_get_fd(mon);
        pfd.events = sd_login_monitor_get_events(mon);

        const uid_t uid = getuid();
        for (;;) {
            // reset the wakeup state
            sd_login_monitor_flush(mon);

            // wait infinitely
            const int r = poll(&pfd, (nfds_t)1, -1);
            if (r > 0 && pfd.revents != 0) {
                std::cout << "poll: " << r << ", revents: " << pfd.revents << std::endl;

                // uid state
                char *state = 0;
                const int ret = sd_uid_get_state(uid, &state);
                if (ret >= 0 && state) {
                    std::cout << "State is: " << state << std::endl;
                    free(state);
                }
            }
        }

        sd_login_monitor_unref(mon);
    }
}

プログラムを実行して、「ユーザースイッチ -> ユーザー切り替えを行わずに再度ログイン」したところ、以下のように出力された。監視対象の状態が多いので、(イメージよりも)不意に呼ばれたりする。

$ ./a.out
poll: 1, revents: 1
State is: online
poll: 1, revents: 1
State is: online
poll: 1, revents: 1
State is: online
poll: 1, revents: 1
State is: active
poll: 1, revents: 1
State is: active

タイムアウト時間の設定値

ドキュメントを見るとsd_login_monitor_get_timeoutで返る値から計算するようになっているけど、これはイマイチ動作確認できず。試した環境だと計算上-1になって、結局無限に待つことになった。

参考