カテゴリー別アーカイブ: Linux

Orange PI PCで動画のハードウェアエンコードはできるのか

これはLinux Advent Calendar 2015 18日目の記事です。

ARMベースの小型PCとしてRaspberry PIシリーズが人気です。ハードウェアによる動画(H.264)エンコード支援がついていて、それが目的で使っている人もいるようです。

しかし私はRaspberry PI 2の代わりにOrange PI PCを購入しました。機能的には持っているようなので、できればなんとか使いたいところです。

参考にするため、そもそもRasPiではどうやっているのかをちょっと調べてみました。Googleで検索すると、gstreamerでomxh264encを使うという方法が紹介されています

これはgst-omxというコンポーネントのようで、OpenMaxというコーデックの取り扱いを抽象化したAPIのようです。さらに調べてみると、RasPiではlibopenmaxil.soという共有ライブラリが呼び出されているようです。

ではこいつのソースコードはどこだと探してみましたが、ファームウェアの一部という扱いでバイナリーのみが配布されているという残念な状況でした。RasPi側からの調査はここが限界です。

ではOrange PIではどうか。Orange PIとAllwinnerのチップの情報に関しては、linux-sunxi.orgが総本山のようです。ここにいろいろと情報がありました。

Orange PI PC用カーネルにはcedar-veというモジュールが追加されており、これがハードウェアコーデック用のデバイスドライバとなっています。/dev/cedar_devというスペシャルデバイスを介してioctlで直接ハードウェアを操作する機能を提供しています。残念ながらメインラインにはマージされていないようです。

そしてそれを使うよう改造されたffmpegが公開されています。これを動かすことを目標としていろいろ試してみました。リポジトリにはdeb化されたバイナリーもありますが、どうもwheezy向けのようです。しかもパッケージングの作法もよろしくなく、既存のdebianパッケージとファイル単位でいろいろ衝突を起こすので、これを利用するのはお勧めしません。

そこで手動でのビルドを行いましたが、ライブラリ周りで苦労しました。最終的には以下のような手順でビルドできると思います。

# apt-get build-dep ffmpeg
# apt-get install frei0r-plugins-dev libgnutls28-dev ladspa-sdk \
libiec61883-dev libavc1394-dev libass-dev libbluray-dev libbs2b-dev  \
libcaca-dev flite1-dev libgme-dev libgsm1-dev libmodplug-dev \
libmp3lame-dev libopencore-amrnb-dev libopencore-amrwb-dev \
libopenjpeg-dev libopus-dev librtmp-dev libshine-dev libssh-dev \
libspeex-dev libtheora-dev libtwolame-dev libvo-aacenc-dev libvo-amrwbenc-dev \
libvorbis-dev libvpx-dev libwavpack-dev libwebp-dev libx265-dev \
libxvidcore-dev libzvbi-dev libopenal-dev
# ./configure --prefix=/opt/ffmpeg \
        --build-suffix="-ffmpeg" \
        --toolchain=hardened \
        --enable-gpl \
        --enable-shared \
        --disable-stripping \
        --disable-decoder=libopenjpeg \
        --disable-decoder=libschroedinger \
        --enable-avresample \
        --enable-avisynth \
        --enable-gnutls \
        --enable-ladspa \
        --enable-libass \
        --enable-libbluray \
        --enable-libbs2b \
        --enable-libcaca \
        --disable-libcdio \
        --enable-libflite \
        --enable-libfontconfig \
        --enable-libfreetype \
        --enable-libfribidi \
        --enable-libgme \
        --enable-libgsm \
        --enable-libmodplug \
        --enable-libmp3lame \
        --enable-libopenjpeg \
        --enable-openal \
        --enable-libopus \
        --enable-libpulse \
        --enable-librtmp \
        --enable-libschroedinger \
        --enable-libshine \
        --enable-libspeex \
        --enable-libssh \
        --enable-libtheora \
        --enable-libtwolame \
        --enable-libvorbis \
        --enable-libvpx \
        --enable-libwavpack \
        --enable-libwebp \
        --enable-libx265 \
        --enable-libxvid \
        --enable-libzvbi \
        --disable-opengl \
        --enable-x11grab \
        --enable-version3 \
        --enable-libopencore_amrnb \
        --enable-libopencore_amrwb \
        --enable-libvo_aacenc \
        --enable-libiec61883 \
        --enable-libzmq \
        --enable-frei0r \
        --enable-libx264 \
        --enable-libopencv \
        --enable-libvo_amrwbenc
# make

ビルドしたバイナリを実行してみます。/dev/cedar_devへのrwアクセスができる状態でffmpegを実行します。

$ ffmpeg -i inputfile.ts -vcodec cedrus264 -pix_fmt nv12 output.mp4
ffmpeg version git-2015-01-22-f86a076 Copyright (c) 2000-2014 the FFmpeg developers
  built on Dec  7 2015 14:04:23 with gcc 4.9.2 (Debian 4.9.2-10)
(中略)
[VE SUNXI] VE version 0x0000 opened.
[cedrus264 @ 0x1f42f0] Cannot allocate frame.
Output #0, mp4, to 'output.mp4':
    Stream #0:0: Video: h264, q=2-31, 128 kb/s, SAR 1:1 DAR 0:0, 29.97 fps
    Metadata:
      encoder         : Lavc56.0.101 cedrus264
    Stream #0:1: Audio: aac, 0 channels, 128 kb/s
    Metadata:
      encoder         : Lavc56.0.101 libvo_aacenc
Stream mapping:
  Stream #0:0 -> #0:0 (mpeg2video (native) -> h264 (cedrus264))
  Stream #0:1 -> #0:1 (aac (native) -> aac (libvo_aacenc))
Error while opening encoder for output stream #0:0 - maybe incorrect parameters such as bit_rate, rate, width or height

残念ながらうまく動きませんでした。gdbで追いかけてみると、ve_open()で/dev/cedar_devをオープンしてioctl IOCTL_GET_ENV_INFOで構造体ve_infoの値を取得していますが、これ自体は失敗しないものの、各種メンバの値reserved_mem, reserved_mem_size, registersがすべて0になっています。これによりve_mallocが失敗して6行目のエラーメッセージが表示されます。ドライバ側のコードを読むと、「このインターフェースは使うな」というコメントがあり、実際に0を返していました。きっと古いcedar_devで動くコードだったのでしょう…

http://linux-sunxi.org/CedarX/Encoderの記述によると、現在あるコード以外にもsunxi_memドライバという物理メモリにアクセスするドライバ(/dev/sunxi_mem)と併用して動くCedarXというライブラリが別にあるようです。ハードウェアエンコーダのサンプルコードもあるのですが、やはりバイナリでしか存在しないライブラリ群に依存しています。

それでもいいからなんとか動かせないかと、sunxi_memドライバを追加した勝手カーネルをビルドしようとしたのですが、mach/includes.hという存在しないヘッダファイルを必要としてビルドできませんでした。ここはまだ調査不足です。

現状の結論として、Orange PI PCでH.264のハードウェアエンコードは「できそうな雰囲気」という煮え切れない結論となってしまいました。linux-sunxi MLの購読を始めたので、今後はこのあたりどうなっているかを尋ねてみたいと思っています。

VMware PlayerからVirtualBoxへの移行

最近のスラッシュドットの記事から、VMware Playerのライセンスにおける「非商用の解釈がかわった」という記事(ライセンス解釈が変わったようです。)を目にし、この機会にVirtualBoxへの移行を実施しました。

移行対象は違いますが、既にやってみた人の記事(VMware Fusion 5からVirtualBox 4.3への移行)があったので、作業自体はそれほど難しくはありませんでした。ただ、この記事にもあるようにストレージをSCSIからIDEに変更する、という作業はVMware Playerでも同様に必要でした。

自分の場合、もう一点「ホストオンリーネットワークもそのまま移行したい」という希望があったので、そのために若干の作業が発生しました。参考にしたのは「VirtualBoxのネットワーク設定とCentOS6.5のインストール」です。こちらの記事はMacOSXのようですが、Windowsでも行うことはあまり変わりません。

VirtualBox マネージャーからメニューの「ファイル」「環境設定」を選択し、ダイアログを開きます。さらに「ネットワーク」「ホストオンリーネットワーク」を選択し、ネットワークを追加します。ネットワークレンジやネットマスクを、これまでVMware Playerで使っていたものと同じになるよう設定します。

ここで一点注意があります。VMware Playerが入ったままだと、VMwareの仮想ネットワークインターフェースが設定として保持されたままになるので、vmnetcfg.exeで設定を変更するか、VMware Playerをアンインストールする必要があります。そうしないと、同一のネットワーク設定が存在しているとWindows側に認識され、リンクローカルなネットワークが強制的に割り当てられてしまいます。

あとはOVF化した仮想マシンをVirtualBoxからインポートし、ネットワーク設定を先ほど作成したホストオンリーネットワークに指定して、実際に通信ができれば完了です。

NATネットワークの移行も、ほぼ同様の手順で実行し、Window側のネットワーク共有を使えばできると思います。

ELECOM UCAM-C0220Fを使う

ふと思うところがあって、安価なUSBカメラを購入しました。エレコムの200万画素で安価(1000円程度)なモデルです。

最終的な目的の用途とは異なるのですが、とりあえず自宅の様子をストリーミングさせてみました。かつてはgstreamerを使っていろいろと余計な苦労をする羽目になったのですが(Google Slideの資料9ページ目参照)、いまどきはffmpeg/libavを使ってVorbis/TheoraやWebMにエンコードし、icecastに配送させるのが簡単だと思います。

このデバイスは映像入力をUVCクラス、音声(マイク)入力をAudioクラスとした複合デバイスに見えます。

 % lsusb
Bus 004 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 006 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub
Bus 003 Device 002: ID 8564:1000
Bus 003 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 005 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub
Bus 002 Device 004: ID 056e:7016 Elecom Co., Ltd
Bus 002 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 001 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub
 % arecord -L
null
    Discard all samples (playback) or generate zero samples (capture)
default:CARD=UCAMC0220F
    UCAM-C0220F, USB Audio
    Default Audio Device
sysdefault:CARD=UCAMC0220F
    UCAM-C0220F, USB Audio
    Default Audio Device
front:CARD=UCAMC0220F,DEV=0
    UCAM-C0220F, USB Audio
    Front speakers
surround40:CARD=UCAMC0220F,DEV=0
    UCAM-C0220F, USB Audio
    4.0 Surround output to Front and Rear speakers
surround41:CARD=UCAMC0220F,DEV=0
    UCAM-C0220F, USB Audio
    4.1 Surround output to Front, Rear and Subwoofer speakers
surround50:CARD=UCAMC0220F,DEV=0
    UCAM-C0220F, USB Audio
    5.0 Surround output to Front, Center and Rear speakers
surround51:CARD=UCAMC0220F,DEV=0
    UCAM-C0220F, USB Audio
    5.1 Surround output to Front, Center, Rear and Subwoofer speakers
surround71:CARD=UCAMC0220F,DEV=0
    UCAM-C0220F, USB Audio
    7.1 Surround output to Front, Center, Side, Rear and Woofer speakers
iec958:CARD=UCAMC0220F,DEV=0
    UCAM-C0220F, USB Audio
    IEC958 (S/PDIF) Digital Audio Output

ffmpegでalsaデバイスとしてこのオーディオデバイスを使うと、定期的に”[alsa @ 0x15fcc00] ALSA buffer xrun.”というエラーが出ます。どうもUSB audioとしての動作が何かおかしいようです。この状態でoggfwdを続けていると、icecast側から接続を切られてしまいます。まあ自分の主目的は映像をとることなのでその用途には十分使えそうです。

cgroupsのnamespace手動削除

自宅のPCルーターは10年以上前に構築したDebian環境をもとに、HDDだけ移しながらupgradeを長年重ねて積み上げたもので、.emacsが結構な腐臭を放っているとか自作init scriptをmake styleにいい加減に修正したものがあったりとか、いろいろガタはあるものの今でも元気に動いています。

時代が時代なので当然アーキテクチャはi386なのですが、いまどきはDebian側でi386向けのamd64 kernelパッケージを提供しているので、現在はそれを使っています。メインメモリも8GB積んでいます。

kernelがamd64なので、debootstrapでamd64のuserlandを構築することも可能です(–arch amd64)。最近、これをベースにLXCコンテナ化しました。やることは単純で/var/lib/lxc/$name/rootfsにシンボリックリンクを張って、設定ファイルを適当にコピペででっちあげるだけです。

ただ、この作業の過程の中で、lxc-startを-KILLシグナルで殺してしまい、cgroupsのnamespaceが残ってしまうという問題が発生しました。これをどうやったら削除できるか調べてみた結果、rmdir /sys/fs/cgroups/lxc/$name で削除できることを覚えました(kernel documentのcgroups.txt)。最初 rm -rfで消そうとして消せない、と苦労していたのですが、ディレクトリだけを削除すれば良いようです。逆にmkdirするとblkioとかcpusetとか必要なものがごそっと生えてきます。

というわけで、何かしらの不手際でcgroupsのnamespaceが残ってしまった場合には、ディレクトリだけをrmdirしましょう。

ついでにわかったこととして、LXC 1.0.6ではこういった「同じ名前のnamespaceが既にある」状況では$name-1といった名前で重複しないように作るような動きをするようです。

Linuxで扱う乱数に関する話

これはLinux Advent Calendar4日目の記事です。

Unix系OSには、カーネルに乱数生成器を持つ実装が多くあります。乱数は暗号分野でも利用され、非常に重要な位置を占めています。Linuxにおける乱数に関する話題を取りあげてみます。

エントロピープール

一般的に、特別なハードウェアを持たない限り、真の乱数を計算機が生成することは困難です。Linuxでは、質の良い乱数を生成するためにエントロピープールと呼ばれる領域を持っています。エントロピープールには、キーボードの入力タイミングやストレージ、ネットワークなどで発生するハードウェア割り込みなどをもとにした推測の困難な情報(環境ノイズ)が蓄積されます。乱数の生成時には、このエントロピープールの内容を消費、加工します。

エントロピープールにどの程度情報がたまっているかを調べるには、/proc/sys/kernel/random/entropy_availを見ます。エントロピープールの上限値は/proc/sys/kernel/random/poolsizeをcatした出力値です(デフォルト値は4096) 。OpenPGPやSSLの鍵などを生成する際には、この値を確認した方がよいかもしれません。

// drivers/char/random.c
/*
 * Configuration information
 */
#define INPUT_POOL_WORDS 128
#define OUTPUT_POOL_WORDS 32
#define SEC_XFER_SIZE 512
#define EXTRACT_SIZE 10

// 途中略

static int sysctl_poolsize = INPUT_POOL_WORDS * 32;
ctl_table random_table[] = {
        {
                .procname       = "poolsize",
                .data           = &sysctl_poolsize,
                .maxlen         = sizeof(int),
                .mode           = 0444,
                .proc_handler   = proc_dointvec,
        },
        {
                .procname       = "entropy_avail",
                .maxlen         = sizeof(int),
                .mode           = 0444,
                .proc_handler   = proc_dointvec,
                .data           = &input_pool.entropy_count,
        },

スペシャルデバイス

乱数を生成する特別なデバイスファイルには、/dev/randomと/dev/urandomの2種類があります。

/dev/randomは全ての乱数をエントロピープールから生成します。エントロピーが不足した場合は新たにたまるまでブロッキングされるため、あまり速度が出ません。乱数の質よりも速度に重点を置く場合には、/dev/urandomを使います。urandomを使う場合、エントロピープールを再利用して乱数を生成します。

havege

havegedを動かすことで、より多くの環境ノイズ(主にCPUの情報)をもとにしてエントロピープールを常に多い状態へと保つことができます。エントロピープールへの情報の追加は/dev/randomのioctrlインターフェースを用いています。また、havagedは単独の乱数生成アプリケーションとしても利用できます。

NeuG

g新部さんによるハードウェア乱数生成実装として、NeuGがあります。NeuGはSTM32F103上で動作し、チップに搭載されているA/Dコンバーターからの入力の量子化誤差を乱数生成に利用します。NueGの動作するハードウェアとしてFST-01があります。この記事を書いている2014年12月初頭では品切れ中ですが、今後追加生産・販売を予定しているそうです。

仮想化の問題

エントロピープールの情報は、予測が困難であるハードウェアからの情報に大きく依存しています。仮想マシンの場合、多くのハードウェアは仮想化されており、ハイパーバイザ等の配下にあります。したがって、物理マシンと比較すると質の良い乱数を生成させることがより困難となっています。セキュリティに十分な注意を払う必要がある場合には、信頼できない仮想マシン上での暗号鍵の生成などは控えた方が良いでしょう。

訂正

コメントでg新部さんからいくつかご指摘を頂きましたので修正・追記しました。FST-01はhttp://www.seeedstudio.com/より購入可能とのことです。詳細はコメントをご覧ください。

宣伝

この記事はFSIJ勉強会での解説をもとに得た知見を自分なりに記録したものです。FSIJでは定期的に勉強会を開催しています。GnuPGの開発者の一人であるg新部さんもいらっしゃるので、興味のある方はぜひお越しください。

 

動作中のプロセスのptyをつなぎかえられる、reptyrを使ってみる

UNIX板のGNU screenスレッドで紹介されていたreptyrを使ってみました。

screen外のプロセスにattachさせる

既にscreenを実行している状況で、screenのセッション外のプロセスを起動してみます。適当なterminalを起動するなり、外からsshでつなげるなりしてみます。あとでプロセスを探しやすいようにttyコマンドで現在使っているptyデバイスが何かを確認しておきます。

$ tty
/dev/pts/7

次にscreen内の任意のウインドウのshellから、当該プロセスにつなげてみます。

$ reptyr -s `pgrep -t pts/7 bash`
[-] Timed out waiting for child stop.
[+] Allocated scratch page: 7faebd18b000
[+] Opened the new tty in the child: 3
[+] Set the controlling tty
$

この時点で、元のターミナルは無反応になり、bashの制御がscreen下に移りました。shellを終了すればreptyrに制御が戻ります。

$ exit
ログアウト
$

gdbと組み合わせる

reptyrのもう一つの機能、ptyを外部から使わせるために確保する-lオプションを使ってみます。

$ reptyr -l
Opened a new pty: /dev/pts/7

別のshellからgdbでこのptyを使ってみます。set inferior-ttyで/dev/pts/7を指定することで、gdbが動かすプロセスの出力をreptyr側の端末に変更できます。

$ gdb ls
GNU gdb (GDB) 7.4.1-debian
Copyright (C) 2012 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from /bin/ls...(no debugging symbols found)...done.
(gdb) set inferior-tty /dev/pts/7
(gdb) run
Starting program: /bin/ls
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
[Inferior 1 (process 15662) exited normally]
(gdb)

reptyrを実行していたshell上に、lsの出力が表示されます。標準入出力をデバッガの画面と切り離すことができて便利です。

実装方法

以前「起動済みプロセスのリダイレクト先を設定したい」という記事を見かけた記憶があったので、基本的には同じようなやりかただろうと思いつつソースを見てみたら、その通りでした。

起動しているプロセスに対してptrace(2)でattachし、ptyをdup2でfile descripter 0, 1, 2に複製しています。ただし、単なるリダイレクトと違い仮想端末が相手なのでresize等に対応するためのコードも含まれているようです。

前述の記事や本プログラムのドキュメントにもありますが、Ubuntuのカーネルはデフォルトで一般ユーザーのptraceを禁止しています。procfsを見てそのあたりをチェックするコードも入っています。Ubuntuユーザーが利用する場合、rootで実行するか、sysctlの設定を変更する必要があります。

SSLv3の脆弱性(POODLE)対応

SSL 3.0に、プロトコル自体の脆弱性がGoogleによって発見されました。”Paddning Oracle On Downgrade Legacy Encryption”略してPOODLEと呼ばれています。

検証サイト https://www.poodletest.com/ が立ち上がっており、脆弱性の詳細に関する元ソースへのリンクもあります。

今回の脆弱性はサーバー、クライアントともに対策が必要になります。とり急ぎ自分の周りにあるSSL/TLSを使うサービスに対策を施しました。

Apache+mod_ssl

SSL_Engine onとしているセクション内で、以下の設定をしました。

SSLProtocol all -SSLv2 -SSLv3
SSLHonorCipherOrder on
SSLCipherSuite "EECDH+ECDSA+AESGCM EECDH+aRSA+AESGCM EECDH+ECDSA+SHA384 EECDH+ECDSA+SHA256 EECDH+aRSA+SHA384 EECDH+aRSA+SHA256 EECDH+aRSA+RC4 EECDH EDH+aRSA RC4 !aNULL !eNULL !LOW !3DES !MD5 !EXP !PSK !SRP !DSS"

SSL 2.0, 3.0を無効にし、利用可能な暗号からMD5,  3DES等を外しています(参考: httpsだからというだけで安全?調べたら怖くなってきたSSLの話!?)。

Postfix

smtpd_use_tls=yes
smtpd_tls_mandatory_protocols = !SSLv3, !SSLv2
smtpd_tls_mandatory_exclude_ciphers = aNULL, MD5, RC4

参考: Postfix TLS Support

Courier-imapd

##NAME: TLS_PROTOCOL:0
#
# TLS_PROTOCOL sets the protocol version.  The possible versions are:
#
# OpenSSL:
#
# SSL3 - SSLv3
# SSL23 - either SSLv2 or SSLv3 (also TLS1, it seems)
# TLS1 - TLS1
#
# Note that this setting, with OpenSSL, is modified by the TLS_CIPHER_LIST
# setting, below.
#
# GnuTLS:
#
# SSL3   - SSLv3
# TLS1   - TLS 1.0
# TLS1_1 - TLS 1.1
#
# When compiled against GnuTLS, multiple protocols can be selected as follows:
#
# TLS_PROTOCOL="TLS1_1:TLS1:SSL3"
#
# DEFAULT VALUES:
#
# SSL23 (OpenSSL), or "TLS_1:TLS1:SSL3" (GnuTLS)
TLS_PROTOCOL="TLS1"

Debianのcourier-imapdはOpenSSLをリンクしているのでTLS1のみ設定しています。

確認方法

OpenSSLのs_clientを利用することで、任意のバージョンのSSL/TLSを使った接続を試すことができます。

$ openssl s_client -connect target.server.example.jp:443 -ssl3
(略)
SSL handshake has read 2844 bytes and written 289 bytes
---
New, TLSv1/SSLv3, Cipher is ECDHE-RSA-AES256-SHA
Server public key is 2048 bit
Secure Renegotiation IS supported
Compression: NONE
Expansion: NONE
SSL-Session:
    Protocol  : SSLv3
    Cipher    : ECDHE-RSA-AES256-SHA
(略)
$ openssl s_client -connect example.jp:443 -ssl3 -cipher `openssl ciphers RC4`
(略)
New, TLSv1/SSLv3, Cipher is ECDHE-RSA-RC4-SHA
Server public key is 2048 bit
Secure Renegotiation IS supported
Compression: NONE
Expansion: NONE
SSL-Session:
    Protocol  : SSLv3
    Cipher    : ECDHE-RSA-RC4-SHA

上記サンプルでは、1回目はSSLv3, AESでの接続、2回目はSSLv3, RC4での接続ができています(参考: 私が愛した openssl (SSL/TLS 編))。設定が正しくできれば、以下のような結果となりネゴシエーションに失敗します。

SSL handshake has read 7 bytes and written 0 bytes
---
New, (NONE), Cipher is (NONE)
Secure Renegotiation IS NOT supported
Compression: NONE
Expansion: NONE
SSL-Session:
    Protocol  : SSLv3
    Cipher    : 0000

STARTTLSを使う場合は-starttlsオプションが利用できます。

$ openssl s_client -connect example.jp:587 -ssl3 -cipher `openssl ciphers RC4` -starttls smtp
(略)
SSL handshake has read 2128 bytes and written 264 bytes
---
New, TLSv1/SSLv3, Cipher is ECDHE-RSA-RC4-SHA
Server public key is 2048 bit
Secure Renegotiation IS supported
Compression: zlib compression
Expansion: zlib compression
SSL-Session:
    Protocol  : SSLv3
    Cipher    : ECDHE-RSA-RC4-SHA

Debianでの対応状況(2014/10/16朝頃)

不安定なマシンを無理やり動かす(watchdog, memtest)

実家に設置してあるサーバがここのところ妙に挙動があやしく、いろいろ調べてみた結果どうもメモリがあやしい、という結論に至りました。しかしそのためだけに新幹線で名古屋まで帰ることも出来ず、しかも今ちょっと入院中なので、リモートで出来る限りのことをしてみました。

まずはsoftware watchdogの導入です。これは/dev/watchdogというデバイスをつくり、定期的に書き込みがあるかチェックして、一定時間(デフォルトは60秒)書き込みがなかったら再起動するというものです。チェックをするためのソフトウェアがDebianにはwatchdogという名前のパッケージであります。インストール時にdebconfでいくつか質問されます。私の設定は以下になります。

# debconf-show watchdog
* watchdog/restart: false
* watchdog/module: softdog
* watchdog/run: true

moduleは利用するwatchdogドライバのモジュール名で、software watchdogはsoftdogを指定します。watchdogデーモンが起動するときにこれがinsmodされます。runは自動起動するかどうかの設定、restartはアップグレード時に再起動をさせるかさせないかを指定します。下手に再起動させると、kernelがwatchdogがとまったと誤認識することがあるので、falseのほうが安全です。

次に/etc/watchdog.confを編集します。デフォルトではコメントアウトされているwatchdog-deviceの行を有効にします。

watchdog-device = /dev/watchdog

これで何かあってシステムがハングすると、再起動するようになります。ただし、ソフトウェア実装なので確実ではありません。

しかしそもそも不安定な原因がメモリにあるので、そこをなんとかしたいところです。昔はbadramというパッチがあって、問題のあるメモリ領域を手動で指定して使わないようにさせる、という方法があったのですが、今はもっといい方法があります。

Linux 2.6.26から導入されたkernel組み込みmemtestです。起動パラメータにmemtest=試行回数を指定することで、メモリのエラーをチェックしエラー領域を使わないよう動作します。

残念ながら、Debianデフォルトのカーネルでは有効になっていないので、
CONFIG_MEMTEST=yを.configに追加して自分でカーネルの再構築をする必要があります(make-kpkgを利用)

自作したカーネルパッケージをインストールしたら、カーネルパラメータを追加してやる必要があります。/etc/default/grubを編集します。

GRUB_CMDLINE_LINUX="memtest=32"

GRUB_CMDLINE_LINUXにmemtest=数値を指定して、テスト回数を指定してやります。この例では32回テストします。テストを増やせばエラーを発見する確立が高まりますが、当然起動には時間がかかります。手持ちのノートPCで試しに255を指定したところ、数分待っても終わる様子がないので完走させる前に止めてしまいました。

最後にupdate-grubを実行して、このパラメータをgrub.cfgに反映させてから再起動します。カーネルがエラーを発見すれば、dmesgに以下のような出力が出ます。


[    0.000000] early_memtest: # of tests: 32
[    0.000000]   0000014000 - 000009d800 pattern 0000000000000000
[    0.000000]   0000100000 - 0001000000 pattern 0000000000000000
[    0.000000]   00014ee56c - 00014ef000 pattern 0000000000000000
[    0.000000]   00014fc15c - 00177df000 pattern 0000000000000000
[    0.000000]   001803f9ef - 0020000000 pattern 0000000000000000
[    0.000000]   0020200000 - 00377fe000 pattern 0000000000000000
[    0.000000]   0000014000 - 000009d800 pattern ffffffffffffffff
[    0.000000]   0000100000 - 0001000000 pattern ffffffffffffffff
[    0.000000]   00014ee56c - 00014ef000 pattern ffffffffffffffff
[    0.000000]   00014fc15c - 00177df000 pattern ffffffffffffffff
[    0.000000]   001803f9ef - 0020000000 pattern ffffffffffffffff
[    0.000000]   0020200000 - 00377fe000 pattern ffffffffffffffff
[    0.000000]   0000014000 - 000009d800 pattern 5555555555555555
[    0.000000]   0000100000 - 0001000000 pattern 5555555555555555
[    0.000000]   00014ee56c - 00014ef000 pattern 5555555555555555
[    0.000000]   00014fc15c - 00177df000 pattern 5555555555555555
[    0.000000]   001803f9ef - 0020000000 pattern 5555555555555555
[    0.000000]   0020200000 - 00377fe000 pattern 5555555555555555
(以下略)

これで今のところ問題は起きていません。しかしこんな状態で利用し続けるのも問題なので、可能であればECCメモリーにでも乗せかえたいところです。