Kernel/VM Advent Calendar 33日目: VirtualBoxでホストOSからゲストOSのコマンドを実行する

VBoxManage guestcontrol exec

VMwareのvmrunやUser Mode Linuxのumlrunを使うとホストからゲストのコマンドを叩けるのですが、VirtualBoxにも同じような仕組みが無いかなーと思ったら、ありました。

Chapter 8. VBoxManage


execute, which allows for executing a program/script (process) which is already installed and runnable on the guest. This command only works while a VM is up and running and has the following syntax:


VBoxManage guestcontrol | exec[ute]
--image
--username --password
[--dos2unix]
[--environment "= [=]"]
[--timeout ] [--unix2dos] [--verbose]
[--wait-exit] [--wait-stdout] [--wait-stderr]
-- [[] ... []]

「--image」の後にコマンド名を入れて、あとはゲストOS上のユーザ名/パスワードを打ち込めば良さそうです。

というわけで、MacホストからLinuxゲストのコマンドを叩いてみたいと思います。

やってみた

実験環境はこんな感じです。

ハードウェア Mac mini, Intel Core 2 Duo(2.53GHz)
ホストOS Mac OS X 10.6
VMM VirtualBox 4.1.4
ゲストOS Linux 3.1.6 (x86_64) (Fedora16)

まずはゲストOSにGuest Additionsを入れる必要があります。
VMのメニューの「デバイス」->「Guest Additions のインストール」でインストール用の仮想デバイスをゲストOSに認識させた後、

GuestOS:# /media/VBOXADDITIONS_4.1.4_74291/VBoxLinuxAdditions.run

とやると、Guest Additionsがインストールされます。(カーネルモジュールのコンパイルも含まれるので事前にkernel-develやgcc等のパッケージを入れておきましょう)

準備はここまでで、早速ホストからゲストのコマンドを叩いてみましょう。

HostOS:$ VBoxManage guestcontrol fed16-64-vbox1 exec --image /bin/ls --username hoge --password hogehoge
HostOS:$

あれ。へんじがない、ただのしかばねの(ry
マニュアルをよく見ると、「--wait-stdout」を付けないと標準出力に出された結果を受け取れないようですね。

HostOS:$ VBoxManage guestcontrol fed16-64-vbox1 exec --image /bin/ls --username hoge --password hogehoge --wait-stdout
bin
boot
dev
etc
home
...
HostOS:$

今度はうまくいきました。

他には、引数を渡すための「--」くらいは覚えておくと良さそうですね。

誰がゲストのコマンドを実行してるの?

ちょっと気になったので調べてみました。

最近のLinuxにはftraceという便利なトレースツールが組み込まれています。今回はこれを使ってゲストのコマンドを実行しているプログラムを探してみましょう。

ftraceはdebugfsのインタフェースで操作します。debugfsがマウントされていない場合は、下のようにしてマウントしましょう。

GuestOS:# mount -t debugfs none /sys/kernel/debug
GuestOS:# cd /sys/kernel/debug/tracing/

ftraceには色んな機能があるのですが、今回はEvent Tracingというものを使います。これは、カーネル内に埋め込まれたトレースポイントの情報を取ってくる仕組みです。
イベントはたくさんあるのですが、今回はプロセスのコンテキストスイッチのイベント「sched_switch」とfork()のイベント「sched_process_fork」を採取しましょう。
とりあえずこの2つがあると、「sched_switch」イベントからは、ホストから実行されたコマンドのPIDが分かり、「sched_process_fork」イベントからは、そのコマンドのためのプロセスをforkした親プロセスが分かります。

イベントをONにするには、対応する疑似ファイルに1を書き込みます。デフォルトではこれでトレースが始まります。

GuestOS:# echo 1 > events/sched/sched_process_fork/enable
GuestOS:# echo 1 > events/sched/sched_switch/enable

ここでホストOSからコマンドを実行してみましょう。

HostOS:$ VBoxManage guestcontrol fed16-64-vbox1 exec --image /home/hoge/my_command --username hoge --password hogehoge

実行したら、ゲストのトレースをいったん止めます。

GuestOS:# echo 0 > tracing_on

トレースの結果はtraceファイルに入っているので、grepしてみましょう。

GuestOS:# grep my_command trace
      my_command-1850  [000]  3146.618841: sched_switch: prev_comm=VBoxService prev_pid=1850 prev_prio=120 prev_state=R ==> next_comm=VBoxService next_pid=1849 next_prio=120
...

my_commandはPID 1850で実行されているようです。これをfork()したのは誰かというと、、、

GuestOS:# grep sched_process_fork trace
...
     VBoxService-1849  [000]  3146.615976: sched_process_fork: comm=VBoxService pid=1849 child_comm=VBoxService child_pid=1850
...

どうやら、VBoxService(PID 1849)のようです。

おわりに

というわけで、VirtualBoxでホストからゲストのコマンドを実行するguestcontrol execを簡単に使ってみました。
ゲスト側で実際にコマンドを実行しているVBoxServiceを辿っていけば、ゲスト-ホスト間の通信路がどうなっているのか等、さらに詳しいことも分かりそうですね。

それではカーネル/VMな皆様、今年もよろしくお願いいたします。