バージョン管理された人

subversionで管理されてます

wpa_cliに渡せるアクションスクリプト

wpa_cliはwpa_supplicantからのイベントを待ち受けて実行されるアクションスクリプトというものを設定できる。 このアクションスクリプトは第二引数にwpa_supplicantからのイベントが渡される。

イベント 意味
CONNECTED APへの接続時に発行される
DISCONNECTED APから接続解除されたときに発行される

これを使って、次のようなスクリプト(ここではhoge.sh)を作ってやり、

case "$2" in
  "CONNECTED") echo "APへ接続されたときの処理。DHCPとかを連続して呼びたいときとか";;
  "DISCONNECTED") echo "APから接続解除されたときの処理、クリーンアップとか";;
esac

wpa_cli-aオプションの引数として指定してやると、wpa_supplicantからイベントが呼ばれるたびに、登録したスクリプトが発行される。

wpa_cli -B -i wlan0 -a hoge.sh

みたいにしてやると、バックグラウンドで動作しつつ、wlan0インターフェースを監視して、wpa_supplicantからイベントが来たら、hoge.shが呼ばれる。

オプション 意味
-B wpa_cliをバックグランドで起動するようにする
-i wlan0 操作対象のWi-Fiインターフェースを指定する。wlan0に好きなインターフェース名を入れる

問題は、アクションスクリプトに実行権限が付いているかどうかという部分。 気付かないと時間が溶ける(溶けた)。 この手のスクリプト名を指定しておいて実行させるようなコマンドは気を効かせて、bashコマンドとかでスクリプト名指定して確実に走らせるようにしたりすることが多い(気がする)、のだけど、このwpa_cliはそんなことはしてくれない。 実行権限を適切に付けてやらねばならない。

chmod +x hoge.sh

原因

理由はわかりきっているが、きちんと書いておく。 なお、適宜いらない部分は削除している。

まず、main関数のオプション解析部で、-aに渡されたオプション引数をaction_fileという変数に収めている。

/* https://w1.fi/cgit/hostap/tree/wpa_supplicant/wpa_cli.c#n54 */
static const char *action_file = NULL;

int main(int argc, char *argv[])
{
    /* https://w1.fi/cgit/hostap/tree/wpa_supplicant/wpa_cli.c#n4641 */
    for (;;) {
        c = getopt(argc, argv, "a:Bg:G:hi:p:P:s:v");
        if (c < 0)
            break;
        switch (c) {
        case 'a':
            action_file = optarg;
            break;
        }
    }
}

main関数内の前処理が終了したら、action_file変数を見て、スクリプトが指定されていたらwpa_cli_action関数へ処理を移す。

int main(int argc, char *argv[])
{
    /* https://w1.fi/cgit/hostap/tree/wpa_supplicant/wpa_cli.c#n4724 */
    if (action_file)
            wpa_cli_action(ctrl_conn);
}

このwpa_cli_actionが、wpa_cli_action_receiveをAP接続イベントが発生したときのハンドラとして登録しておく。

/* https://w1.fi/cgit/hostap/tree/wpa_supplicant/wpa_cli.c#n4533 */
static void wpa_cli_action(struct wpa_ctrl *ctrl)
{
    /* https://w1.fi/cgit/hostap/tree/wpa_supplicant/wpa_cli.c#n4544 */
    eloop_register_read_sock(fd, wpa_cli_action_receive, ctrl, NULL);
}

APへ接続できたら、wpa_cli_action_receiveが呼ばれる。 wpa_cli_action_receivewpa_cli_recv_pendingを単に呼びだすだけ。

/* https://w1.fi/cgit/hostap/tree/wpa_supplicant/wpa_cli.c#n4525 */
static void wpa_cli_action_receive(int sock, void *eloop_ctx, void *sock_ctx)
{
    /* https://w1.fi/cgit/hostap/tree/wpa_supplicant/wpa_cli.c#n4529 */
    wpa_cli_recv_pending(ctrl, 1);
}

このwpa_cli_action_recivewpa_cli_action_processを呼ぶ。

/* https://w1.fi/cgit/hostap/tree/wpa_supplicant/wpa_cli.c#n4185 */
static void wpa_cli_recv_pending(struct wpa_ctrl *ctrl, int action_monitor)
{
    /* https://w1.fi/cgit/hostap/tree/wpa_supplicant/wpa_cli.c#n4191 */
    while (wpa_ctrl_pending(ctrl) > 0) {
        if (wpa_ctrl_recv(ctrl, buf, &len) == 0) {
            if (action_monitor)
                wpa_cli_action_process(buf);
}

wpa_cli_action_processが内部でwpa_cli_execを呼ぶ。

/* https://w1.fi/cgit/hostap/tree/wpa_supplicant/wpa_cli.c#n3907 */
static void wpa_cli_action_process(const char *msg)
{
    /* https://w1.fi/cgit/hostap/tree/wpa_supplicant/wpa_cli.c#n3939 */
    if (str_starts(pos, WPA_EVENT_CONNECTED)) {
        if (wpa_cli_connected <= 0 || new_id != wpa_cli_last_id) {
            wpa_cli_connected = 1;
            wpa_cli_last_id = new_id;
            wpa_cli_exec(action_file, ifname, "CONNECTED");
        }
    }
}

wpa_cli_execは、os_execを呼び出してプログラムの実行を行っている。

/* https://w1.fi/cgit/hostap/tree/wpa_supplicant/wpa_cli.c#n3884 */
static int wpa_cli_exec(const char *program, const char *arg1,
            const char *arg2)
{
    /* https://w1.fi/cgit/hostap/tree/wpa_supplicant/wpa_cli.c#n3900 */
    res = os_exec(program, arg, 1);
}

このos_exec関数はutils/os.h内で定義されていて、wpa_cli.cutils/common.hをインクルードしているため、この関数が利用できる。 関数本体はutils/os_internal.cに定義されていて、この関数が子プロセスを生成してexecvを実行している。

/* https://w1.fi/cgit/hostap/tree/src/utils/os_internal.c#n503 */
int os_exec(const char *program, const char *arg, int wait_completion)
{
    /* https://w1.fi/cgit/hostap/tree/src/utils/os_internal.c#n508 */
    pid = fork();
    if (pid == 0) {
        execv(program, argv);
    }
}

ここまで見るとわかるけど、bashshを引数に渡していない。 当然、実行権限が付与されていないと駄目になるという話だった。