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_receive
はwpa_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_recive
がwpa_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.c
はutils/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); } }
ここまで見るとわかるけど、bash
やsh
を引数に渡していない。
当然、実行権限が付与されていないと駄目になるという話だった。