NYAGOS保守ノート:OpenSSH でパスワード入力中に Ctrl-C で中断すると画面が乱れる

これな

症状

nyagos から OpenSSH を起動して、パスワードプロンプトで Ctrl-C を押下すると、nyagos に復帰してこなくなる(ように見える)。

f:id:zetamatta:20190126103235p:plain

原因

これは実は nyagos には復帰しているのだが、コンソールのモードがパスワード表示向けの状態から元に戻っていないだけである。なので、画面が更新されないが「exit」と入力すると普通に終了する。

対応

コマンドを実行した後、コンソールの出力のモードをいちいち元に戻すようにした。入力のモードについては既に対応していたので、同じような対応を出力にもするようにした。

詳しくは差分みて

余談1

このコンソールのモードを変える関数(dos.ChangeConsoleMode)の仕様は我ながらなかなかうまいこと作れていると思う

if restore,err := dos.ChangeConsoleMode(windows.Stdout, dos.ModeSet(enableVirtualTerminalProcessing)) ; err == nil {
    defer restore()
}
  • モードを元に戻す関数(クロージャ)を戻り値に変えす。一時的にモードを変えるだけなら、これを defer で戻すようにすればよい
  • モードのセット・リセットは、dos.ModeOp というインターフェイスを実装した dos.ModeSet , dos.ModeReset という型をパラメータとして0個以上渡せばよい。
  • コンソールのハンドルはいちいち自分で "$CONIN" "$CONOUT" をオープンしなければいけないと思いがちだが、実は準標準の golang.org/x/sys/windowswindows.Stdout とか windows.Stdin がそのままあるので、これを使った方が早い。

余談2

なぜ、Ctrl-C で OpenSSH を中断すると、コンソールのモードを元に戻してくれないのか? beepcap さんによると、CMD.EXE や PowerShell 上では同症状は発生しなかったそうだ。

可能性としては以下の2説がある。

  • CMD.EXE や PowerShell はコマンド実行ごとにいちいちコンソールモードをリセットしている
  • Ctrl-C をキャッチした時の子プロセスの殺し方がまずい(SIGKILL相当)ので、OpenSSH が SIGINT 扱いでハンドルできない

nyagos の Ctrl-C の扱いについては、最近(go-tty導入前後)になってからうまく処理できなくなったので、 "os/exec".Run を参考にして最近実装しなおしたのだが…見直しが必要かもしれない。

Ctrl-C の取扱については、現在のように Go言語の "os/signal" の関数を使うほか、Windows のそれ用の API SetConsoleCtrlHandlerがある。 おいおいと調べなくては…