SSブログ

半日無駄になった [Raspberry Pi]

macOSをホストに、A/DとD/AコンバータをつないだRaspberry PiにWi-Fiで接続してアナログデータを読み書き表示するようなのを書いた。なんだか似たようなものばかり書いてるような気がする。ところが今日は動く動かない以前の問題で悩んだ。わかればつまらない原因だったんだけど、これで半日潰してしまった。残り時間が少ないというのに....

ホストのmacOSはまずRaspberry Piが起動してるかどうか調べる。それにはこんなことをした。
- (BOOL)isServerAlive:(NSString *)hostName
{
    NSString    *pph = [NSString stringWithFormat:@"ping -q -t 2 %@", hostName];
    int         ret = system([pph cStringUsingEncoding:NSASCIIStringEncoding]);
    return (ret == 0);
}
まだSwiftじゃなくてObjective-Cで書いてるけど、それはいいとして(さくっと書こうとするとやっぱりどうしてもObjective-Cになってしまう)、pingで反応があるかどうかで判断してる。system()というのはunixに古くからあるユーティリティ関数で、与えられた文字列をsh用のコマンドとみなしてシェルをfork/execする。返り値はシェルのexit statusの値で、シェルのexit statusは最後に実行したコマンドのexit statusの値、すなわちこの場合はpingのものになる。

macOSのmanページには
The ping utility exits with one of the following values:
     0       At least one response was heard from the specified host.
     2       The transmission was successful but no responses were received.
     any other value
             An error occurred.  These values are defined in <sysexits.h>.
1回でも応答があればexit statusは0になるので、その場合は生きているとみなす。ちなみに、sysexits.hにはこれに役に立つ情報はあまりない。

生きていればサーバソフトを起動する。それは
- (BOOL)invokeCommandServer:(NSString *)hostName
                    account:(NSString *)account
                 serverPath:(NSString *)serverPath
{
    NSString    *sshPhrase = [NSString stringWithFormat:
                              @"ssh %@@%@ %@ &"
                              account, hostName, serverPath];
    system([sshPhrase cStringUsingEncoding:NSASCIIStringEncoding]);
    
    sleep(1);   //  wait for server establishing
    return YES;
}
としている。sshPhraseという文字列は例えば
ssh pi@raspberrypi.local /home/pi/localServer &
などということになる。

つまりこれもさっきと同じでsshでログインして、サーバソフトを起動して終わる、というもの。sshはパスワードではなく公開鍵認証の設定をしてあるのでそのまま起動できる、というもの。

この文字列を手動でコマンドラインから入れれば動くのでなんの疑いもなくこれで起動しようとしたら、動かない。Xcodeのコンソールには
2 packets transmitted, 2 packets received, 0.0% packet loss
round-trip min/avg/max/stddev = 6.139/8.127/10.114/1.988 ms
load pubkey "/Users/decafish/.ssh/id_rsa": Operation not permitted
Host key verification failed.
socket connect: Connection refused
errno = 61
となって、pingは動いてるのに、sshが失敗してる。おっかしいなあ。パーミッションの設定や、ファイルの中身を調べたけどおかしなところはない。だって、手動だと動くんだもんな。 そもそもエラーメッセージの意味が理解できない。~/.ssh/id_rsaは秘密鍵で公開鍵でも共通鍵でもない。「load pubkey」は何を言いたいんだ?ググってもこんなメッセージはヒットしない。

なんてぐちゃぐちゃしてる間に半日がすぎてしまった。ついさっきふと気がついた。macOS側のアプリはSandbox化してる。なぜかNSOpenPanelやNSSavePanelが動かないという問題があって、試行錯誤の結果、Sandboxに入れるとなんとか動くところまではできた。しばらく前からそうなってしまっていてずっとそうしてるが、なぜSandbox無しではNSSavePanelが動かないか、Sandboxに入れても動かないとこがあるけど何がいけないのか、そもそもなんで僕だけそうなのか(僕以外でこういう症状はググっても見つからない。Sandbox内ではAppKitではなくPowerBoxのNSSavePanelが使われるとあるけど、AppKitそのものが変わってるとしか思えない)とか、よくわからないままになっている。GigEやUSB3.1接続のカメラはSandbox内でも通るように設定できるので、Sandbox化自身はこれまでそれほど不便はなかった。

今回も何も考えずにSandbox対応していた。ところがそうすると、Sandbox内からは無関係なファイルを書くことどころか読むことさえできない。したがってsshの認証に使う秘密鍵id_rsaファイルにアクセスできない。そのせいでsshが失敗していた。これに気がつくのに半日もかかった。sshのエラーメッセージが「id_rsa can not be read」とかだったらすぐ気がついたのに。

で、どうすればいいか。いくつか考えられる。
  1. Sandboxを使わない
  2. id_rsaファイルをユーザに指定させる
  3. id_rsaファイルをSandbox内からでも読める場所に置く
  4. パスワード(共通鍵認証)に切り替える
  5. サーバの起動をsshを使わずに行う
Sandboxに入れないとNSSavePanelがなぜか使えないので自分で実装するしかない。しかしそれは面倒。Sandboxに入れればNSOpenPanelが使えるので、まずOpenPanelを表示してid_rsaの場所を指定させる、というのはできる。しかしそんなの煩わしいし、sshがなんなのか知らない人も使うことになる予定なのでダメ。さらにSandbox内から読めるような、
  • ~/Downloads
  • ~/Pictures
  • ~/Music
  • ~/Movies
  • アプリ自身のBundle内
のどれかにコピーする、というのはもっとsshの思想に反するので、そもそもやってはいけない。またいまさらパスワードというのもイマイチだし、接続のたびにパスワードをいちいち入力するのはさらに煩わしい。

サーバをRaspbianの上でsystemdで起動しておくというのは、やりかたとしては正しい。というか、これまではそうしていた。しかしデバグの時点では、ホスト側がクラッシュしたときの起動し直しとか、依存関係を考えてsystemctlを書き直す、というのは面倒。しかもそういうのってすぐ忘れてしまって、動かなくなった、なんでだ、と騒いだり、systemctlってどうやって書いたのか思い出せなくて、manページをひっくり返して、そんなことをやってると半日や1日すぐ過ぎてしまう。

どうすればいいんだろう....
nice!(0)  コメント(0) 

nice! 0

コメント 0

コメントを書く

お名前:
URL:
コメント:
画像認証:
下の画像に表示されている文字を入力してください。

この広告は前回の更新から一定期間経過したブログに表示されています。更新すると自動で解除されます。