ぶていのログでぶログ

思い出したが吉日

APTコマンドの操作をJSON Hookで受け取る

とあることをやりたくて調べていたら見つけたので記録。 APT 1.6からJSON-RPC2.0を使ったフック機能が追加されたらしい。 詳しくは以下のドキュメント。

salsa.debian.org

フックスクリプトの準備

まず、フックスクリプトを用意する。 以下はテストコードから持ってきた。

#!/bin/bash
while true; do
    read request <&$APT_HOOK_SOCKET
    read empty <&$APT_HOOK_SOCKET

    if echo "$request" | grep -q ".hello"; then
        echo "HOOK: HELLO"
        printf '{"jsonrpc": "2.0", "result": {"version": "0.1"}, "id": 0}\n\n' >&$APT_HOOK_SOCKET
    fi

    if echo "$request" | grep -q ".bye"; then
        echo "HOOK: BYE"
        exit 0;
    fi

    echo HOOK: request $request
    echo HOOK: empty $empty
done

$APT_HOOK_SOCKET経由でUnixドメインソケットのFDが渡されるので、それを参照して読み込む。 区切り文字が \n\n なのでここでは2回readしている。 APTコマンドはHookスクリプトの始めて起動するときにはhelloパケット(org.debian.apt.hooks.hello )を送ってくるので、規定のパケットを返す。 また、byeパケット(org.debian.apt.hooks.bye)が来た時はそこで処理が終了する。 $request に含まれるパラメータは前述のドキュメントに書いてあるので参考にしてほしい。 シェルスクリプトではJSONパースが面倒だが、LL言語を使えば簡単にパースできるはず。

フックの有効化

フックスクリプトができたらフックが有効になるように設定を行なう。 例えば以下のように設定する。

$ cat /etc/apt/apt.conf.d/99hook
AptCli::Hooks::Install:: "/path/to/hook.sh";
AptCli::Hooks::Search:: "/path/to/hook.sh";

設定すると以下のようにフックが機能するようになる。

$ sudo apt install sl
Reading package lists... Done
Building dependency tree
Reading state information... Done
HOOK: HELLO
HOOK: request {"jsonrpc":"2.0","method":"org.debian.apt.hooks.hello","id":0,"params":{"versions":["0.1"]}}
HOOK: empty
HOOK: request {"jsonrpc":"2.0","method":"org.debian.apt.hooks.install.pre-prompt","params":{"command":"install","search-terms":["sl"],"unknown-packages":[],"packages":[{"id":23690,"name":"sl","architecture":"amd64","mode":"install","automatic":false,"versions":{"candidate":{"id":55553,"version":"3.03-17build2","architecture":"amd64","pin":500},"install":{"id":55553,"version":"3.03-17build2","architecture":"amd64","pin":500}}}]}}
HOOK: empty
HOOK: BYE
The following NEW packages will be installed:
  sl
0 upgraded, 1 newly installed, 0 to remove and 26 not upgraded.
Need to get 26.4 kB of archives.
After this operation, 98.3 kB of additional disk space will be used.
Get:1 http://archive.ubuntu.com/ubuntu bionic/universe amd64 sl amd64 3.03-17build2 [26.4 kB]
Fetched 26.4 kB in 1s (32.9 kB/s)
debconf: delaying package configuration, since apt-utils is not installed
Selecting previously unselected package sl.
(Reading database ... 4377 files and directories currently installed.)
Preparing to unpack .../sl_3.03-17build2_amd64.deb ...
Unpacking sl (3.03-17build2) ...
Setting up sl (3.03-17build2) ...
HOOK: HELLO
HOOK: request {"jsonrpc":"2.0","method":"org.debian.apt.hooks.hello","id":0,"params":{"versions":["0.1"]}}
HOOK: empty
HOOK: request {"jsonrpc":"2.0","method":"org.debian.apt.hooks.install.post","params":{"command":"install","search-terms":["sl"],"unknown-packages":[],"packages":[{"id":23690,"name":"sl","architecture":"amd64","mode":"install","automatic":false,"versions":{"candidate":{"id":55553,"version":"3.03-17build2","architecture":"amd64","pin":500},"install":{"id":55553,"version":"3.03-17build2","architecture":"amd64","pin":500}}}]}}
HOOK: empty
HOOK: BYE

おわりに

APTコマンドの操作をJSON Hookで受け取れることがわかった。 同じようなことは Dpkg::Pre-InvokeDpkg::Post-Invoke、dpkgコマンドの --pre-invoke / --post-invoke などでできるのだがこれだとパッケージ情報が取れないので、パッケージ情報を取りたい場合はJSON Hookを利用することになる。 またこのフックはAPTコマンドだけで、aptdaemonやunattended-updatesみたいにlibapt-pkg(のPythonバインディング)を使っているようなコマンドではフックできないので注意。 フックできてもいいと思うんだけどなぁ。