アシアルブログ

アシアルの中の人が技術と想いのたけをつづるブログです

走れ!ラズベリーパイ!

こんにちは、斉藤です!

みなさんは、Raspberry Pi(ラズベリーパイ)というものをご存知でしょうか?5v 700mAという低電力で動く小さなコンピューターです。
こんなもの
Raspberry Pi公式サイト

Linuxをインストールすることができるので、WEBサーバーや、ファイルサーバーとして活躍させることができます。今回はこれを使って、こんなラジコンカーを作ったので、その過程をご紹介します。



IT系エンジニアらしく、LinuxiPhoneの話も出てくるのですが、電子工作だったり、機械の機構だったりの話も出ますので、興味のある方はお付き合いいただければと思います。
また長くなりますので、何回かに分けて連載する予定です。

第二話はこちらから





* Raspberry Piとは?


Linuxをインストールすることができる小型のコンピューターです。以下の入出力を持っています。


* USBポート x 2
* DMI出力端子
* オーディオ出力端子
* GPIO: 汎用的な電気信号を扱うための端子。電子回路との接続インターフェースになる。

特に、このGPIOという入出力が重要となってきます。
コマンドラインもしくは、プログラムから、電子回路用の信号を出力することができます。もっと平たく言うと、例えばここにLEDを接続して、点灯を制御することができます。

このRaspberry Piを購入して、動かす(SSHログインなど)までの過程は、以下のページを参考にしました。
Arch Linux on Raspberry Pi
約3,300円で買えるLinuxパソコンRaspberry PiをMacで使う



* 仕様と実装方法


ラジコンを作るにあたり、まずは仕様を考えます。


* 前進後退
* 速度調整
* ステアリング
* 電源を搭載
* 無線化
* リモートでのコントロール

というところでしょうか。これを実装する方法として、それぞれ以下を検討。

- 前進後退
この用途専用の電子回路、モータードライブ回路を自作します。ただし、モーターを制御するには、Hブリッジ回路というものがすでに考案されているので、これを使います。この回路は四つの入力を持ち、それらを切り替えることで、正転、逆転、停止、ブレーキを実現できます。
また、Hブリッジ回路を組む用途のICも販売されていますので、これを購入して実装します。
Hブリッジ回路について、もっと詳しく

- 速度調整
PWM(Palse Width Modulation)という方法を使います。信号のON/OFFを一定間隔で繰り返すことで、出力する電圧の平均を制御する方法です。例えば5v直流電圧にもかかわらず、3v直流電圧と見なして扱うことが可能になります。モーターは掛ける電圧によって、トルクも比例して変動しますので、結果として速度調整をすることができます。このPWMをプログラムで生成します。
PWMについて、もっと詳しく

- ステアリング
おもちゃ屋でも買えるタミヤのギアボックスを用意しました。モーターが左右別々についているため、これでステアリング出来るだろう、と考えました。
ダブルギヤボックス(左右独立4速タイプ)

- 電源を搭載
ラジコンなので、電源ケーブルによる行動の制約を受けないことが必要になります。これは、モーター用電源とシステム用電源と二つに分けます。モーターに負荷が掛かったとき、電流が変動するので、それによってシステムが壊れることを避けるためです。
最終的にどんな電源を使ったかは、次回以降の記事で紹介します。

- 無線化
ここで、Raspberry Piを使います。
当初はArduinoを使うことを考えており、iPhoneで操作できるようにBluetooth通信をしようと思っていました。が、あまり知識が無く、デバッグも大変だろうということから、なかなか開発に着手できませんでした。
そんなとき、Raspberry Piを知ったのですが、Arduino同様GPIOが存在すること、無線化がIPレイヤー(WiFi)で実現可能なことから、こちらを使うことになりました。
特にLinuxを搭載することで、"SSHログインができる車"というアイデアが自分には革新的に思えたので、こちらを採用することに決定。

- リモートでのコントロール
無線化の話でも触れたように、当初はBluetooth通信によって、iPhoneなどのモバイル端末で操作することを考えていました。
が、Linuxを使うことになり、モバイル端末から送信した制御用パケットを受信して制御することになりました。


以上、仕様を実現するための実装方法が揃いました。
システム構成図を描くと、以下の様になります。

早速一つづつ着手。



* モータードライブ回路による前進後退の実装


Raspberry PiのGPIOから出ている電気信号を直接モーターに繋いでも、モーターを動かすだけの電力を得ることはできません。それどころか、運が悪ければ、Raspberry Piが壊れてしまいます。そのためトランジスターを間にはさみ、Raspberry Piの信号によってモーター用バッテリーの電力を制御する必要があります。モーターを制御するだけでなく、そういったためにもこのモータードライブ回路が必要になります。
ちなみに、トランジスターは電気的なスイッチ(信号によって、別の電流を流したり、止めたりできる)のようなものだと思ってください。

これをブレッドボードで実験します。


問題なく動くことが分かったので、ハンダ付け実装。モーター左右別々にHブリッジ回路を組んでいるので、二つICがついています。


また、コマンドラインから、信号のON/OFFができます。
コマンドライン上から、以下のコマンドを実行。


$ echo "4" > /sys/class/gpio/export
$ echo "out" > /sys/class/gpio/gpio4/direction
$ echo "1" > /sys/class/gpio/gpio4/value

これで、GPIOの特定の端子(この例では、4番のGPIO端子)から信号を出力させることができます。
ここまでで、自作の電子回路とRaspberry Piを接続することが出来ました。
GPIOについて、もっと詳しく



* プログラムによる速度調整の実装


ただし、上記コマンドでは単純なON/OFFは出来るものの、速度を変更することができませんので、ここでPWMを導入します。
以下のライブラリを使って、c言語でプログラムを組みます。
Raspberry Pi向け C言語PWMライブラリ
c言語用、Python用、nodejs用など、探せばライブラリはいくつかあるようです。手持ちのRaspberry Piでは、コンパイル速度の速さから、c言語用のライブラリを使用。

コマンドラインからプログラムを実行して、動かしている様子が以下の画像になります。

タイヤが勢いよく回転してますね!

ここまでで、WiFiによる無線通信によって、モーターの制御するという、ラジコンの核となる部分ができました!Linuxを使えば、あっさり無線化できることに感動。。。





* いったん、まとめ


だんだんとラジコンカーが実現に近づきました。
特に、無線化のおまけでLinuxという環境がくっついてきたことが、とても大きかったと思います。
その気になれば、MongoDBやMySQLを使って、走行データのログ収集したり、nginxでマシンの状態をWEBインターフェースで配信したりできるんじゃないかとワクワクしますね!
とりあえず、次回は車体作りとステアリングの過程について、触れてみたいと思います。

第二話に続く!



* 参考情報


Raspberry Pi 使用例まとめ
ミニ四駆×Arduino×Bluetoothで“夢のミニ四駆”を作ろう
トヨタが「Tizen IVI」の開発に参加、車載情報機器のLinux採用に本腰

nginx(リバプロ)とapache(Webサーバ)でアクセス要求制御をしよう

皆様、ご無沙汰しております。笹亀です。
ブログを書くのが、去年以来となってしまい、年が変わってしまいました・・
遅れましたが、今年度もよろしくお願い致します。

本日はリバースプロキシとWebサーバを利用したサーバ構築方法についてご紹介したいと思います。リバースプロキシとは、外部のネットワークを内部のネットワークへ処理の振り分けをするサーバのことを示します。リバースプロキシは長いのでリバプロと言います。
一般的には下記のようなリバプロを使用せずにWebサーバへ直接アクセスをさせる方法が多いのです。


リバプロを使用した場合は、リバプロが受けた要求をWebサーバへ転送して、Webサーバ側でコンテンツを表示するといったフローになります。


大規模なサイトを構築する場合には役割をわけるためにリバプロを使用した構成で構築されていることもあります。nginxとApacheを使用しますが、どちらもWebサーバとしてもリバプロとしても動作させることが可能です。今回はnginxの方が高速に動作するということからnginxをリバプロにし、WebサーバをApacheにしたいと思います。

まずはnginxとApacheをインストールします。
 ※インストール処理はMacPortsをインストールしていることを前提とします


$ sudo port install nginx
$ sudo port install apache2


Apacheの設定ファイルを変更します。
80番はリバプロ側で処理するためApacheではListenポートを8010に変更する


nginx側の設定を変更します。
インストール後は「/opt/local/etc/nginx」に設定ファイルのサンプルがあり、
通常はこちらを参考にして設定しますが、今回は設定ファイルを下記のように設定します。

1.mimeタイプはデフォルトのものを利用します


sudo mv /opt/local/etc/nginx/mime.types.default /opt/local/etc/nginx/mime.types


2.nginx.conf(基本設定)を作成します


sudo vim /opt/local/etc/nginx/nginx.conf

設定内容:


user  nobody;
worker_processes  1;  

error_log  logs/error.log  info;

events {
    worker_connections  256;
}

http {
    include       mime.types;
    default_type  application/octet-stream;
    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  logs/access.log  main;
    sendfile        on; 
    keepalive_timeout  65; 

    proxy_headers_hash_max_size 1024; 
    proxy_headers_hash_bucket_size 256;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header Host $host;
    proxy_read_timeout 600s;
    proxy_connect_timeout 10s;

    include domains/*.conf;
}


3.基本設定で読み込んでいるdomains/localhost.conf(ドメインごとの設定)を作成します。


sudo vim /opt/local/etc/nginx/domains/localhost.conf

設定内容:


upstream web-apache {
  server localhost:8010;
}

server {
  listen       80; 
  server_name  localhost;
  access_log  logs/localhost.access.log  main;
  error_log logs/localhost.error.log debug;
  location / { 
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $remote_addr;
    proxy_set_header X-CB-REMOTE-ADDR $remote_addr;
    proxy_set_header Host $host;
    proxy_pass http://web-apache/;
  }
  
}

※port 80番のアクセスを受けたらlocalhost:8010(apache)へ転送する

4.ログ出力先のフォルダを作成して、権限を与える


sudo mkdir -p /opt/local/logs/
sudo chmod -R 777 /opt/local/logs/


ここまでで基本的なリバプロの設定完了です。
まずはnginxをスタートさせ、httpでローカルにアクセスします。


sudo nginx -c /opt/local/etc/nginx/nginx.conf


Apacheの受け側が起動していないため、nginx側でエラーとなることがわかります。

それでは、受け側のApacheをスタートさせ、再度、httpでローカルにアクセスします。


sudo /opt/local/apache2/bin/apachectl start


正しくApacheのhtdocsが表示されれば、リバプロにて転送がうまくいったことになります。

この例のようにリバプロとwebサーバを分けて構築することでリバプロでWebアクセスを一元管理し、処理させたいWebサーバをリバプロで制御をすることができます。例えば、ドメインごとに処理をさせたいWebサーバを選択して専用に処理をさせるようなことも行えますし、負荷がかかる特定のエイリアスに専用の処理をさせるWebサーバを設定するといったことも可能です。

リバプロサーバとWebサーバは分けて使うことでメリットが得られますので、サーバの台数的には増えてしまうというデメリットもあります。採用する際はメリットとデメリットの両方をしっかり考えて、採用をする必要がございます。

検証するだけでしたら、設定例のようにローカルマシーン1台でも試せますので、ぜひご興味がある方はお試しくださいませ。

Linux でフォントのレンダリングを好みのものにする方法

PC を使っていると文字を見るのは避けては通れませんよね。
特に近年は日常的に PC を利用する機会が増えた為、長時間文字を見ることになりがちです。
文字が綺麗に表示されていれば、読みやすいだけでなく目の負担も心なしか減り、作業効率も向上します。
そこで今回は Linux のフォントのレンダリングを自分好みのものにする方法をご紹介します。

Linux では fonts.conf で各種設定・調整が行えますが、今回ご紹介するのは freetypeInfinality.net にて配布されているパッチを当てる方法です。
このパッチは環境変数を用いて freetype にパラメータを渡せるようにし、レンダリングを調整できるようにします。

Linux も近年は綺麗なレンダリングを行なっていますが、個人的には Mac の様な太く濃いレンダリングが好みなので、調整する際の目標は Mac のそれになります。
(ちなみに Windows であれば mactype を利用することでレンダリングを調整することができます。)

では早速パッチを当てた freetype をビルドしましょう。
なお、以下の内容は Kubuntu 12.04 を対象としています。
また、Fedora や Arch Linuxパッケージが用意されていますので、自前でビルドする必要はありません。

パッチを当てた freetype をビルドする



まずは Infinality Freetype Patches と freetype のソースを入手します。


$ curl -LOJ http://www.infinality.net/fedora/linux/zips/freetype-infinality-2.4.10-20120616_01-x86_64.tar.bz2
$ curl -LOJ http://downloads.sf.net/project/freetype/freetype2/2.4.10/freetype-2.4.10.tar.gz


続いて展開し、パッチを当てます。


$ tar xvf freetype-infinality-2.4.10-20120616_01-x86_64.tar.bz2
$ tar xvf freetype-2.4.10.tar.gz
$ cd freetype-2.4.10
$ patch -p1 < ../freetype-add-subpixel-hinting-infinality-20120616-01.patch
$ patch -p1 < ../freetype-enable-subpixel-hinting-infinality-20120615-01.patch
$ patch -p1 < ../freetype-entire-infinality-patchset-20120615-01.patch

※パッチは上記の順で当てる必要があります。

パッチ当てが完了したらビルド〜インストールしましょう。
(今回は /usr/local 配下にインストールします)


$ ./configure
$ make
$ sudo make install


パッチを当てた freetype を各プログラムが参照するようにする



せっかくパッチを当てた freetype をインストールしても、各プログラムがそれを参照してくれなくては意味がありません。
そこで ldconfig でパッチを当てた freetype を認識させるようにします。
(この方法以外にも環境変数 LD_PRELOAD を使う方法がありますが、今回は割愛させて頂きます。)
が、Kubuntu 12.04 ではデフォルトで /usr/local 配下のライブラリが優先されるようになっているはずなので、何もしなくても以下の様に今回インストールした freetype が最初に現れるはずです。


$ ldconfig -p | grep libfreetype
        libfreetype.so.6 (libc6,x86-64) => /usr/local/lib/libfreetype.so.6
        libfreetype.so.6 (libc6,x86-64) => /usr/lib/x86_64-linux-gnu/libfreetype.so.6
        libfreetype.so (libc6,x86-64) => /usr/local/lib/libfreetype.so


なお、ldconfig -p の結果に表示されなかったり、順序が後の方だったりした場合は適宜 ld.so.conf 等を修正する必要があります。

fontconfig による設定



さて、ここまででパッチを当てた freetype が利用できるようになりましたので、続いては設定です。
設定は fontconfig を使った設定と、環境変数を使った設定の2つがあります。

まずは fontconfig の設定を行いましょう。

設定ファイルが用意されていますので、それを /etc/fonts に配置します。


$ cd /etc/fonts
$ curl -L http://www.infinality.net/fedora/linux/zips/fontconfig-infinality-1-20120615_1.tar.bz2 | sudo tar jxvf -


いくつかプリセットが用意されています。


$ ls /etc/fonts/infinality
debug  infinality  linux  osx  osx2  win7  win98  winxp


今回は osx を使ってみます。


$ cd /etc/fonts/infinality
$ sudo ./infctl.sh setstyle osx


環境変数による設定



続いて、環境変数を使った設定を行いましょう。
レンダリングのパラメータを設定するため、好みのレンダリングにするにはこちらの設定が重要になります。

先ほどの Infinality Freetype Patches の中に infinality-settings.sh というスクリプトが含まれていますので、このスクリプトを改変・利用し、設定を行います。
まずはホームディレクトリにコピーし、ログインシェルの設定ファイルでログイン時に読み込まれるようにします。(以下は zsh の例です)


$ cp infinality-settings.sh ~/.infinality
$ echo "[[ -f ~/.infinality ]]  & & source ~/.infinality" >> ${ZDOTDIR}/.zshenv


先ほどの設定と同様、このスクリプトにもいくつかのプリセットが定義されています。
例によって OSX を使ってみましょう。
コピーしたファイルをエディタで開き、


USE_STYLE="DEFAULT"


となっている部分を


USE_STYLE="OSX"


と書き換えます。

これで大まかな設定が完了しました。
反映を確認するには一度ログアウトして再度ログインします。

結果



それではスクリーンショットで変化を確認してみましょう。

まずはデフォルトの状態。



続いてプリセット OSX を適用した状態。



どうでしょうか?
適用後の方が若干文字が濃くなっているのがわかるかと思います。

今回はプリセットを使いましたが、もちろんプリセットを用いずに各パラメータを指定することができます。
上記スクリプトにはコメントで各パラメータの詳細な説明がされていますので、そちらを参考に設定されてみてはいかがでしょうか。
また、各パラメータの説明と実際に反映させてみたスクリーンショットをまとめたものを次回の記事にしようと考えています。

フォントのレンダリングの見栄えはお使いのディスプレイやコントラスト・ガンマ値などにより大きく変わるため、なかなか好みの見た目に調整するのは骨が折れますが、自分好みに調整された文字は見ているだけでもウットリできる魅力がありますので、ぜひお試しください。

Debianで指紋認証を使用する

最近は指紋認証を搭載したPCが増えてきました。業務用グレードなら大抵は標準装備かオプションで存在します。
便利そうなので(今更ながら)自分も導入してみました。

なお、今回導入したPCは Thinkpad X61s です。(6番で詳細)

1・モジュールのインストール
Linuxでは、pamを使って認証をしていますので、まずはpamにモジュールを追加します。
Thinkpad 指紋認証ググるとthinkfinger使えと出てきますが、今回はwheezy(testing)に入っているlibpam-fprintdを利用します。



apt-get install libpam-fprintd fprintd


これでpamの設定までしてくれます。簡単ですね。さすがパッケージ。
なお、/etc/pam.d/common-auth に下記のように追加されるはずです。


auth	[success=3 default=ignore]	pam_fprintd.so max_tries=1 timeout=10 # debug

ここのパラメータを調整して、リトライ回数とタイムアウトを設定できます。

2・事前準備
そのままだとxlockなどの一般ユーザ権限で動くアプリがアクセスできないため、
先に /usr/share/polkit-1/actions/net.reactivated.fprint.device.policy を書き換えます。
(ずっと/dev/bus以下のパーミッションが問題かと思ってハマっていました)

内容はただのXMLで、下記のように
net.reactivated.fprint.device.verify と
net.reactivated.fprint.device.enroll の
allow_any を yes に書き換えるだけです。


...
<action id="net.reactivated.fprint.device.verify">
...
   <defaults>
      <allow_any>no</allow_any> ←このnoをyesに書き換え
...
<action id="net.reactivated.fprint.device.enroll">
...
   <defaults>
      <allow_any>no</allow_any> ←このnoをyesに書き換え
...


3・指紋の登録

fprintd-enroll [ユーザ名]
で登録できます。
複数回指紋を読み取らせれば登録完了します。



hoge@:debian~$ fprintd-enroll hoge
Using device /net/reactivated/Fprint/Device/0
Enrolling right index finger.
Enroll result: enroll-stage-passed
Enroll result: enroll-stage-passed
Enroll result: enroll-completed


なお、manpageによれば、下記のように -f オプションで指を指定出来ます。


fprintd-enroll -f left-middle-finger hoge


ただし、pamの方で別の指選ぶ方法が不明で調査中です。
とりあえず右の人差し指だけでも使えれば便利なので、人差し指だけで使っています。

4・認証

通常のターミナルで認証や、sudo、XDMなどで指紋認証が使用出来るようになっているはずです。
xlockでは、パスワード認証のプロンプトが出るので、Enterを押してやると指紋を入力出来るようになります。

5・使用感

試しに導入してみましたが便利ですね。
xlock解除の手間が省けていい感じです。
ログインについては・・・ずっと起動しっぱなしなのでそれほど変わらず。

6・おまけ

自分のThinkpad X61s
CPU: Core2 L7300 @ 1.40GHz
RAM: 4GB

確か3,4年位使っていますが、性能は普段の開発なら十分です。
この間、ファンが壊れた事以外は特に問題もなく元気に動いています。
Thinkpadは保守パーツを購入可能なので、ファンは自力で修理。このままあと5年は戦えます。
社内ではいい加減買い換えろとか、macにしろとか言われますが。

nginxで認証用proxyサーバを作成

nginxと言えば、言わずと知れた高速なwebサーバ+ロードバランサです。
とにかく軽量で高速なので、apacheをこれに置き換えて高速化という話もよく聞くようになって来ました。

先日、このnginxとmemcacheを組み合わせてセッション認証サーバを構築したので、それについて書こうと思います。

1・システム概要


今回作るシステムは、静的コンテンツを配信するサーバがすでにあり、
・認証機構(ログイン画面)を追加
・変更を最小限に抑えて開発
・スケールアウトしやすい
・アクセス数が多いので、できるだけ処理を軽くする
ことを目的とします。

この手のシステムは、既存システムに手を入れるのが普通ですが、既存システムでプログラムが動かないことには話になりません。
という事で、認証機構を追加したプロキシサーバを作成することで実現したいと思います。

仮に、PHPのみを使用して実装する場合は、下記のように出来ます。
・ログイン画面はPHP
rewriteでログイン画面系以外の全てのアクセスをindex.phpに向ける
・認証に失敗したらログイン画面にリダイレクト
・認証成功したら認証状態をセッションに記録(有効期限も記録)
・セッションはmemcacheなどで全サーバ共有
・セッションの値が有効なときはコンテンツを取得して表示

よくある方法ですが、アクセス毎にPHP実行のオーバヘッドが発生するため効率は良くありません。
そこで、Nginx側から認証できればもっと速いはず。ということで、下記のような仕様にしました。

・ログイン画面はPHP
・ログイン画面で認証成功したら、ランダムなIDを発行してCookieとmemcacheに有効期限を設定して記録しておく
・Nginx側でcookieとmemcacheのIDを読み取り、IDが存在すればそのまま表示
・IDが存在しなければログイン画面にリダイレクト

つまり、全てのアクセスでわざわざPHPを呼び出すのは非効率なので、セッションの判定部分はNginxのみで解決することにより高速化します。

2・ログイン画面


本題では無いので、あくまでも概要です。

要は、認証に成功したら下記のようにするという意味です。
cookieにauthidというキーで、ランダム値を保存
・memcacheにランダム値をキーにして、認証状態(1 or 空文字)をセット



(略)
    if (@$_POST['id']  & & @$_POST['pw']) {

        $is_member = auth($_POST['id'], $_POST['pw']); // 会員認証

        $key = generate_random_value(); // ちゃんとしたランダム値を返す関数

        // cookieをセット
        setcookie('authid', $key, COOKIE_AUTH_EXPIRE, COOKIE_PATH, COOKIE_DOMAIN);
        
        // memcacheに入れておく値。1ならばログイン成功
        $memcache->set($key, $is_member ? 1 : '', false, MEMCACHE_AUTH_EXPIRE);

	header('Location: comp.php'); // ログイン完了画面
    } else {
        header('Location: login.php'); // ログイン画面のHTML
    }


定数は適当に読み替えてください。
また、このスクリプトはかなりいい加減なのでご注意ください。

3・Nginxコンパイル


今回はサードパーティのモジュールであるevalが要るので、ダウンロードしてコンパイルします。

こちらからtarでもgitでもいいのでダウンロードし
http://www.grid.net.ru/nginx/eval.en.html

Nginxのconfigureに下記のように、モジュールを解凍した先のディレクトリを指定します
--add-module=/path/to/nginx_eval_module

あとは普通にコンパイルしてインストールしてください。

4・Nginx設定


evalは下記のように、memcached_passやproxy_passなどのレスポンスを変数(この場合は$var)に代入してくれます。



location / {
    # 会員チェック
    eval $var {
        if ($cookie_auth) {
            set $memcached_key $cookie_authid;
            memcached_pass     localhost:11211;
        }
    }
}


memcached_passは、$memcached_keyに設定された値をキーとしてmemcacheから値を取得して表示するだけです。
なので、evalモジュールを組み合わせることによりphp側で値を設定しておけば、nginx側でそのままの値を取得出来ます。

あとはこの値を比較して、リダイレクトするかしないかを判定すれば完成です。



location /login/ {
    # php設定など(割愛)
    break;
}
location / {
    # 会員チェック
    eval $var {
        if ($cookie_authid) {
            set $memcached_key $cookie_authid;
            memcached_pass     localhost:11211;
        }
    }

    # $varが1ならそのまま
    if ($var = 1) {
        proxy_pass   http://webserver.example.com:80;
        break;
    }

    # それ以外ならばログイン画面
    rewrite ^(.*)$ /login/login.php permanent;
    break;
}


簡単に書けばこんな感じです。
この場合は、memcache側に1が入っていればログイン済みという扱いにしましたが、
工夫次第で、1なら会員、0なら非会員、値が無ければ未ログインなど権限による場合分けも出来ます。


5・使用感

簡単なテストプログラムを作ってabをかけてみました。
(純粋にモジュールのみチェックしたいので、認証成功なら0バイトのファイルを返すだけ)

webサーバ: X5680 3.33GHz 1コア 512MB
abサーバ : E5645 2.40GHz 2コア 1024MB

Nginx設定


location /ngx/ {
    eval $var {
        if ($cookie_authid) {
            set $memcached_key $cookie_authid;
            memcached_pass     localhost:11211;
        }
    }

    if ($var != 1) {
       return 403;
    }

    break;
}

location /php/ {
    include /etc/nginx/fastcgi_params;
    fastcgi_pass unix:/var/run/php-fastcgi/php-fastcgi.socket;
    fastcgi_index index.php;
    fastcgi_param SCRIPT_FILENAME /path/to/docroot/$fastcgi_script_name;
    break;
}

Nginxで認証するパスと、PHPをただ実行するパスの設定をしました。

PHP


<?php
$memcache_obj = new Memcache;
$memcache_obj->connect('127.0.0.1', 11211);

if (!$memcache_obj->get($_COOKIE['authid'])) {
    header('Status: 403 Forbidden');
}

Nginxの location /ngx/ 内でやっていることと同じ事をしています。

コマンド: ab -n 10000 -c 500 -C 'authid=hogehoge' http://xxx.xxx.xxx.xxx/ngx/test.php
     ab -n 10000 -c 500 -C 'authid=hogehoge' http://xxx.xxx.xxx.xxx/php/test.php
※/ngx/test.phpは空ファイルで、nginx側で特に設定していないのでパースされず、タダのHTML扱いになります。

5回実行した平均値

PHPのみ(spawn-fcgi) : 約500 req/s
Nginx eval使用 : 約1000 req/s
※Webサーバの性能が低いのしか用意出来なかったのと、さくらVPSからインターネット経由で某クラウド上のサーバにabかけていたので正確なデータは端折ります。

今回実験したサーバでは約2倍の性能差が出るようです。

ちなみに、vmstatも同時にとってみましたが、PHPはCPUをほぼ使い切っていたのに対し、Nginx evalの場合は2割程度残っていましので、チューニングによってはもう少し成績が上がる可能性があります。

そのうちにもう少し真面目なベンチマーク取ってみるかもしれません。
・・・誰かサーバと時間をください。

LVMのスナップショット機能を使ってXenイメージのバックアップ

今回は、社内サーバに関するバックアップの一環で、LVMのスナップショットを使用してXenイメージのバックアップをとってみました。

LVM(Logical Volume Manager)とは?


名前の通り、論理ディスクを管理する機能です。
通常のパーティションだと、サイズを動的に変更したり、複数のディスクにまたがって一つのパーティションにするなど出来ませんが、そこにLVMを噛ませることにより、柔軟に管理出来るようになります。

LVM概要


LVMは既存のパーティション上に乗って仮想化する機構なので、下記の3種類のレイヤがあります。
・PV(Physical Volume)
 物理的に書き込む領域です。(HDDのパーティションなど)
 領域をLVMのPVとして初期化した際に、PE(Physical Extents)という単位で分割され、この単位で論理領域の確保が出来るようになります。

・VG(Volume Group)
 PVをまとめたもので、LVMとして使用出来る領域になります。
 物理領域(PV)をまとめて一つの デバイス にしたものと思ってください。

・LV(Logical Volume)
 実際に使用する領域。
 VGにパーティションを切ってしたものとでも思ってください。

細かい説明はググってください。

スナップショットとは?


ある時点に記録されている状態を取っておいたもの。
スナップショットを作成した瞬間の情報を保持することが出来ます。

Xenのイメージなど、常時使用中で何か書き込みがあるファイルの場合、cpなどで普通にコピーするとコピー中もデータが書き換わり不整合が起てしまうので、スナップショットを作成してコピーすることにより、破損を最小限(突然電源を落とした程度)にとどめてバックアップ出来ます。
ただし、Copy on write で実装されている関係で、スナップショットを作成した瞬間から書き込みコストが増大しますので、恒久的なバックアップには使えません。(使えないこともないですが遅くなります)
そのため、今回はスナップショットからデータを吸い出したあとに消去する手法にしました。

スナップショットの作成


スナップショットの作成は下記のようにします。


lvcreate -s -L 2G -n "snap_20120719" "/dev/xenimages/xen-image-disk0"

簡単ですね。

-s スナップショットとして作成
-L スナップショット領域のサイズ(元ボリューム側で変更されたサイズがこのサイズを超えるまで使用可能。
  超えるとスナップショットのデータが壊れる可能性あり)
-n スナップショットの名前
元ボリュームのパス

となります。

実際の作業


基本的な流れは、下記のようになります。
・スナップショット作成
・マウント
rsyncで内容をコピー
・アンマウント
・スナップショット削除

毎回手動で実行するのは馬鹿馬鹿しいので、cronで実行出来るようにスクリプト化しました。
ついでに古いものの自動削除も。



#!/bin/bash

export PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"

function backup {
    orgvol="$1";

    # スナップショットパス定義
    snapvol="$orgvol-snap_`date '+%Y%m%d%H%M%S'`"

    if [ -b "$snapvol" ];then
        echo "$snapvol が存在します"
        return 9;
    fi

    if [ ! -b "$orgvol" ];then
        echo "$orgvol が存在しません"
        return 9;
    fi
    
    ret=0

## LVM自体バックアップ版
## 無駄が多いのでボツ
#    # スナップショットのエリアをddする
#    echo "バックアップ中..."
#    if ! dd if="$snapvol" | gzip > "$backupdir/`basename $snapvol`.gz";then
#        echo "ddの実行に失敗"
#        ret=2
#    fi

    # バックアップ先ディレクトリ作成
    snapbackupdir="$backupdir/snap_`basename $snapvol`"
    test ! -d "$snapbackupdir"  & & mkdir "$snapbackupdir";
    if [ ! -d "$snapbackupdir" ];then
        echo "バックアップディレクトリの作成に失敗"
        return 3
    fi
    # マウント先作成
    echo "tmpmount作成"
    test ! -d "tmpmount"  & & mkdir "tmpmount"
    if [ ! -d "tmpmount" ];then
        echo "マウント先ディレクトリの作成に失敗"
        return 4
    fi

    # LVMスナップショットを取る
    echo "スナップショット作成 `basename $snapvol`" "$orgvol"
    if ! lvcreate -s -L 2G -n "`basename $snapvol`" "$orgvol";then
        echo "スナップショットの作成に失敗"
        return 1;
    fi

    # スナップショットをマウントしてrsync
    echo "mount"
    if ! mount -o ro,nouuid "$snapvol" "tmpmount";then
        echo "マウントに失敗"
        ret="5"
    fi

    if [ $ret -eq 0 ];then
        echo "rsync中..."
        if ! rsync -auv --progress "tmpmount/" "$snapbackupdir/";then
            echo "rsync実行に失敗"
            ret=6
        fi
    fi

    # umount
    echo "umount"
    if ! umount "tmpmount";then
        echo "umountに失敗"
        ret=7
    fi

    # rmdir
    echo "rmdir"
    if ! rmdir "tmpmount";then
        echo "tmpmount削除に失敗"
        ret=8
    fi

    # スナップショットを削除
    echo "スナップショット削除"
    if ! lvremove -f "$snapvol";then
        echo "スナップショット削除に失敗"
        ret=4
    fi
    
    return $ret;
}

function rotate {
    # 1日分以降を削除
    backupdir="$1"
    for dir in $backupdir/snap_*; do 
        # unixtimeに変換
        utime="`echo $dir | perl -MTime::Local -pe 's/.*(201\d)(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)$/($6,$5,$4,$3,$2-1,$1)/; $_=(timelocal eval $_)."\n"'; `"
        time7="$((`date +%s` - 86400))"
        if [ "$utime" -gt 1240432473 ];then # 念の為
            if [ "$utime" -lt "$time7" ];then
                rm -rfv "$dir"
            fi
        fi
    done
}

# バックアップするデバイス スペース区切りで複数指定可
lvmnames="/dev/xenimages/xen-image-disk0"

# バックアップ先
backupdir="/mnt/backup"

rotate $backupdir;

# 全てのボリュームに対して実行
for orgvol in $lvmnames;do
    backup "$orgvol"
    res="$?"
    if [ "$res" -ne 0 ];then
        echo "code: $res" | /usr/bin/mail -s "[`hostname`] LVM snapshot faild" mailaddress@example.com
    fi
done


単純ですがこんな感じで書いて見ました。
これを毎日1回実行すればスナップショット使用してバックアップ出来ます。
なお、ディレクトのパスとか名前は環境によって違うのであくまでも 参考まで にしてください。
もし実際のサーバで試す場合には、壊れてもいいサーバでテストを入念にしてください。

バックアップしようとして、全部消えるとか洒落にならないですよね。
ちょっとしたバグで、多量のデータが消えると言う悪夢がたまにあるので・・・。

使用感


弊社の場合、対象が400GB程度ある+バックアップ先にGlusterFSを使用しているためバックアップ自体は遅いですが、数時間あれば終わります。
バックアップ中はパフォーマンスは落ちますが、深夜なのでそれほど苦情は出ていないようです。
※詳細なパフォーマンスについては測定もできますが、本番運用中+GlusterFSを使用しているため速度的に不安定で大したデータは取れないので、そのうちに環境できたらやるかもしれません。

また、この方法だとディスクのフルバックアップになるため無駄が多くなります。どうにかして差分バックアップ出来ないか模索中なので、出来たらブログの記事にします。

以上、LVMスナップショットを使ったバックアップの紹介でした。

Privoxyで透過型書き換えプロキシを作ってみた

今回は会社や家庭で便利に使えるプロキシ Privoxy についてまとめてみようと思います。

1・プロキシ(Proxy)とは


直訳すると「代理」
書いたままの意味で、ある通信をする際にデータを直接提供元に取りに行くのではなく、別の代理サーバを経由して取りに行くようにできるシステムです。
プロキシサーバはそれを提供しているサーバで、一般的にプロキシといえばHTTPプロキシを指すことが多い。
今回の記事もHTTPプロキシの意で使用します。

2・Proxyの利点


64Kbpsのモデムで通信していた時代、全く更新がないページをダウンロードするのは無駄以外の何者でもないので、一度見たページはプロキシサーバを使用してキャッシュして高速化という使い方もありましたが、最近は回線が太くなりプロキシを経由するボトルネックの方が大きくなって来ました。
そこで、今導入する利点を挙げて見ました。(自分が思いつく限り)

・フィルタリング
 IPレイヤではできない、HTMLの内容を見て判定。
 今回の記事で解説。
・通信の圧縮
 ziproxyあたりを入れて、通信を圧縮したり画像の画質を下げて軽くする。
 スマートフォンでこのプロキシを使用して快適に見るとか出来ます。
 OperaTurboもこれを使用。
・匿名性の確保(?)
 こちらもだいぶ昔から言われている利点で、サーバ側の設定によってはクライアントの情報をすべて隠蔽できる。(サーバ側のものになる)
 ターゲットサイトの管理者レベルから見たら何もわからないが、「サーバのログを押収する権限のある機関」には多分無力なので、悪いことはしないように。

上記3件はクライアントが使いたいサーバを選択して機能を享受しますが、サーバ側(データ提供側)で使用されるプロキシがあります。
・リバースプロキシ
クライアントからは普通のWEBサーバとして振る舞います。
複数のWEBサーバにロードバランスするとか、URL書き換えたり、静的ファイルをキャッシュなどをして、サーバ側の負荷対策に貢献します。
このブログ見ている人だと、こちらのほうが馴染み深いんではないかと思います。


なお、どのタイプにも言えることですが、
サーバを公開する際には、セキュリティをしっかり設定してください。
千客万来にしておくと不正中継されて、悪用される可能性があります。


2・Privoxyとは


HTTPプロキシの一種。
名前の通り(?)プライバシを確保する機能が乗っています。
Cookieを無効化したり、サーバへ送るUAなどの環境変数を偽装したり、
HTMLの内容自体の書き換えも出来るので、よく広告ブロックに使用されます。
広告をブロックするなんて無料サイトの根底を揺るがしかねないので、
そんなけしからん機能使っちゃダメですよね。
もちろん、フィルタをかけ過ぎると重くなるので注意してください。

3・インストール





apt-get install privoxy

で入ります。

Redhat系でも、多分yumで入るのではないかと思います。

なお、デフォルトの設定で一般的な広告のサイズの画像やドメインをブロックしているので、設定変更は必要です。

4・設定


/etc/privoxy/にまとまっており、
/etc/privoxy/config が全てです。ここで各種く設定や別ファイルの読み込みを指定します。

通信の書き換えに使用する設定ファイルは
なんとか.action
なんとか.filter
の2種類があり、
〜.actionで適用するURL、〜.filterで実際に適用する内容を定義します。

自分の場合は、先にziproxyを経由するので、待受アドレスを下記のようにローカル限定。
どこからでも待ち受ける場合は0.0.0.0を書いてください。


listen-address  127.0.0.1:8118


フィルタは、全角文字の書き換えをしたいので下記のようにascii+各文字コード分を独自に定義しました。


filterfile user.filter
filterfile user.utf8.filter
filterfile user.sjis.filter
filterfile user.euc.filter


フィルタ本体の設定です。
まずは、user.actionに適用したいドメインを部分一致で列挙します。


{+filter{word_filter}}
.

{+filter{hogehoge_example_com_filter}}
hogehoge.example.com


最初の"+"はフィルタを追加、"-"を書けば、そのフィルタを外す命令です。
filterの次は、適用するフィルタ名です。(〜.filterで定義されているもの)

次にuser.filterに実行したいperl方式の正規表現を書きます。
いくつか指定していますが、
例えばWordPressで、「Back To Top」をピンク色で表示するプラグインがありますが、iPhone使いなので特にいりません。
スクロールするたびにフェードインして出てきて非常に鬱陶しいので、idを消して無効化してます。



{+filter{word_filter}}
s|takeMeUpContainer||g


その他、自分には不要なjsとか、cssとか画像とかを削除しておく快適になりますが、必要なものまで消してしまう可能性があるのでご注意ください。
その状態な事を忘れて、アプリケーションの開発やテストをすると訳のわからないことでハマるので(ry

なお、日本語をフィルタする場合には、utf8,euc,sjisでそれぞれ同じ内容を書いて指定すれば多分動きます。

user.action


{+filter{word_filter_utf8}}
.
{+filter{word_filter_sjis}}
.
{+filter{word_filter_euc}}
.


user.filter


{+filter{word_filter_utf8}}
s|.*ほげ.*|あぼーん|g

eucsjisでそれぞれ同じ内容を保存。

"ほげ"はサンプルですが、基本的に罵り合いか下品な広告くらいにしか使われない言葉を書いておくと便利です。
さすがにそのまま消すと文脈がよくわからなくなるので、あぼーんに変えてます。
どこかの掲示板見る場合には便利ですね。


4・透過型プロキシにする



通常、プロキシはブラウザにて指定しますが、ゲートウェイtcp通信をフックして強制的にプロキシを使用させる方法が透過型プロキシです。

Linuxでやるのは簡単で、
まずはprivoxyのconfigで、accept-intercepted-requestsを1にします。
(プロキシ特有の通信でなくても処理出来る機能を有効)



accept-intercepted-requests 1


あとはiptablesで、どこかの80へのアクセスが来たら、そのままプロキシサーバへ渡すと書けばOKです。


iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 80 -j DNAT --to 192.168.1.2:8080



ここまでで、Privoxyとサーバ側の解説は完了です。

5・フィルタ例(おまけ)



自宅の環境では使っていませんが、こんなことフィルタすると捗るかもしれない事を書いてみます。

■会社編
基本的にサボり防止とか流出防止とか。
ただし、アクセス禁止しすぎると何も調べられなくなるので注意。

youtube,niconico,yourfilehostなどへのアクセスは、「仕事しろページ」へリダイレクト
 または、「R18」とかの文字列を含んでいたらリダイレクト。

2ch.netへのアクセス禁止
 技術系スレならOKだが、雑談系には禁止。
 s|.*以下、名無しにかわりましてVIPがお送りします.*||名前欄デフォルトを追記しておく。

・まとめ系アクセス禁止
 上記と同じ。

・特定のアドレスに行く時には携帯のUAに偽装
 いちいちブラウザでUA切り替えなくても便利。

その他、いちいちログが残るので監査的なことにも使用できます。
フィルタリングするにもサーバの性能が必要なので、あまり複雑なことはできないかと思います。

個人的には、こんな細かいことで縛るよりも自由にした方がいいかと思いますが。


■自宅編
※以下は私個人の独断と偏見で書いています。あくまでも個人の意見です。

フィルタリングは教育所良くないサイト から 子どもたち を守るのに使えます。
子供はいませんが、純粋無垢な 子どもたち の事を考えてみました。

プロバイダや、ブロードバンドルータにもフィルタリングサービスはありますが、厳密に指定できないので不便。
そこで、上記のフィルタリングを緩くして、カバーしきれないところをブロックすると便利。

仮に過剰にブロックしても "健全なサイトと証明できれば外すだけ" なので問題ありません。

子どもたち に不要な単語を発見したらページを真っ白にしてしまいましょう。
※公共の場にそぐわない単語があるため、一部伏字にしています。


s|.*.エ□.*||gs
s|.*$ex*||igs
(中略)
s|.*竿.*||gs
s|.*抜.*||gs
s|.*糸.*||gs
s|.*果実.*||gs
s|.*花びら.*||gs
s|.*露.*||gs
s|.*林.*||gs
s|.*森.*||gs
s|.*丘.*||gs


また、暴力や麻薬関係にも制限を入れましょう。


s|.*酒.*||gs
s|.*タバコ.*||gs
s|.*ドラッグ.*||gs
s|.*調合.*||gs
s|.*ケンカ.*||gs
s|.*暴力.*||gs
s|.*暴走.*||gs
s|.*改造.*||gs
s|.*チューニング.*||gs
s|.*クラブ.*||gs
s|.*ダンス.*||gs
s|.*パフォーマンス.*||gs


実に不健全で子どもたちの教育に悪そうな単語が揃いました。
この調子で単語やパターンを増やしていけばいいと思います。

--

本当はフィルタなんか使わないほうがいいのですが、
情報を公開する悪い人間がいる以上、フィルタは必要なのです。

また、プロキシを入れることにより、見ようとしたURLの一覧も簡単に出力できます。
親はログを見るだけで、不健全なサイトを見ようとしたことを察知でき、
その情報を元に子供に正当な 教育 を施すことが可能となるのです。

・そもそも単語が汎用的すぎるって?
子どもたちを守るためなので仕方のないことなので、仕方のないことです。

もし、フィルタに引っかかったら、
とりあえず子供を連れてきて事情を聞いて"正当性を証明"させればいいだけなのです。
証明できなければ"教育"を施し、
過剰にフィルタリングしていれば、"正当な手続き"で解除すればいいだけですからね。

soxコマンドで音声ファイルを編集する10の例

こんにちは、浦本です。

今回は、サウンドファイルの編集に便利なsoxコマンドの使用例を紹介します。

sox (Sound eXchange) 配布元: http://sox.sourceforge.net/

soxを使うとコマンドだけでサクっとサウンド編集を行うことができます。
LinuxMacWindowsで利用可能です。
日々のサウンド編集だけでなく、Webアプリのバックエンド処理として活用しても面白いと思います。

ちなみに、soxネタは被らないだろうと思っていたのですが、
検索してみるとなんとアシアルブログの過去記事でも一度紹介されていました。さすがKさん!
以下はその続編として書きたいと思います。


【1】音量を上げる
+3dB上げる例です。
soxの基本的な使い方は「sox 入力ファイル 出力ファイル エフェクトオプション」となります。


$ sox in.wav out.wav gain 3


【2】リミッター付きで音量を上げる
音量を上げながらも、リミッターによって音割れを抑えることができます。


$ sox in.wav out.wav gain -l 3


【3】ノーマライズする
ピークが最大音量(0dB)となるように、自動的にgainを調節します。


$ sox in.wav out.wav gain -n


【4】リピートする
8回リピートした音声を出力する例です。
例えば1小節の素材を8小節にして保存できます。


$ sox in.wav out.wav repeat 8


【5】連結する
4ファイルを連結する例です。


$ sox inA.wav inB.wav inC.wav inD.wav out.wav


【6】フェードインする
先頭の5秒間の音量を徐々に上げてフェードインさせます。


$ sox in.wav out.wav fade t 5


【7】フェードアウトする
先頭から60秒の時点を終了位置とし、終わりの5秒間をフェードアウトします。


$ sox in.wav out.wav fade t 0 60 5

1番目の数字がフェードイン秒数(この場合0)、2番目が終了位置の秒数(残りはカットされる)、3番目がフェードアウト秒数です。

【8】ハイパスフィルタをかける
1000Hzより高音域を通します。


$ sox in.wav out.wav highpass 1000


【9】ローパスフィルタをかける
1000Hzより低音域を通します。


$ sox in.wav out.wav lowpass 1000


【10】サウンド情報を表示する
再生時間やサンプル数などの情報を表示することができます。
この場合、soxではなくsoxi(Sound eXchange Information)です。


$ soxi in.wav
Input File     : 'in.wav'
Channels       : 2
Sample Rate    : 44100
Precision      : 16-bit
Duration       : 00:00:01.90 = 84000 samples = 142.857 CDDA sectors
File Size      : 401k
Bit Rate       : 1.68M
Sample Encoding: 16-bit Signed Integer PCM


以上のように簡単な加工であれば手軽にサウンド編集ができるので、
皆さんもぜひ試してみてください。
私の場合は、DAWで調節するまでもないような日々のミックスを、
あとで聴きやすいように音量調節するため利用しています。

soxコマンドにはオプションが多く存在するため、
こちらのHTMLマニュアルを見ながら利用するのがおすすめです。
http://sox.sourceforge.net/sox.html

KURO-RSとJuliusで家電をコントロールしてみた

最近はLinuxの設定ばかりでしたが、今回は趣向を変えてLinuxを使って生活をちょっとだけ便利にする方法を書きたいと思います。

KURO-RSと言えば、玄人志向から発売された赤外線学習リモコンキット。
JuliusはOSS音声認識エンジンです。
これらを組み合わせれば音声認識リモコンを作ってみました。

はじめに言っておきますが、私の方法では割と誤動作するので、
・ミッションクリティカルな事(医療、軍事などの人の生命に影響する事柄、多額の損害が発生する事柄など)には使用しないでください。
・誤動作しても自己責任でお願いします。
つまり、照明の操作など誤動作しても影響の少ない(電気代くらい)ものでご使用ください。

下記の環境で試しました。
・OS : Debian wheezy
   Linux pc 3.2.0-1-amd64 #1 SMP Sun Feb 5 15:17:15 UTC 2012 x86_64 GNU/Linux
・HW : ZBOXNANO-AD1
 AMD Fusionなマシン。消費電力が最大でも30W程度なので経済的です
 GPUの支援機構によりyoutubeとかニコニコの動画も普通に再生可能。
 GPUの性能がそこそこあるので、WMではCompizを使用した方がサクサク動く。

・照明リモコン: リモコンコンセントOCR-05 07-0155、リモコンスイッチOCR-04 07-0154
 http://www.amazon.co.jp/オーム電機-07-0155-リモコンコンセントOCR-05/dp/B0013L6ACM
 http://www.amazon.co.jp/オーム電機-07-0154-天井照明器具専用-リモコンスイッチOCR-04/dp/B0013L6ACC/

1・KURO-RSの設定


まずはデバイスを接続して、ドライバを組み込む。


# modprobe ftdi_sio vendor=0x0411 product=0x00b3

起動時に組み込むため、/etc/modprobe.d/設定ファイルを作成してください。
面倒ならば、上記コマンドをrc.localにに追記でも可。
※/dev/ttyUSBxの権限を一般ユーザでも書き込めるようにしておくと、一般ユーザでも操作できます。
今回は全部sudo使って作業しています。

2・KURO-RSで赤外線信号を読み取り・送信


ここでperl使ってかっこ良く書けばいいと思いますが、
すでに開発されている先人の知恵をお借りします。
作者に感謝して使います。

http://www.gcd.org/blog/2007/01/113/

そのまま上記サイトの内容を実施すれば使えるようになります。
※実は、KURO-RSについては数年前から使用していますが、今見てみるとなぜか修正している場所があるため、もしかしたら上記のサイトのものでは動かない部分があるのかもしれません。
 動かない場合は自分で修正してみてください。(自分の方はなんのために修正したかわかればその部分を公開すると思います)

以降はこちらのスクリプトを使用できるという事で話を進めます。

コードを登録、送信については実際に実行して確認します。

※私の場合は、照明を3つコントロールするため×ON,OFFということで6つ登録しています。

3・Juliusのインストール


下記から Linux版 Juliusディクテーション実行キット(4.1) をダウンロードしてきます。
私の場合はせっかくなので本体の最新版(4.2)もダウンロードしました。

http://julius.sourceforge.jp/
(ダウンロードメニューにて、Julius最新版、ディクテーションキットそれぞれ)

本体を解凍し、configureを実行します。


./configure --prefix=/usr/local/julius --enable-julian

(中略)

****************************************************************
Julius/Julian libsent library rev.4.2.1:

- Audio I/O
    primary mic device API   : alsa (Advanced Linux Sound Architecture)
    available mic device API : alsa oss
    supported audio format   : RAW and WAV only
    NetAudio support         : no
- Language Modeling
    class N-gram support     : yes
- Libraries
    file decompression by    : gzip command
- Process management
    fork on adinnet input    : no
 
  Note: compilation time flags are now stored in "libsent-config".
        If you link this library, please add output of
        "libsent-config --cflags" to CFLAGS and
        "libsent-config --libs" to LIBS.
****************************************************************

configureの結果で available mic device API の項目に対応できるエンジンが表示されます。
ですが、自分の場合はalsaで動かなかったため(マイクがモノラル16bit非対応?)OSSエミュレーションを使用しました。(後述)

4・Juliusの実行


私の場合はここでかなりハマリました。
最初はalsaにて実行しようとしましたがスピーカーから音はするのにjuliusでもarecordでも録音できず、何をやっても無音状態になりました。
結局のところpulseaudioを入れたところ、なぜか動作するように・・・。
(このあたりは環境依存になる思いますので、うまく行かないようなら別のカード挿してみる、別のエンジンを試してみるなど試行錯誤してみてください。
行き詰まったら休憩するのも手です。何かいい方法を思いつくかもしれません)



padsp julius-4.2.1/julius/julius -C dictation-kit-v4.1/fast.jconf -input mic -input oss -charconv euc-jp utf8 

※padspはpulseaudioでOSSエミュレーションをするためのコマンドです。

こちらで、マイクに話した内容が表示されれば成功です。

5・Julius設定


リモコンに使うならば、認識できる語彙は少ない方が精度が上がります。


</s>    []      silE
<s>     []      silB
光あれ:ヒカリアレ:光あれ:507        [光あれ]    h i k a r i a r e
全て消す:スベテケス:全て消す:507        [全て消す]    s u b e t e k e s u
蛍光灯つける:ケイコウトウツケル:蛍光灯つける:507        [蛍光灯つける]    k e i k o u t o u ts u k e r u
蛍光灯消す:ケイコウトウケス:蛍光灯消す:527        [蛍光灯消す]    k e i k o u t o u k e s u
ライトつける:ライトツケル:ライトつける:507        [ライトつける]    r a i t o ts u k e r u
ライト消す:ライトケス:ライト消す:507        [ライト消す]    r a i t o k e s u
スタンドつける:スタンドツケル:スタンドつける:507        [スタンドつける]    s u t a N d o ts u k e r u
スタンド消す:スタンドケス:スタンド消す:507        [スタンド消す]    s u t a N d o k e s u

辞書ファイルの書式を参考にこんな感じで専用の辞書を作成しておきます。(-wオプションで使用可能)

「光りあれ」については、前にどこかで見た動画で「なにそれかっこいい」と思ったので採用。
誤動作で全部消えてもつけやすいように短い言葉にする目的もあります。
それ以外はそのままの意味です。

これでひと通りの動作はできますが、
Juliusのオプションは全て設定ファイルに書くことができるので、fast.jconfをベースに下記のように設定します。
このファイルを-Cオプションで指定してやれば毎回他のオプションをつける必要はありません。

dictation-kit-v4.1/julius.conf


-w dictation-kit-v4.1/word.list
-h dictation-kit-v4.1/model/phone_m/hmmdefs_ptm_gid.binhmm
-n 5
-output 1
-input mic # マイク入力
-input oss # oss使用
-zmeanframe
-rejectshort 800 # しきい値より短い入力を無視
-module # サーバとして動作(後述)
-charconv euc-jp utf8 # 入出力エンコード指定(内部 euc-jp 出力 utf8)
-lv 5000 


6・Juliusに接続



Juliusはサーバとして動作させることが可能で、-moduleオプションを指定してやれば、ポート10500番で待ち受けます。
(-module ポート番号 と指定すれば変更も可能です)

この待受ポートにソケット通信で繋いでやればデータの受信ができます。

詳細な仕様はこちらをご確認ください。
http://julius.sourceforge.jp/index.php?q=doc/module.html

私の用途ではperlで書けばこんな感じです。
(特定の文字列を拾ったらirrcに命令を出すだけです)


#!/usr/bin/perl
use Switch;
use Socket;

socket(SOCK, PF_INET, SOCK_STREAM, getprotobyname('tcp')) || die;
connect(SOCK, sockaddr_in(10500, inet_aton('localhost'))) || die;

$str = '';
while(1) {
        recv(SOCK, $message, 30, MSG_WAITALL);
        $str .= $message;
        if ($str =~ /\./) {
                print $str;
                switch($str) {
                        case /光あれ/ {
                                system("./irrc -c2 lOn");
                                sleep 1;
                                system("./irrc -c1 l2On");
                                sleep 1;
                                system("./irrc -c3 l3On");
                        }
                        case /全て消す/ {
                                system("./irrc -c2 lOff");
                                sleep 1;
                                system("./irrc -c1 l2Off");
                                sleep 1;
                                system("./irrc -c3 l3Off");
                        }
                        case /蛍光灯つける/ {
                                system("./irrc -c2 lOn");
                        }
                        case /蛍光灯消す/ {
                                system("./irrc -c2 lOff");
                        }
                        case /ライトつける/ {
                                system("./irrc -c1 l2On");
                        }
                        case /ライト消す/ {
                                system("./irrc -c1 l2Off");
                        }
                        case /スタンドつける/ {
                                system("./irrc -c3 l3On");
                        }
                        case /スタンド消す/ {
                                system("./irrc -c3 l3Off");
                        }

                }
                $str = '';
        }
}

# SOCKET切断
close(SOCK);


Juliusを起動した後、上記スクリプトを実行すれば接続できるはずです。

ついでに起動スクリプトも書きます。
本当は/etc/init.d/で起動できる形式にすればかっこいいと思いますが、とりあえずこれで。


#!/bin/bash

exec padsp julius-4.2.1/julius/julius -C dictation-kit-v4.1/julius.conf 2>/dev/null  &
sleep 2
exec /usr/bin/perl socket.pl  &



以上で音声認識リモコンシステムは完成です。
この起動スクリプトを実行し、マイクに話しかければ操作できるかと思います。

7・使用感


概ね正常動作していますが、電話中はもとより、掃除機などの音や全く関係ない独り言などでも誤動作するため注意が必要です。
もしくは、通常では拾わない音(びっくりするほどユートピア!!とか)を3回唱えると操作を受け付けるか全て無視するか切り替えできるように実装してもいいかもしれません。
とはいえ、誤動作するより声で操作できる便利さの方が勝ってます。

例えば、下記のような時に便利さを実感できます。
・寝る前にベッドの上で操作
・読書するときに操作
・両手に荷物を持って家に帰った時に操作
・むしろ、荷物持ってなくても家から出入りする時に操作

半分ネタで構築しましたが、思ったより便利に使っています。

8・その他


この内容は、以前設定した内容を思い出しながら書いたため、全部動作チェックはしていないため、動作しない部分があるかもしれませんがご了承ください。
※現行動いているシステムをリセットしてもう一度構築はしたくなく、その他に実行環境が無いので・・・。

OpenVPNで細々便利な設定

以前OpenVPNについて紹介しましたが、今回はもう少し深い部分について書こうと思います。

1・ルート追加
例えば、下記のようにVPNサーバのセグメント以外に接続先ネットワークを指定する場合に使用できます。


|クライアント|-----|VPNサーバ|------|接続先サーバorネットワーク|


■サーバ側からpushする方法
接続する全クライアントに対して適用させることができます。

設定方法は簡単で、サーバ側の設定ファイルに下記のように追加してください。
※L3を使用している場合に使用可能な方法です。
 L2を使用する場合は調べきれていないので割愛。


push "route 192.168.10.0 255.255.255.0"


pushにて第一引数にクライアントで実行するコマンドを指定、
routeは、第一引数に接続先ネットワーク、第二匹数にネットマスクを指定

上記設定のみで、接続先ネットワークに行くにはこのVPNサーバを使用するという命令を発行できます。
OpenVPNサーバを再起動後、クライアントから接続すれば適用されているはずです。

実は、下記のように書けばメトリックを指定できますので、
PCでは年中OpenVPNを起動しておいて、社内に移動した時は通常のLANを使用(Metricが0なので)、社外に出た時はOpenVPN経由にするなどの使い方ができます。


push "route-metric 100"


■クライアント側で指定
一部のクライアントのみ有効にしたい場合などは、クライアント側に指定もできます。
指定というか、起動時にrouteコマンドを実行するだけですが、


route-up "route add -net 172.16.0.0/16 gw 10.0.0.1"

route-up で、任意のコマンドを実行できるので、routeコマンドを直接実行します。

■その他、クライアント側で指定する場合
L2で接続している場合は、見える範囲なら普通にrouteコマンドを使用出来ます。
L3で接続している場合ですが、クライアント側から出ていくためのゲートウェイが存在しますので、そこに投げればOKです。


※クライアントが192.168.100.6の場合
$ /sbin/route -n
(ry)
192.168.100.5     0.0.0.0          255.255.255.255 UH    0      0        0 tun0 <- VPN側のGWへのルート
192.168.10.0      192.168.100.5    255.255.0.0     UG    100    0        0 tun0 <- 接続先へはVPNのGWを通る


2・暗号化方式の変更

下記のように指定してやれば暗号化方式を変更することができます。
速度と強度の兼ね合いでお好きな物をご使用ください。


cipher aes-256-cbc

方式のアルゴリズムや強度については割愛します。
なお、自分の場合はAES256を使用しています。

こっちの方が重要で、AES-NI対応のCPUの場合は数倍のスループットを引き出せるようです。
※自分の場合はサーバが対応していないため未検証
設定は下記のようになります。


engine aesni


以上、少し便利にする設定でした。