イヤ~ンな仕様

ファイルのアップロードでディスクへの格納に、お客様の名前のディレクトリを作成して格納するって事になっている。

このお客様の名前というのは、フォームの名前欄で、どんな文字でも入力できるので、わしなら絶対に採用しない仕様。

とりあえず、phpの関数しか使っていないし、ヤバそうな文字は予めエスケープしたり、文字置換したりしているからいいかと思っていたが、バックアップでファイルをリアルタイムでコピーするって話になってさあ大変。

最初は php の move_uploaded_file() が成功したら、バックアップディスクにもファイルをcopy() でコピーするコードを書いた。

しかし、ファイルコピーが終了するまで、phpスクリプトが終了しないので、完了時間が数秒延びてしまう。

ディレクトリが入力フリーダムな状況で、その文字列をシェル渡しにするなんぞ、絶対にやりたく無かったが、仕方がないので、exec() でバックグラウンド起動し、スクリプトはすぐに終了するようにした。

シェル渡しにすると何が問題になるのか?

たとえば、名前欄に「野泉雄嗣」と入れて、次のコマンドが実行される様な事を期待していたとする。

copy /var/www/upload/野泉雄嗣/filename.lzh /backupdisk/野泉雄嗣/filename.lzh

では名前欄に「; rm -rf /;」って入れたらどうなるか?

copy /var/www/upload/; rm -rf /;/filename.lzh /backupdisk/; rm -rf /;/filename.lzh

で、これがシェルに渡ると5つのコマンドと解釈される。
1. copy /var/www/upload/
2. rm -rf /
3. /filename.lzh /backupdisk/
4. rm -rf /
5. /filename.lzh

1,3,5はエラーで終わるが、2と4が実行されると、apacheに書き込み権のあるディレクトリのファイルが全部削除される。

php の copy() はパス名にこういうヤバい文字が含まれていても、うまい事握ってくれるが、exec()でやるとなると、この辺は自前で何とかしないといけない。

変なコマンドを実行されない為には、次の様にヤバい文字をエスケープすれば良い。

copy /var/www/upload/;\ rm\ -rf\ /\;/filename.lzh /backupdisk/\;\ rm\ -rf\ /\;/filename.lzh

escapeshellcmd()とか、ヤバい文字をエスケープしてくれる関数があるが、これはマルチバイト非対応で、漢字文字列があると全部削除される。

つまり「野泉雄嗣」の様な漢字のディレクトリが作られる事を想定しているプログラムには使えない。

とりあえず、escapeshellcmd()でエスケープする文字に空白とかも追加して
$str = addcslashes($str,’ \'”#&;`|*?~<>^()[]{}$\\’.”\x0A\xFF”);

とやったが、これってUTF-8の漢字の一部に該当するコードがあっても置換される予感がするなあ…。前に組んだmb_replace_string()って自作関数使った方が良さそう。

しかし、そもそも論で言えば、「何で人間が見て分かる形にする必要があるの?」ってのがある。

システム化というのは、「人間が見て判断する」という非効率でヒューマンエラーが発生しやすい状況を改善する事が目的である。

効率化を追求した結果、これ以上効率化が無理だからシステム化するってなら分かるが、非効率な手順は一切変えず、それを無理矢理システム化するってのは、システム化に対する幻想がある気がするな。

友人が電気店で見たある客がMacを指差しながら店員に「これ買ったらすごい絵が描ける様になるんですよね!?」って言ってたって話を思い出したな 🙂

Ubuntu 9.10のFDTI USB シリアル通信

会社でRBIO-2Uのシリアル通信プログラムが動かなくなったので、テストプログラムでテストしてみた。

ソース

Ubuntu 9.10 Desktop kernel 2.6.31

Ubuntu 9.04 Desktop kernel 2.6.28-16

9.04だと、”PCR01″って送信したら、”\r\nOK\r\n”って文字が返ってきている。
シリアルポートの設定は同じだが、read()で読み出した時の挙動が違う。
これは、kernel 2.6.31のFTDIのドライバか、シリアル関係がバグってそうな予感…。

Ubuntu 9.10

会社のマシンのUbuntu 9.04のアップデートマネージャが「9.10にアップグレードできます」とアップグレードさせる気満々だったので、ついポチっとボタンを押してしまった。

9.04で快調に動いていたRBIO-2U用の自作シリアル通信プログラムが動かなくなりました(´・ω・)

コマンドを送ったら、ちゃんとリレーは作動するので、送信はできてるっぽいが、受信で殆ど文字列が帰ってこない。1文字受け取るまで受信をブロックするってシリアル設定にしたら、read()で返ってこなくなってデッドロックする。

gtktermで繋ぐと問題無いので、カーネルヘッダか、それとも何かパッケージで抜けているものがあるのか…。

とりあえず、来週は、VirtualBoxで新規インストールして、シリアル通信プログラムをコンパイルして試すつもり。

これで、カーネルが悪いのか、アップグレードが悪かったのかが分かるだろう。

WordPress アップデート

アップデートしたら、皇紀の表示が西暦になった orz

単に表示部分で表示データひったくって660年足してるだけだが、アップデートにも強い改造法を編み出さねば。

ATCOM IP08

会社でATCOMのIP08の設定をやった。

同僚が設定していて、転送設定ができないとの事で、わしが見る事になった。
BAPsのfirmwareアップデート済みだったが、#を押しても”Transfer”っておねいちゃんの声が聞こえず、DTMFがスルーされてる感じ。

シリアル端末で、
ipkg remove asterisk-1.4.4
ipkg install asterisk-1.4.20

ってasteriskのバージョン上げても変わらず、しまいには、Grandstreamの電話機では繋がらず、ソフトフォンなら繋がるという謎の現象になってしまったので、BAPsのファームウエアは放棄!

ATCOMのDownload centerから、ファームウエアを落っことしてアップデートすると、転送の#押下は認識するようだが、電話がブチ切られる。

asterisk -vvvr でモニタすると、pbx-transferの音声ファイルが開けませんとかいうログが出ていた。

ファイルの位置を調べると、
/persistent/asterisk/sounds
とかにあるみたいで、

/var/lib/asteriskのディレクトリで、
ln -s /persistent/asterisk/sounds .

とシンボリックリンク貼ってやったら動いた。

AsteriskNowは、asteriskを作ったdigiumが作っただけあって、非常に良くできている。

パッケージのビルドさえ完璧であれば、GUIで設定できて、簡単なのだが、そうでない場合は、Linux弄る知識が無いと、とても触れたもんじゃない。ちょっと残念な製品だな。

シグナルハンドラ

シグナルハンドラ内では、malloc()やprintf()系が使えず、thread safe な関数しか呼べないので、シグナルハンドラではキューに記録して、メインルーチン内でキューを取り出して処理する形式を考えてみた。

実際に動作させてみると、0.5秒毎にタイマーが起動してキューにシグナル番号が積まれるが、メインルーチンで、0.05秒毎にキューをチェックするので、キューに積まれるシグナル番号は殆ど増える事は無く処理される。

30:30 sigevent:0
30:30 sigevent:0
30:30 sigevent:0
30:30 sigevent:0
30:30 sigevent:0
30:30 sigevent:0
31:31 sigevent:35
31:31 sigevent:0
31:31 sigevent:0
31:31 sigevent:0
31:31 sigevent:0
31:31 sigevent:0
31:31 sigevent:0

実行すると、こんな感じ。

表示しているのは、
キュー先頭(head):キュー末尾(tail) sigevent:シグナル番号
というフォーマットになっている。

しかし、Ctrl-Cを押しっぱなしにすると、メインルーチンでは、usleep()から抜けないので、キューにSIGINTが積まれまくる。

130:130 sigevent:0
131:131 sigevent:35
^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C132:42 sigevent:2
133:42 sigevent:2
134:42 sigevent:2
135:43 sigevent:2
136:43 sigevent:2
137:43 sigevent:2
138:43 sigevent:2
139:43 sigevent:2
140:43 sigevent:2
141:43 sigevent:2
142:43 sigevent:2
143:43 sigevent:2
144:44 sigevent:2
145:44 sigevent:35

ちなみにこのキューはオーバーフローすると、古いデータを捨てるようになっている。オーバフローを確認する方法は作ってない(笑)。

Ctrl-Cを離すと、メインルーチンでキューに溜まっているデータをせっせと処理し、キューの先頭のheadの数字と末尾のtailの数字がみるみる接近していくのが見て取れる。

これで、メインルーチンでシグナル割り込みを検知し、malloc()、printf()使いたい放題。
難点は、ちょっとリアルタイム性が悪くなるという点だな。

このプログラムはリアルタイムライブラリを使うので、コンパイル時には -lrt オプションがいる。

待ってくれないsleep

というC言語のプログラムだと、1秒待ってループってな感じだけど、create_timer()とかでタイマー作ってシグナル割り込みがあると、時間に関わらず、シグナルが発生した時点で、sleep()から抜けるんやな。

メイン処理で、sleep()で1秒間隔、タイマーの割り込みで0.5秒間隔でシグナルが発生するとすると、sleep()は必ず-1の戻り値で抜けてしまい、メイン処理は0.5秒間隔でループするんだな。

シグナルが来たら、メイン処理で何かするって組み方だと、sleep()がシグナルで抜けてくれた方がデッドロックが無くて良さそうだ。

RBIO-2U

社長にRBIO-2Uを買ってもらった。

USBインターフェースだが、Ubuntu 9.04 Desktopのマシンに繋いだら、USB Serialportとしてサックリ認識。

ターミナルで”PCR01″とかコマンド送るとリレーがカチカチ作動する。

使い方は色々あるが、例えば、パソコンの温度が高い時に、外付けの強力な電動ファンのスイッチを自動で入れるとか、炭酸ガスボンベのソレノイドバルブを開閉するとか応用次第で色々楽しそうな事ができそうだ。

リレー出力10、入力4だが、入力ポートが結構おもしろい。GNDと入力の線を短絡させて、RBIOに”FB0″とコマンドを送ると”0″と返ってきて、開放状態だと、”255″という文字列がシリアルから返ってくる。

これも色々と応用が効くが、出力と入力と合わせて、電動ねずみ取り機とか作れるな(笑)

餌のチーズに触れると、入力で検知して、リレー出力で扉をガシャンと電動で閉じるってな具合。果てしなく無用の長物感がするが、まあパソコンで入力の判断と出力ができるってのが色々応用できて、おもちゃとして面白い。

【Apache】穴

会社の自分のマシンは、NIC二枚挿しで、一方をネットワークテスト用のローカルネットが切ってある。

そのローカルネットで、motionを動かしているが、こいつは8081番のポートを、Webブラウザで叩いてやると、カメラ画像がモーションJPEGで見る事ができる。

これは、当然、ローカルネットの外では見る事ができないので、自分のマシンのApacheの特定ディレクトリの.htaccess で

RewriteEngine ON
RewriteRule  (.*)/ http://$1:8081/ [P]

なんてやって、ローカルマシンの motion のモーションJPEGを外部公開する設定にした。

http://localhost/hoge/camera/172.16.0.12/

とやると、外部から参照可能の自分のマシンのApacheが、
http://172.16.0.12:8081/

を実行して、先のURLのコンテンツを置き換えてくれる。

しかし、これって、camera以下のIPアドレス部分を変えれば、どんなホストにもアクセス可能だから、どうみても穴だよなあ…。

ただ、色々と制約があって、
・DNSは使えない
・閲覧するカメラの数は決まっていない。
・motionの動くマシンのネットワークは決定していない。

という理由から、mod_rewriteを使って、URLに任意のIPアドレスをぶら下げるというインターフェースしか思いつかぬ。

PHPで、proxyモドキを作ってみたが、JPEGの画像が書き換わってるのが分かる程、オーバーヘッドがでかいので、動画データの転送には向いてないから断念した。

なんかいい方法ないかなあ。

【PHP】設定ファイル書き出し

PHP でコンフィグレーションファイルを読むのに
parse_ini_file()があるが、書き込む関数が見当たらない。

webで検索しても見つからないので作ってみた。

これで一応、parse_ini_file()のサンプルが正常に展開格納できるが、Zend Frameworkにiniファイル書くヤツがあるというのが分かった。

しかし、Zend Framework の Zend_Config_Writer_Ini() と同じ結果だから、手軽に使うならこれでいいや。