SSH してみる Part 3 (コマンド実行編)
今回は SSH 経由で リモートサーバ上のコマンドを実行する方法です。
SSH してみる Part2 でも
書いている通り 最終的な目標はサーバのモニタリングツールを作ることですが、
まずは vmstat を実行して その結果をそのまま画面に表示することからはじめてみましょう。
今回もサーバには Ubuntu 10.04 LTS を使っています。
サーバ にログインして vmstat をオプションなしで実行してみると
以下のような情報がコンソールに標準出力されます。
procs -----------memory---------- ---swap-- -----io---- -system-- ----cpu---- r b swpd free buff cache si so bi bo in cs us sy id wa 0 0 0 235708 24976 100720 0 0 6 3 13 31 0 0 100 0
これと同じことを JavaFX で実現してみましょう。
まずは表示する画面の作成から...
画面は 『SSH してみる (パスワード認証編)』 で作成したものをそのまま利用します。
もちろん パスワード認証部分も...
init 状態 (初期状態) の 真っ黒な画面に 識別子 "foreground" で ラベル を一つ追加します。
後は 『SSH してみる (パスワード認証編)』 で作成したコード中の
Alert.inform("Auth success");を以下のように変更するだけです。
もちろん、今回もエラー処理等は考慮していません。
// コマンドを実行するためのチャネルを開く def channel = session.openChannel("exec") as ChannelExec; channel.setCommand("vmstat"); channel.setErrStream(System.err); // TODO エラー処理 def in = channel.getInputStream(); // コマンドを送信する channel.connect(); // アプリケーション終了時にチャネルを閉じるためのフック FX.addShutdownAction(function(): Void { channel.disconnect(); }); try { def r = new BufferedReader(new InputStreamReader(in, "UTF-8")); // チャネルが開いている間 処理を続ける while (true) { // ブロックせずに読み込めるか確認 while (r.ready()) { // 一行分のデータを読み込み画面に表示 foreground.text = "{foreground.text}\n{r.readLine()}" } // チャネルが閉じていたら終了 if (channel.isClosed()) { break; } // 暫く待ってもう一度データを読み込む Thread.sleep(500); } } catch (e: InterruptedException) { // ignore }
ポイントは
- コマンドを送信する前に InputStream を取得しておく
- Reader#ready() で読み込み可能か確認する
1つ目ですが Channel#connect() を呼び出す前に InputStream を取得しておかないと
タイミングによっては サーバからの出力が受け取れないことがあるからです。というのも...
サーバからの出力を受け取るための InputStream は Channel#getInputStream() を実行したときに初めて生成されるのです。
その為 Channel#connect() して サーバ上で コマンドを実行する前に準備しておかないとダメなのです。
2つ目ですが BufferedReader で いつもの
var line: Object; while ((line = r.readLine()) != null) { ... }のように readLine() をいきなり呼んでしまっては readLine() でブロックされてしまいます。
その為 ready() を実行して ブロックされずに読み込めるか 確認してから読み込まないとダメなのです。
でないと サーバからの出力が返ってこない場合、永遠に readLine() で待たされてしまうことに...
今回はサンプルなので問題ないですが、通常 タイムアウト処理 や 再実行処理等 を ちゃんと実装しないといけないのでこれは重要です。