まだプログラマーですが何か?

プログラマーネタ中心。たまに作成したウェブサービス関連の話も https://twitter.com/dotnsf

2015/10

ラズベリーパイを個人で2台持ってます。 うち一台(Raspberry Pi 2)は常用、というか常に自宅のネットワークに繋げて電源も入っています。SSH や VNC で常にアクセスできる環境が整っています。そういう意味でも「常用」です。

もう一台(Raspberry Pi B+)は、元々はこっちを常用機として使っていましたが、2を買ったらさすがに常用機としてはスペックの高い方にしたくなったため交換しました。結果として、こいつは電源を入れればいつでも動く状態ではありつつも余っていました。 そういうこともあり、新しいことにチャレンジする際の実験機的な位置付けになりました。ぶっちゃけ「壊れてもいいや」的な。


今回挑戦したのは「シリアル接続」です。ラズベリーパイ(以下「ラズパイ」)はネットワークに繋がった状態で起動していて、かつその IP アドレスが分かっていれば SSH や VNC でログインして使うことができて非常に便利なコンパクト Linux 環境です。でもネットワークに繋がっていても DHCP 環境などで IP アドレスが分からない場合にどうやって調べるかというと、(PiFinder など外部から IP アドレスを調べる専用のツールもあったりしますが)USB キーボードと HDMI ディスプレイを繋いで、キーボードからログインして ifconfig コマンドなどで IP アドレスを調べて・・・という手順が必要になります。が、そもそもキーボードを用意するとか、HDMI ケーブルを用意するとか、そのケーブルが届く範囲に外部ディスプレイを用意するとか、準備のハードルがかなり上がってしまいます。また(ネットワークの設定前の段階や、普段と異なるネットワーク環境に持ちだした場合などで)そもそもネットワークが繋がっていない状態では PiFinder も使えないので、結局設定のための最初の1回のためだけにこれらのキーボードやディスプレイの準備も必要になってしまいます。

それをどうにかするための方法の1つが「シリアル接続」です。要するにラズパイと手元の PC をシリアルに直結した上で、シリアル接続をサポートするターミナルアプリからラズパイに接続してログインする、という方法です。特に目新しい方法ではなく、PC に TCP/IP 環境がなかった昔はむしろこっちの方法が主流でした。オッサンの出番です(笑)!


用意するのはラズパイに加えてこの3つです:
(1) ラズパイで使える USB シリアルケーブル
(2) (1) のデバイスドライバ
(3) シリアル接続をサポートするターミナルアプリ


自分のメイン PC は Windows7 ということもあって、(1) にはこの変換ケーブルを使うことにしました:
https://strawberry-linux.com/catalog/items?code=15076

ラズパイの GPIO を使ってシリアル接続し、かつ USB ポートに変換して PC とつなげる、というケーブルです。Windows8 以降だとデバイスドライバが正しく動作しない、と書かれてます。Windows8 以降や他の環境をお使いのみなさん、ごめんなさい。

(2) のデバイスドライバですが、上記ケーブルの場合であればリンク先ページの「Windowsドライバ」と書かれたリンク先からダウンロードできます。


また (3) のターミナルアプリですが、自分は TeraTerm を使いました:
2015101601



では実際にシリアル接続するまでを紹介します。まずはデバイスドライバのインストールです。ラズパイの電源を切って、ケーブル接続も何もしない状態で、(2) のデバイスドライバをダウンロード&展開し、実行ファイルをダブルクリックしてインストールします:
2015101602


次に USB シリアルケーブルでラズパイと PC とを接続します。ラズパイ側の GPIO の 6(GND), 8(TXD), 10(RXD) 番のピンに、ケーブルの赤、緑、青のケーブルをそれぞれ差し込みます:
2015101603

そしてケーブルのもう一方の端を PC の USB ポートに接続します(ラズパイにはまだ電源の USB ケーブルはつなぎません)。するとプラグアンドプレイでデバイスドライバが導入されます。この画面だと COM6 として認識されたことになっていますが、ここは環境によって異なります:
2015101604


この段階でデバイスマネージャーを起動して COM のポートを確認すると、上記で "USB-to-Serial" のデバイスが、COM6 として認識されていることが分かります。つまりシリアル接続では(この例では)COM6 ポートを指定して接続すればよい、ということになります:
2015101605


では実際に接続してみます。シリアル接続をサポートするアプリ(この例では TeraTerm)を起動して、認識された COM ポートを指定してシリアル接続します:
2015101606


接続後に COM 接続の設定を変更します。TeraTerm の場合、デフォルトではシリアルポートのボーレートが低いので上げておきます。メニューの 設定→シリアルポート を選択します:
2015101607


指定 COM ポートのボーレートを 9600 から 115200 に変更して OK をクリックします:
2015101608


で、この状態に戻ります。これでラズパイにシリアル接続して待ち受け状態になりました:
2015101609


ここではじめてラズパイに電源ケーブルをつなぎます。するとブートプロセスの画面がシリアルターミナル画面に次々と表示され、ログインを待つ状態のプロンプトが表示されます:
2015101610

※途中で "Welcome to rescue login" というメッセージが表示されて画面が止まりますが、そこでは何もせずにしばらく待つと通常のブートプロセスが開始され、ログインプロンプトが表示されます。


ログインすると普通に操作できます。ちなみに ifconfig でネットワーク接続がないことを確認してみました:
2015101611


lo はループバックなので、本体から見た自分自身です。そして eth0 (有線LAN)と wlan0 (無線LAN)はハードウェアの認識こそされていますが、IP アドレスは割り振られていません(でも接続してこの画面が見れている、ということです)。

ネットワークがなくてもラズパイが使えることが確認できました!


ペネトレーションテスト向けにカスタマイズされた Linux ディストリビューションである『Kali Linux』に VNC サーバー機能を導入する手順を紹介します。

まず、インストールする VNC サーバーアプリケーションは vnc4server です。これを apt-get でインストールします:
# apt-get install vnc4server

vnc4server の起動は以下の vncserver コマンドで行います。起動後にパスワードを聞かれるので入力内容を覚えておきます。なお、VNC クライアントでアクセスする際には、この vnc4server を実行したユーザーのユーザー権限でログインするので、必要であれば su でユーザーを切り替えてから実行してください。また ":" の後の数字はポート番号を意味する数字(この数字 + 5900 番ポートで実行する)で、この例であれば 5901 番で実行されます:
# vncserver :1

You will require a password to access your desktops.

Password: (パスワードを入力)
Verify: (パスワードを再入力)
   :
   :

これで VNC サーバーが起動しますが、少しカスタマイズを加えます。そのために一旦起動した VNC サーバーを終了します。終了は以下のコマンドで行います(最後の : の後の数字は実行時に指定したものを同じもの):
# vncserver -kill :1

この vncserver コマンドを実行したユーザーのホームディレクトリ以下の ~/.vnc/xstartup というファイルに VNC 実行時の X11 サーバーの設定が記述されているので、このファイルを編集します。具体的にはデフォルト Window Manager を使うのではなく、GNome セッションを使うことにします(赤字の3行を変更):
# vi ~/.vnc/xstartup
  :
  :
xsetroot -solid grey
#x-terminal-emulator -geometry 80x24+10+10 -ls -title "$VNCDESKTOP Desktop" &
#x-window-manager &
gnome-session &

ここまでのカスタマイズが終わったら、改めて VNC サーバーを実行します。
# vncserver :1

では、この Kali Linux マシン以外から VNC クライアントでこのマシンに接続を試みてみます:
2015101201


今のところ、この VNC サーバーを自動起動させる方法が分からないので、再起動するたびに vncserver コマンドを実行する必要がありますが、一応 VNC サーバーを使えることがわかりました。


 

ペネトレーションテスト向けにカスタマイズされた Linux ディストリビューションである『Kali Linux』。いろいろアレな機能が標準搭載されていることに加え、見た目もカッコいいし、LibreOffice とかも入れれば普段使いのデスクトップ環境としても使える、という印象を持っています:


なお Kali Linux の導入手順および日本語化他については以前に作成した以下のエントリを参照してください:
Kali Linux を使ってみる


デスクトップ環境が目の前に使える状態になっていると便利なのですが、ペネトレーションテスト用のこだわりなのか、一般的な Linux ではデフォルト状態で使えるサービスの多くが使えません。例えば sshd も無効なので SSH によるリモートログインなども無効になっています。

これはこれでちょっと不便に感じることもあるので、Kali Linux で sshd を有効にする方法を紹介します。手順は簡単で、root でログインして以下のコマンドを実行します:
# update-rc.d ssh enable

で Kali Linux を再起動すると sshd が有効になっており(というか、自動で起動するようになり)、SSH によるリモートログインもできるようになります。


 

現在、世界中の IBM が学生を対象にメインフレーム上でのアプリケーション開発コンテストを開催しています:
Master the Mainframe 2015(メインフレーム・コンテスト)

2015100908


このプログラムに参加すると、IBM の最新版メインフレームである IBM z13 の環境にリモートログイン可能なアカウントを入手することができます。そのアカウントを利用して課題をクリアしつつ、最終的にはアプリケーションを開発して世界中の参加者とその出来を競い合う、というものです。リモートログイン先はネイティブ OS である z/OS だけでなく、Linux 専用メインフレームである LinuxONE 上の RedHat Enterprise Linux(RHEL) 7.1 を利用することもできます:
2015101004


zSystems の環境、一度使ってみたかったんだよなあ。でもこのコンテスト、学生向けなんだよなあ・・・ 社員がやったら怒られるかなあ・・・ いいなあ・・・ 残念だなああああああああああ


というわけで、以下のスクリーンショットは私の脳内を念写したものです(笑)。

まずは z/OS にアクセス。なんか学生時代の Fortrun の授業でこんなの使っていたような記憶が・・・
2015100907


vi がかわいく見えるレベルで操作方法がかなーり独特なテキストエディタ。。。
2015100909


・・・うん、わかった。ごめんなさい、部屋を間違えました(?)

問題はこっちだ。RHEL 7.1 環境へログイン。そうそう、このプロンプトが出ると安心(苦笑):
2015100901


まずはシステムを確認。"el7.s390x" の文字が目新しい:
2015100902


念のためディストリビューションも確認。RHEL 7.1 ですね、と:
2015100903


/proc/cpuinfo を確認。"IBM/S390" の CPU を3つも使わせてくれるっぽい:
2015100904


root 権限は・・・ NG っぽい。しかも root になろうとしたこの操作がレポートされる、とな!? の、脳内で助かった(震え声):
2015100905


最後に top を実行。CPU 3つ、メモリ 4GB、スワップ 1GB のえらく贅沢な環境:
2015100906


コンテストは 11月20日まで応募を受け付けています。最優秀賞は NY で開催される世界大会へ招待される他、優秀賞は来年のラスベガスイベントへのご招待、メインフレーム賞として Oculus Rift など、副賞もメインフレーム級なものが用意されているっぽいです。



 

2日ほど泥沼にハマって抜け出せなくなっていた問題が解決できたので、その謎を共有します。

元々はこんなシステムを PHP で作ろうとしていました:
 - 画像をアップロードできる
 - アップロード画像は ID を付与した上で MySQL の BLOB (バイナリ・ラージ・オブジェクト)として格納する
 - ID を指定して、アップロード済みの画像を表示させることができる

自分自身でも Java では何度も作ったことのある仕組みで、その PHP 版を作ろうとしただけでした。

いざ作って動かしてみると、アップロードは成功します。でも表示時に画像が壊れてしまい、うまく表示できないのです。この原因究明と解決に時間を使ってしまいました。


もう少し詳しく状況を説明します。調べていくうちに同じ現象が MySQL を使わなくても再現できることがわかったので、話をシンプルにするために MySQL なしで説明します(実際にはこの切り分けも簡単ではなかった)。

ソースコードはこんな感じ。同一ディレクトリにある bmxug148.png というファイルのバイナリコンテンツ(元々のコードでは MySQL の BLOB に格納されていたもの)を取り出して、PNG 画像用の HTTP ヘッダを指定して echo でバイナリをそのまま出力しています:
<?php
$img = file_get_contents("./bmxug148.png");
header('Content-Type: image/png');
echo $img;
?>

この PHP ファイルをブラウザから呼び出すと、画像が表示・・・されることを期待していたのに、画像が壊れてしまっている旨のメッセージが表示されてしまいます(FireFox で確認した場合):
2015100202


こういう状況だと、表示側の PHP スクリプトに問題があるのか、それともアップロードしたファイルバイナリデータの格納時に既に壊れてしまっているのかすらわかりません。まあ、上記のシンプルなスクリプトでも再現するということは前者ということになるのですが、それが分かったのは相当後になってのことです。


で、とりあえずこの「壊れた画像バイナリ」を無理やり取り出してファイルとして保存し、バイナリエディタで開いてみました。そこで分かったのは画像の最初3バイトに PNG 画像としてふさわしくない情報が追加されていた、ということです:
2015100203


0xEF, 0xBB, 0xBF という3バイトが余分です(4バイト目から本来の PNG 画像のヘッダが始まっています)。4バイト目から表示されていれば正しく表示できそうなのですが、ではこの最初の3バイトはどこで付与されてしまったのでしょう? DB への格納時の処理を疑うこともできたのですが、この EF BB BF という3バイトの組みあわせ、どこかで見たことありました。。

ちょっと調べると分かるのですが、これは BOM(Byte Order Mark) と呼ばれる符号で、テキストが Unicode で記述されていることと、その符号化の種類を示すものです。で、UTF-8 の場合の BOM がまさにこの 0xEF 0xBB 0xBF の3バイトなのです。


つまり、実はもとの PHP ファイルそのものが UTF-8 で記述されていて、しかも BOM 付きでファイルが保存されていたことが直接の原因だったのでした。このため、ブラウザでこのファイル実行した場合も、最初に 0xEF 0xBB 0xBF の3バイトは(PHP ファイルの一部として)返ってきて、次に PHP で処理した結果の画像バイナリが続いていた、ということになります:
2015100201


画像の情報は正しく返っていたのに、その前に画像とは関係のない3バイトが送られてきていたため、「画像としては壊れている」と判断された、ということです:
2015100202


したがって、解決策は PHP ファイルを BOM なしで保存することです:
2015100204


この PHP をブラウザから実行すれば、本来の正しい画像が表示されるようになります:
2015100205


あー、よかった。
 

このページのトップヘ