September 1, 2013

879 letters 2 mins read

Go言語:sshパッケージを使って対話型SSHクライアントを実装する

code.google.com/p/go.crypto/sshで提供されているsshパッケージを使って、標準のsshコマンドと同等の対話型SSHクライアントを実装してみた。

コードは以下。
https://github.com/inatus/ssh-client-go

API・コード例はGoDocに記載されている。
http://godoc.org/code.google.com/p/go.crypto/ssh

しかし、Sessionを作るところまでは例があるからいいとして、その先がよくわからない。
セッションを開始するメソッドと以下のようなものがあるようだ。

  • Run: コマンドを引数に指定。コマンドの実行が完了するまでWaitが掛かり、結果はSessionのStdio、Stderrに出力される。
  • Start: コマンドを引数に指定。コマンドの実行完了を待たずに先に進む。結果はSessionのStdio、Stderrに出力される。つまり、Startメソッドを実行した直後には結果が出力されない。
  • Shell: コマンドを実行せずにシェルを開始する。
  • Output: コマンドを引数に指定。コマンドの実行が完了するまでWaitが掛かり、結果が戻り値に返る。
  • CombinedOutput: コマンドを引数に指定。コマンドの実行が完了するまでWaitが掛かり、結果が戻り値に返る。標準出力に加え標準エラー出力が返る。

また、Sessionに対してこれらのメソッドは一度しか実行できない。
これらのメソッドの中で、Shell以外は非対話型的な実行を想定していると思われる。したがって、Shellを用いる。

Shellメソッドを実行すると、ログイン直後の出力がStdioに出力される。これをOSの標準出力に出力するようにした。

session.Stdout = os.Stdout 
session.Stderr = os.Stderr

次に、ログイン後のコマンド受付処理を実装する。コマンドはStdinに受け付けられる。端末の標準入力から入力されたコマンドをStdinPipeを使ってSessionのStdinに伝える。

in, _ := session.StdinPipe()

for { 
    reader := bufio.NewReader(os.Stdin) 
    str, _ := reader.ReadString('n') 
    fmt.Fprint(in, str) 
}

この際、以下に注意する。

  • StdinPipeはセッションを開始する前に取得すること。
  • コマンドの末尾には改行コード(n)を付けること。

これで通常のsshコマンドと同等な対話型SSHクライアントが構築できる。
ただし、パスワードのマスクやテキストエディタの起動などがうまくできない。