ブラウザだけで様々な言語のコードを即時実行できる、超便利なオンラインプログラミング環境 paiza.IO。前の記事で C# で実行環境を調査するという遊びをやっていました。
今回はその延長として、paiza.IO の実行環境を覗いて遊ぶコードを 4 つ紹介します。「エンジニアたるもの、コードが動くならその中を覗き込まないワケには行かない!」という謎発想で作ったのが以下のサンプルコードです。Main 関数にある 4 つのメソッドはそれぞれ独立して動きますので、必要に応じてコメントを設定/解除してご利用ください。
1. ファイルシステム内を探検
paiza.IO のソースコードは Docker 上の Ubuntu 環境で実行されます。私は Docker に詳しくありませんが、どうやら Docker は Hyper-V などの仮想マシンと違ってホスト OS を共有するようです。ということはファイルシステムも共通と思われ、もしファイルシステムを覗くことができればそれはホスト OS のもの!というウハウハ気分ががが...(ジュルリ (※この認識は間違ってるかもしれない)
ファイルシステムは以下のようなコードで覗くことができます。メソッドの引数にフォルダパスを与えればその階層にあるファイルとフォルダを出力画面に表示してくれます。ファイルの場合は先頭に 'f' の文字を、フォルダには 'd' の文字を付けているのでそれなりに見やすいと思います。
static void EnumerateFileSystemEntries(string path) { var infos = new DirectoryInfo(path) .EnumerateFileSystemInfos("*.*", SearchOption.TopDirectoryOnly) .Select(x => new { IsDirectory = x.Attributes.HasFlag(FileAttributes.Directory), FullName = x.FullName, }) .OrderByDescending(x => x.IsDirectory) .ThenBy(x => x.FullName); foreach (var x in infos) Console.WriteLine("{0} : {1}", x.IsDirectory ? 'd' : 'f', x.FullName); } /* EnumerateFileSystemEntries("/etc/mono"); d : /etc/mono/2.0 d : /etc/mono/4.0 d : /etc/mono/4.5 f : /etc/mono/browscap.ini f : /etc/mono/config */
2. 実行ファイルを叩く
先のファイル/フォルダ検索で見つかった実行ファイルをその場で動かせたら面白いでしょう。以下がそのサンプルです。メソッドに実行ファイルの絶対パスと、起動引数を与えてあげれば OK です。paiza.IO 上のコードはユーザー権限下で実行されるように制御されているため、ホスト OS にダメージを与えるようなものの実行はできません。残念...(何
static void RunExe(string exe, string argument = null) { var psi = string.IsNullOrWhiteSpace(argument) ? new ProcessStartInfo(exe) : new ProcessStartInfo(exe, argument); psi.UseShellExecute = false; psi.RedirectStandardOutput = true; var process = Process.Start(psi); Console.WriteLine(process.StandardOutput.ReadToEnd()); process.WaitForExit(); } /* RunExe("/usr/bin/git", "--help"); usage: git [--version] [--help] [-C <path>] [-c name=value] [--exec-path[=<path>]] [--html-path] [--man-path] [--info-path] [-p|--paginate|--no-pager] [--no-replace-objects] [--bare] [--git-dir=<path>] [--work-tree=<path>] [--namespace=<name>] <command> [<args>] <以下略> */
3. bash コマンドを叩く
Unix 系を使っていたら、やっぱり bash。先の方法で実行ファイルを直接叩く方法もありますが、bash 上で実行するコマンドは基本的にパスが通っている状態なのでコマンドの実態がどこにあるかは通常あまり気にしません。そこで、bash 上で実行しているような感覚でコマンドを叩く機能も用意しました。以下がそのサンプルです。
static void RunCommand(string command) { var exe = "/bin/bash"; var args = string.Format("-c \"{0}\"", command); RunExe(exe, args); } /* RunCommand("env"); HOSTNAME=f20fd8104ba6 _WAPI_PROCESS_HANDLE_OFFSET=2 PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin PWD=/workspace LANG=en_US.UTF-8 RUNNER_USER=runner2 SHLVL=1 HOME=/workspace _=/usr/bin/env */
ぶっちゃけ bash を起動引数付きで呼び出しているだけですが、「コマンド入力 → Ctrl + Enter」という心地よい良いコンボが使えます。ただし、「カレントディレクトリを変更してからファイル確認」のような複合的なコマンドには向きません。
4. ファイルを開く
ファイルシステムを探索して見つけたファイルは中身を確認したいですよね。その機能が以下です。ファイルパスを与えるだけなので簡単ですね。
static void ReadFile(string path) { var text = File.ReadAllText(path); Console.WriteLine(text); } /* ReadFile("/workspace/build_time.txt"); Command being timed: "timeout 15 dmcs -sdk:4.5 Main.cs" User time (seconds): 0.54 System time (seconds): 0.02 Percent of CPU this job got: 99% Elapsed (wall clock) time (h:mm:ss or m:ss): 0:00.56 Average shared text size (kbytes): 0 Average unshared data size (kbytes): 0 Average stack size (kbytes): 0 Average total size (kbytes): 0 Maximum resident set size (kbytes): 41820 Average resident set size (kbytes): 0 Major (requiring I/O) page faults: 0 Minor (reclaiming a frame) page faults: 9290 Voluntary context switches: 20 Involuntary context switches: 50 Swaps: 0 File system inputs: 0 File system outputs: 16 Socket messages sent: 0 Socket messages received: 0 Signals delivered: 0 Page size (bytes): 4096 Exit status: 0 */
ちなみに、書き込み権限があるフォルダであればファイルを書き換えたり消したりすることもできます。とは言え、次にコードが実行されるときには Docker コンテナの力ですべてが元通りになっているので、環境破壊を頑張っても無力感しか感じませんが...。
おまけ - GHOST 対策のチェック
2015 年 1 月末に Linux 界隈を賑わせた GHOST。GNU C Library (glibc) というライブラリに存在したバッファオーバーフローの脆弱性(CVE-2015-0235)です。せっかくコマンドを実行する機能を作ったので、paiza.IO の環境でこの対策がなされているか調べてみました。glibc のバージョン確認方法はこちらのサイトを参考にしました。
RunCommand("/lib/x86_64-linux-gnu/libc.so.6"); /* GNU C Library (Ubuntu EGLIBC 2.19-0ubuntu6.5) stable release version 2.19, by Roland McGrath et al. Copyright (C) 2014 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Compiled by GNU CC version 4.8.2. Compiled on a Linux 3.13.11 system on 2014-12-04. Available extensions: crypt add-on version 2.1 by Michael Glad and others GNU Libidn by Simon Josefsson Native POSIX Threads Library by Ulrich Drepper et al BIND-8.2.3-T5B libc ABIs: UNIQUE IFUNC For bug reporting instructions, please see: <https://bugs.launchpad.net/ubuntu/+source/eglibc/+bugs>. */
Ubuntu 環境で version 2.19 が適用されていることが分かります。ちゃんと修正されたモジュールが使われている感じですね。よかった。
paiza の裏側解説
2015 年 2 月 19 日 ~ 20 日にかけて目黒雅叙園で開催された Developers Summit 2015。ここで paiza の裏側でガシガシ利用されている Docker 周りの詳説がされたようです。なかなか興味深いですね。