2007年04月17日

UML のコンソールを screen に表示させる 〜疑似端末を screen に結びつける

はてなブックマークに登録

UML(User Mode Linux)では,ゲストOS上のコンソールデバイスのバインディング先として,色々なものが選べます.先日公開した LVS の実験パックでは,ホストOS上の(空き)コンソールデバイスに割り付けていました.この方法はお手軽なのですが,反面ホストOSのコンソールにアクセスできる環境じゃないと,ゲストOSのコンソールにもアクセスできなくなってしまいます.折角の疑似環境なのに,ハードウェアに縛られるのはあまり嬉しくありません.ということで,私がUMLを使うときには,コンソールのバインディング先を(ホストOSの)疑似端末デバイスにして,この疑似端末と screen を結びつけて使っています.

疑似端末とは

本題に入る前に,疑似端末デバイスとはどういうものか,ということを説明します.

UNIXの眷属に於いて言うところの端末デバイスとは,例えば皆さんが普段お使いのディスプレイとキーボードのセットの事です.抽象化すると,主にユーザとのインタフェースに用いるための入出力装置,と言ったところでしょうか.つまりユーザと直接やりとりしたいプログラムが,入力を受け取り,出力を送り出すためのデバイスになります.端末デバイスを使う代表的なプログラムはシェルや,X Window System になります.

疑似端末デバイスは,端末デバイスの一種です.先ほどの説明では端末デバイスは物理的なデバイスでした.しかし,シェルは常にディスプレイとキーボードから使われるわけではありません.ネットワークからログインしたときにも使われます.このように,入出力先が直接接続されたデバイス以外の場合に,シェルなどの端末デバイスを使うプログラムが普通の端末デバイスと同じ作法で入出力するための相手として,疑似端末デバイスが用いられます.

master と slave

疑似端末デバイスに対してシェルが読み書きしたデータはどこから来てどこに行くのでしょうか.これは,例えば ssh を使ってログインし,起動されたシェルの場合は,ssh のデーモンになります. では,ssh のデーモンはどうやってシェルとデータのやりとりをするのでしょうか.

ssh のデーモンは,ログインを受け付けると疑似端末デバイスを開きシェルを起動します.この疑似端末デバイスにはペアになるデバイスが用意されています.ペアの一方の疑似端末デバイスに対して書き込まれたデータは,もう一方のデバイスから読み出すことができます.この疑似端末デバイスのペアは対称的ではなく,各々役割が決まっています.シェルが使う側の疑似端末デバイスを master,sshd が使う側のものを slave と呼びます.

2種類の疑似端末デバイス

Linux では現在2種類の疑似端末デバイスが使えます."Legacy (BSD) PTY" と,"Unix98 PTY" です(UML も screen も,いずれのタイプの疑似端末デバイスも扱えます).違いは,前者の場合 master 用と slave 用のデバイスファイルを /dev/ の下に,使う予定の数の分だけ固定的に作成しておかなくてはならないのに対して,後者の場合は,master 用のデバイスファイルは /dev/ptmx 固定で,slave 用のデバイスファイルは /dev/pts/ の下に,masterデバイスが open(2)されたときに動的に作成されます.(疑似端末に関しては,pty(7)やpts(4)に詳しく記載されています.)

UML で疑似端末デバイスを使う

さて,UML でゲストOSのコンソールデバイスをホストOSの疑似端末デバイスに割り付けるには,起動オプション(con=)で指定します.

  • "Unix98 PTY"を使う

 $ ./linux ubd0=disk0.img con0=fd:0,fd:1 con=pts

とします(con0=fd:0,fd:1 を指定することで,ゲストOSの起動メッセージを UML を起動した端末上に表示させています.こうしないと次のメッセージが見えません).ゲスト OS でコンソールデバイスが open されるタイミングで,ゲストOSのカーネルメッセージとして
 Virtual console 1 assigned device '/dev/pts/13'
 Virtual console 2 assigned device '/dev/pts/14'

の様なメッセージが出てきます.("/dev/pts/13" は slaveデバイスです.)

  • "Legacy (BSD) PTY"を使う

 ./linux ubd0=disk0.img con0=fd:0,fd:1 con=pty

とします.同様に,ゲスト OS でコンソールデバイスが open されるタイミングで,ゲスト OS のカーネルメッセージとして
 Virtual console 1 assigned device '/dev/ptyp0'
 Virtual console 2 assigned device '/dev/ptyp1'

の様なメッセージが出てきます.注意しないといけないのは,/dev/ptyp0 は(何故か slave ではなく) master 側のデバイスファイル名だということです.screenと結びつけるべき slave側のデバイスファイルは,ptyp0 の頭の p を t に変えたものになります.即ちこの例では /dev/ttyp0 になります.

UML と screen

疑似端末の slave を screen に結びつける

screen(1)コマンドは,起動時にコマンドラインを何も指定しなければ $SHELL で指定されたプログラムを起動します.コマンドラインにプログラムを指定すれば,$SHELL の代わりにそのプログラムが起動されます.いずれの場合でも,疑似端末デバイスを使って,シェルとやりとりをします.

コマンドラインでプログラムを指定する代わりに,疑似端末の slave側のデバイスファイルを指定すると,screenは自分で疑似端末デバイスを用意せずに,指定された slave のデバイスファイルを開きます.即ち,先程の UML の起動例で UML が用意した疑似端末デバイスを screen で開くには

 $ screen /dev/pts/13

あるいは
 $ screen /dev/ttyp0

とします.すると,ゲストOSの tty1 のログインプロンプトが表示されます(何も表示されてなければ,リターンキーを一度たたいてください).

複数の slave を一つの screen のセッションで扱う

screen の起動オプションで,UML が用意した疑似端末の slave デバイスを指定する方法では,1つの screen のセッションで,1つのゲスト OS のコンソールしか開けません.一方screen の特徴は,1つのセッションで複数のウィンドウを管理できることです.同じゲスト OS が開いた疑似端末は全て同じセッションで扱えれば,1つのゲストOSに対して1つのセッションで済ますことができ,とても便利です.

これは,既存の screen セッションに対して,screen セッションの外部から新たなウィンドウを開いてやることで実現できます.これには次の様にします.まず新しく screen のセッションを作るときに,セッションの名前を明示的に指定します(-S uml).

 $ screen -S uml /dev/pts/13

次に,別の端末上から
 $ screen -S uml -X screen /dev/pts/14

とします.これで,同じ screen のセッションの上に,ゲストOSの tty1 と tty2 のためのウィンドウが作成されます.uml という名前の screen に attache して,ウィンドウを切り替えれば,tty1 のログインプロンプトと tt2 のログインプロンプトが表示されます.

UML の起動と screen のウィンドウ作成を自動化する

複数のコンソールをゲストOSが開く場合,1つずつ手で screen のセッションを追加するのは,結構手間です.この辺を自動化してみましょう.まず,次のリストを startuml というファイル名で保存します.

 #!/bin/sh
 ./linux umid=$name       \
         ubd0=./disk0.img \
         con0=fd:0,fd:1   \
         con=pts          \
 | awk -v name=$name '
 /^Virtual console [0-9]+ assigned device /{
         pty=$6;
         gsub("'\''", "", pty);
         sub("pty", "tty", pty);
         system("screen -S " name " -X screen -t tty" $3 " " pty);
 }
 {
         print $0 "\r";
 }'

これを使って,次のようにして screen を起動します.
 $ export name=uml; screen -dmS $name /bin/bash startuml

これで,uml という名前の screen セッションが作成され,0番目のウィンドウ上で UML が起動し,UML が開いたコンソールに対応する各疑似端末の slave デバイスを,screen の uml セッション上のウィンドウに割付けます.

少し解説すると,セッションを作成している screen コマンドは

 screen -dmS $name /bin/bash startuml

です.-dm オプションを付けることで,screen を detach した状態で起動します.はじめから attach した状態で起動したければ,-dmS の代わりに -S を指定してください.この screen はコマンドラインで実行ファイル(/bin/bash)が指定されているので,0番目のウィンドウで,指定されたコマンド /bin/bash startuml を実行します.

startuml はシェルスクリプトです.わざわざシェルスクリプトにしているのは,screen のコマンドラインで指定したプログラムは,シェル経由で実行されるのではなく screen が直接 exec(2) するので,パイプが使えないためです.

startuml の中では,UML を起動して,その出力を後段の awk コマンドにパイプで送っています.この awk コマンドは UML の出力を監視して,UMLの中でコンソール開かれた際に,そのコンソールに対応する疑似端末の slave デバイスを,screen のウィンドウに割り付けるためのものです.screen のウィンドウに割り付けるのは

 system("screen -S " name " -X screen -t tty" $3 " " pty);

の部分でしています.name$3pty は awk の中での変数です.それ以外の " で囲まれた部分は文字列です.name 変数は awk の起動時にオプション(-v name=$name)で設定されています(後ろの $name は環境変数で,これは export name=uml で設定しています).$3 には UML が出力するメッセージ,例えば
 Virtual console 1 assigned device '/dev/pts/13'

の行の,3番目の単語である "1" が入っています.pty 変数には,system()関数の前で $6 変数の内容='/dev/pts/13' が代入され,加工(gsub, sub)されています. 結局のところ,system() 関数に渡される文字列は,
 screen -S uml -X screen -t tty1 /dev/pts/13

になります.この screen コマンドのオプションの -t tty1 は,新しく開くウィンドウのタイトルを指定しています.

screen と端末の行数・桁数の不一致

さて,screen のウィンドウに表示したゲストOSのコンソールにログインして,vi 等を使ってみると,screen が表示できる行数・桁数よりも小さな領域でしか,出力がされないことに気づくかもしれません.これは screen の実際のサイズとゲストOSの端末に設定されたサイズが食い違っているために起こります.ゲストOSの端末のサイズを screen の実際のサイズに合わせるには,resize コマンドを実行します.(resize コマンドは Debian Sarge では xutils パッケージに,Etch では xterm のパッケージに入っているようです.)

 $ eval $(/usr/bin/X11/resize)

ただし,これはあくまで現在の screen のサイズに合わせてくれるだけなので,screen の大きさが変わったときにはもう一度 resize コマンドを実行する必要があります.これを自動化するには,ちょっと強引ですが,.bashrc あたりに

 PROMPT_COMMAND='eval $(/usr/bin/X11/resize)'

と設定しておきます.こうすると,bash のプロンプトが表示される度に resize コマンドが実行されるので,概ね自動で追随してくれます.但し,bash 限定(?)ですが…(工夫すれば,他のシェルでも同様の事はできると思います).
# もう少しスマートな解決方法がありましたら,コメント頂けるとありがたいです m(_ _)m

klab_gijutsu2 at 10:57│Comments(0)TrackBack(0)運用 

トラックバックURL

この記事にコメントする

名前:
URL:
  情報を記憶: 評価: 顔   
 
 
 
Blog内検索
Archives
このブログについて
DSASとは、KLab が構築し運用しているコンテンツサービス用のLinuxベースのインフラです。現在5ヶ所のデータセンタにて構築し、運用していますが、我々はDSASをより使いやすく、より安全に、そしてより省力で運用できることを目指して、日々改良に勤しんでいます。
このブログでは、そんな DSAS で使っている技術の紹介や、実験してみた結果の報告、トラブルに巻き込まれた時の経験談など、広く深く、色々な話題を織りまぜて紹介していきたいと思います。
最新コメント