更新日: $Date: 2005-11-20 08:29:46 $ UTC ($Revision: 1.4 $)
公開日: 2004/05/09
リモートでマシンを管理する際にシリアルコンソールがあると心強いです。 特にネットワーク関連のものをいじるとき −パケットフィルタリングの設定、IPアドレスをつけ変える、NICのドライバを入れ替える、NICが壊れたなど− にはシリアルコンソールがないと一発勝負もしくは不可能なことが多いですし、 他にもkernel入れ替えたら起動しなくなった、rebootしたらfsckで引っ掛かってしまったなど、ネットワークが有効になる前のトラブルにもシリアルコンソールならば対応できます。
また、 アクセスパスがネットワーク経由(SSH)ともう一つ確保できるというのは片方に障害が発生したときの代替手段にもなり冗長性が増しますし、 リモートではなくすぐそばにマシンがある場合でも、モニタとキーボードを繋げる必要がないのでシリアルコンソールは便利です。
シリアルコンソールを使いたいマシンが沢山ある場合、 専用の箱もののコンソールサーバーを導入するとか、 PCにマルチシリアルカード (cycladesの製品が有名ですが、 玄人志向のSERIAL4P-LPPCIは4ポートですが安価なので購入しやすいと思います) を挿してconserverなどで管理するのがよく行われると方法だと思いますが、 もっと単純に、2台ペアずつシリアルクロスケーブルで繋ぐだけでも安心感は大分高まるのでお奨めです。
通常状態 (initが上がってgettyがシリアルデバイスをつかんでいる状態) でログインできる他、以下のようなこともできます。
最初の二つについては、
Remote Serial Console HOWTO
( JFの日本語訳 )
に詳しく書かれているのでそちらを参照してください。
ファイル転送は、「シリアル」「zmodem」「sz」「rz」などをキーワードに検索すればその方法が見つかると思います。
他に、PCでもハードウエアによってはBIOSの操作もシリアル経由で行えるものもあります。(DELLのサーバークラスの製品のConsole Redirect機能とか)
世の中にはUSBシリアル変換器というものがあります。
大抵のPCではシリアルポートは背面についていてPCの配置によっては手が届きづらいところにある一方で、USBポートは全面についていることが多いです。しかもUSBは抜き差しOKです。
このメモは、「背面のシリアルポートに挿すのが面倒」という理由だけで、 USBポートにUSBシリアル変換器を挿してシリアルコンソールの受け口にした際のメモです。
方針は以下の通りです。
動作環境は以下の通りです。
シリアルコンソールへ繋ぐマシンでは jerm を使いました。機能がシンプルで、*BSD、Linux、Mac OS XなどいろいろなOSで使えます。
LinuxのVS-60Rのドライバはpl2303などdrivers/usb/serial/にあるものではなく、
drivers/usb/serial/cdc-acmです。
cdc-acmも含め、USB関連のドライバは全てmoduleにしました。
(組み込みにするとブートメッセージをUSBポートに出力できるようになりますが、今回は必要なかったのでモジュールにしました)
Device Drivers ---> USB support ---> <M> Support for Host-side USB (snip) <M> EHCI HCD (USB 2.0) support <M> OHCI HCD support (snip) <M> USB Modem (CDC ACM) support
kernelをコンパイルして再起動後、
USBシリアル変換器を挿すと自動的にcdc-acmがロードされて変換器が認識されました。ちょっと拍子抜け。
(/lib/modules/modprobe.conf
にalias char-major-166 cdc_acm
と書いてあるからかな?)
cdc-acmのデバイスは/dev/ttyUSB0
じゃなくて/dev/ACM0
であることに注意です。
変換器が認識されたらログインできるようにinittabを書き換えます。
ここらへんはデバイス名が違うぐらいでシリアルもUSBも同じなので、
不馴れならば
Remote Serial Console HOWTO
( JFの日本語訳 )
を読んでおくことをお奨めします。
/etc/inittab
に次の通り追加します。
T2:23:respawn:/sbin/getty -L ttyACM0 9600 vt100
追加したらtelinitを使ってinittabを再読み込みします。
# /sbin/telinit q
psでttyACM0で待ち受けるgettyが確認できればOKです。
$ ps axww|grep getty 4280 tty1 Ss+ 0:00 /sbin/getty 38400 tty1 4290 tty2 Ss+ 0:00 /sbin/getty 38400 tty2 4300 tty3 Ss+ 0:00 /sbin/getty 38400 tty3 4310 ttyS0 Ss+ 0:00 /sbin/getty -L ttyS0 9600 vt100 4899 ttyACM0 Ss+ 0:00 /sbin/getty -L ttyACM0 9600 vt100
受け側の設定ができたので早速繋いでみます。
接続元のマシンのシリアルポートにクロスのシリアルケーブルを繋げ、 その先にUSBシリアル変換器、受け側のマシンを繋げます。
接続元 受け側 ┏━━━━━━┓ (RS-232C) (USB) ┏━━━━━━┓ ┃ [D-sub25]--------------<変換器>==============[USB] ┃ ┗━━━━━━┛ ┗━━━━━━┛ jerm ───────────────→ getty minicom kermit
接続元のマシンで、/dev/ttyS0
への読み書き権限があるのを確認して、
jerm
で繋ぎます。
$ jerm /dev/ttyS0 Jerminal v0.8091 Copyright (C) 2000, 2001, 2002, 2003, 2004 candy Type "Ctrl-M ~ ." to exit. ispeed 13 ospeed 13 -IGNBRK -BRKINT -IGNPAR -PARMRK -INPCK -ISTRIP -INLCR -IGNCR -ICRNL -IXON -IXOFF -IXANY -IMAXBEL -OPOST +ONLCR -TABDLY cs8 -CSTOPB +CREAD -PARENB -PARODD +HUPCL +CLOCAL -CRTSCTS +ECHOKE +ECHOE -ECHO -ECHONL -ECHOPRT +ECHOCTL -ISIG -ICANON -IEXTEN -TOSTOP -FLUSHO -PENDIN -NOFLSH
で、リターンキーを押すとログインプロンプトがでるはずなんですが…
host login: inux 3.0 host ttyACM0
ん? なんか変です。本来ならば、
Debian GNU/Linux 3.0 host ttyACM0 host login:
と表示されるはずなんですが…
どうも行末でCRLFじゃなくてCRしか送られてこないのが原因で、
カーソルが行頭に戻るだけで改行されていないようです。
jermの他に、
minicom、
C-Kermit、
Taylor UUCPのcu
も試してみましたがどれも同じでした。
接続元、接続先の両方でsttyでいろいろ(opostとか[oi]nlcrとか)設定したみましたがどれも効果なしでした。
ネットで検索してみると、linux-usb-devel MLで既に度々問題になっていたようなのですが、どうも直すのは難しくて放置してあるようでした。
仕方がないので接続に使うツールの側で対応することにしました。
まず、
C-KermitにはCRをCRLFに変換する機能
(set terminal cr-display crlf
)
があるのでそれを使えばOKでした。
$ kermit -l /dev/ttyS0 set terminal cr-display crlf set carrier-watch off connect
次に、
jerm
にはそのような機能はないのですが、クイックハックで機能追加してみました。
実行時に-r
オプションを指定するか、
実行中に~r
で行末(CRとLF)をCRLFに変換するようになります。
参考までにjerm-v0.8092に対するパッチを置いておきますが、 次期バージョンではこの機能は取り込まれるかもしれません。
jerm-v0.8093から行末文字の変換機能がつきました。-rオプションで制御することができます。
今回のように受け取ったCRをCRLFに変換するには、
jerm -r tnrn /dev/ttyS0
とすればOKです。
これでUSBポートのシリアルコンソールへ接続できるようになったのですが、
いつもはUSBシリアル変換器は抜いておいて、
必要なときだけ変換器を挿して使いたいです。
しかし、USBシリアル変換器を抜いた状態ではgettyがデバイス(/dev/ttyACM0
)にアクセスできないのでsyslogに延々とエラーメッセージが出力されてしまいます。
気にしなければいいのかもしれませんが、気持が悪いのでちと考えてみます。
エラーメッセージが出ないようにするには、inittabを書き換えて再読み込み
(telinit q
)
すればよいわけです。
つまり、
USBシリアル変換器が挿されたときにinittabに/dev/ttyACM0
をgettyする行を追加して、
抜かれたら追加したものを削除すればよいです。
そのためには抜挿のイベントをフックしてスクリプトを実行すればいいはずです。 そこでイベントを取得する方法を考えてみました。
まず思いついたのはUSBドライバのモジュールがロードされたときとアンロードされたイベントです。
kernel 2.4時代のmodutilsでは/etc/modules.conf
にpost-install
やpre-remove
といった設定ができました。
しかし新しいmodule-init-toolsではこれらの設定項目はなくなってしまったので、
同じことを実現するのはちょっと面倒になりました。
例えば、モジュールがロード/アンロードされたときに/etc/inittab
のシンボリックリンクを張り替えてtelinit q
するには次のようにmodprobe.conf
に書きます。
install cdc_acm { ln -sf inittab-acm /etc/inittab && /sbin/telinit q; }; /sbin/modprobe --first-time --ignore-install cdc_acm remove cdc_acm /sbin/modprobe -r --first-time --ignore-remove cdc_acm && { ln -sf inittab-normal /etc/inittab && /sbin/telinit q; }
さて、モジュールのロードは自動でなされるのでいいのですが、
問題はモジュールのアンロードです。
USBシリアル変換器を抜いた後でも、モジュールはロードされっぱなしです。
modutilsのrmmodでは-aオプションをつければ使っていないモジュールを見つけて削除してくれるのでcronで定期的に実行すればよかったのですが、
module-init-toolsではrmmodからは-aオプションがなくなりモジュール名の指定が必須になりました。
また、(rmmodも動作しますが)rmmodではなくmodprobe -rを使うようになりました。
従ってmodprobe -r cdc-acmを定期的に実行すればいいのですが、
その殆んどが無駄な実行となりますし、
変換器を抜いてからしばらくはアンロードされない(エラーメッセージが出続ける)のでちといまいちな方法です。
どうしたもんかなぁといろいろ調べていると、
hotpluggingというものを見つけました。
全く知らなかったのですが、kernel 2.4からの機能で、USBに関してはkernel 2.2にもバックポートされているようです。
この機能を使えばドライバモジュールのロード/アンロードのタイミングではなく、より確実なデバイスの抜き挿しのタイミングで設定を切り替えることができます。
hotpluggingのサイトのドキュメントや、/etc/hotplug/*
、/etc/hotplug.d/*
にあるスクリプトを読んでみて、関係のある個所のみ抜き出すと以下のような動作になるようです。
/proc/sys/kernel/hotplug
で指定されているプログラム、
/sbin/hotplug
が実行され、/sbin/hotplug
が…
/etc/hotplug.d/usb/*.hotplug
を実行する。/etc/hotplug.d/default/*.hotplug
を実行する。/etc/hotplug.d/default/default.hotplug
が実行され、
default.hotplug
が…
/etc/hotplug/usb.agent
を実行する。
/etc/hotplug/usb.agent
が…
/lib/modules/`uname -r`/modules.usbmap
を元に合致するデバイスを探すが見つからない。
/etc/hotplug/usb.usermap
、
/etc/hotplug/usb/*.usermap
、
を元に合致するデバイスを探すが見つからない。/etc/hotplug/hotplug.functions
で定義されている
load_drivers
が実行され…
/etc/hotplug/usb.agent
で定義されている
usb_map_modules
が実行され、ドライバを探す。
(見つからない場合は直ぐにreturnする)
/etc/hotplug/usb/MODULE
という実行ファイルがある場合は実行する。
/etc/hotplug/usb.agent
を実行する。
/etc/hotplug/usb.agent
が…
従って、以下のことをすれば抜挿のタイミングで任意のプログラムを実行できることがわかりました。
/etc/hotplug/usb.usermap
(もしくは/etc/hotplug/usb/*.usermap
)
にUSBシリアル変換器の定義を追加する。
/etc/hotplug/usb/cdc-acm
を用意する。
これは挿したときに実行されるファイルとなる。
まずは/etc/hotplug/usb.usermap
です。
ドキュメントや他のマップファイル/lib/modules/`uname -r`/modules.usbmap
を見てみると、この形式はモジュール名で始まる全13カラムで1行1レコードのようです。
そしてこのマップファイルを扱うusb_map_modules
を読んでみると、
必ずしも全てのカラムを記述する必要はなく、第2カラムのmatch_flagsでどのカラムまでマッチすれば見つかったことにするか制御できるようです。
今回はベンダーIDとプロダクトIDの二つで判定するようにしました。
ベンダーIDとプロダクトIDはlsusbや/proc/bus/usb/devices
で確認することができます。
# lsusb (snip) Bus 003 Device 006: ID 05db:0012 Device Descriptor: bLength 18 bDescriptorType 1 bcdUSB 1.10 bDeviceClass 2 Communications bDeviceSubClass 0 bDeviceProtocol 0 bMaxPacketSize0 8 idVendor 0x05db idProduct 0x0012 bcdDevice 1.00 iManufacturer 17 Sun Corporation SCC div. iProduct 18 SUNTAC USB Device iSerial 19 USB-COM (snip) # less /proc/bus/usb/devices (snip) T: Bus=03 Lev=01 Prnt=01 Port=01 Cnt=01 Dev#= 6 Spd=12 MxCh= 0 D: Ver= 1.10 Cls=02(comm.) Sub=00 Prot=00 MxPS= 8 #Cfgs= 1 P: Vendor=05db ProdID=0012 Rev= 1.00 S: Manufacturer=Sun Corporation SCC div. S: Product=SUNTAC USB Device S: SerialNumber=USB-COM (snip)
そして3番目のカラム(プロダクトID)までを判定に使うように
match_flagsを3に指定して、次のような行を
/etc/hotplug/usb.usermap
に追記します。使わないカラムは0で埋めておきました。
# module match_flags idVendor idProduct bcdDevice_lo bcdDevice_hi bDeviceClass bDeviceSubClass bDeviceProtocol bInterfaceClass bInterfaceSubClass bInterfaceProtocol driver_info cdc-acm 0x0003 0x05db 0x0012 0x0000 0x0000 0x00 0x00 0x00 0x00 0x00 0x00 0x00000000
次に/etc/hotplug/usb/cdc-acm
です。
これはshellスクリプトで、以下の機能を実装します。
add
になるのでそれを判定に使う。
idVendor/idProduct/bsdDevice
」
となっている。
/etc/inittab
を書き換えて再読み込みする。
注意が必要なのはRMOVERスクリプトで、
デバイスが抜かれたときにusb.agent
がREMOVERスクリプトを実行してくれるのですが、実行した後に削除してしまうので、
挿されたタイミングで毎回生成する必要があります。
REMOVERスクリプトのパスは複雑なのですが、
/etc/hotplug/usb/cdc-acm
が実行される時点でパスが変数REMOVERに格納されているので、そこへスクリプトの内容をリダイレクトすればよいです。
以下にcdc-acm
を示します。
#! /bin/sh # # $Id: usb-console.html,v 1.4 2005-11-20 08:29:46 hirose31 Exp $ # cd /etc/hotplug . ./hotplug.functions # DEBUG=yes export DEBUG if [ "X${ACTION}" != "Xadd" ]; then debug_mesg "ABORT: invalid action ${ACTION}" exit 1 fi case $PRODUCT in 5db/12/*) if $MODPROBE cdc-acm >/dev/null 2>&1; then debug_mesg "succeed in ${MODPROBE} cdc-acm" cp -f /etc/inittab /etc/inittab.$$ modlabel='cdc-acm' if ! grep -q "^#>>${modlabel}" /etc/inittab.$$; then cat <<EOFINITTAB >> /etc/inittab #>>${modlabel} T2:23:respawn:/sbin/getty -L ttyACM0 9600 vt100 #<<${modlabel} EOFINITTAB fi /sbin/telinit q mv -f /etc/inittab.$$ /etc/inittab.orig debug_mesg "cdc-acm: generate ${REMOVER}" cat <<EORMOVER >${REMOVER} #! /bin/sh cp -f /etc/inittab /etc/inittab.\$\$ cat /etc/inittab.\$\$ | awk ' /^#(>>|<<)${modlabel}/ { skip = ! skip ; next } skip == 0 { print } ' >/etc/inittab /sbin/telinit q mv -f /etc/inittab.\$\$ /etc/inittab.orig exit 0 EORMOVER chmod +x ${REMOVER} else mesg "ABORT: failed to ${MODPROBE} cdc-acm" exit 1 fi ;; *) mesg "ABORT: unknown product: ${PRODUCT}" exit 1 ;; esac exit 0
これでデバイスを挿したらUSBポートのシリアルコンソールを準備し、 抜いたら元に戻すようになったのですが、2つ問題が発生しました。
最初にUSBシリアル変換器を挿して抜いたときは首尾よく機能するのですが、 再度、変換器を挿すとkernel oopsが出て以降ずっとデバイスにアクセスできなくなってしまいました。
ネットで調べたところ、どうもこれはkernel 2.6.5のバグで、2.6.6-rc1あたりで修正された問題のようでした。 そこでkernel 2.6.6-rc3に入れ替えたところ、この問題は解決しました。
USBシリアル変換器を挿すとhotplugが2つ実行されてしまい、
結局、cdc-acm
も2つ実行され、REMOVERスクリプトも2つできてしまいます。
どうもインターフェースが複数ある場合はその数だけ実行されてしまうようです。
なので/etc/hotplug/usb/cdc-acm
やREMOVERスクリプトでの処理は競合しても大丈夫なように書く必要がありますし、場合によっては排他制御が必要になるかもしれません。
先の例でも、このせいでinittab.origが処理前のオリジナルにならないことがありますが、inittabの書き換え自体には問題ないように実装しているのでまぁいいかと放置してあります。