d.sunnyone.org
sunnyone.org

ページ

ラベル Linux の投稿を表示しています。 すべての投稿を表示
ラベル Linux の投稿を表示しています。 すべての投稿を表示

2024-11-25

exiftoolとpolarsでよく撮っている焦点距離を調べる

スマホを買い替えて以来、カメラで撮るよりスマホのほうが撮りやすいところあるなあと思っていたのだけど、いまさら焦点距離の微妙な違いが影響しているのではないかと気づいたので、カメラ購入以来控えてきたレンズ購入を検討することにした。キットレンズで行けるところまで行こうと思っていたがついにこのときが来てしまった。

カメラのレンズはSONY FE 28-60mm F4-5.6なので、28mmスタート。スマホはPixel 8 Proで1xが24mmらしい。先の困り具合から24mmは必要ということがわかる。しかし、24mmで十分かはわからない。単焦点で良いのか?ズームのほうが良いのか?そこで実際に撮っている写真からどのあたりをよく使うのか調べてみることにした。

ステップ1: exiftoolで写真ファイル群から焦点距離を収集する

写真のファイルに付与されたEXIF情報にレンズや焦点距離は記録されている。このEXIF情報を取得するツールは様々あるが、exiftoolを使うとコマンドラインで取得することができる。

コマンド例は以下のとおり。

$ exiftool -LensId -FocalLength -csv -ext jpg 20230103-浅草
SourceFile,LensID,FocalLength
20230103-浅草/DSC04803.JPG,Sony FE 28-60mm F4-5.6,35.0 mm
20230103-浅草/DSC04799.JPG,Sony FE 28-60mm F4-5.6,28.0 mm
20230103-浅草/DSC04818.JPG,Sony FE 28-60mm F4-5.6,28.0 mm
20230103-浅草/DSC04814.JPG,Sony FE 28-60mm F4-5.6,35.0 mm

exiftoolはファイルだけでなくディレクトリも引数に撮ることができ、-ext jpgとすると拡張子の絞り込みまでできる(ARWとどっちかで良いので)。さらに、-csvでCSV出力にできるし、-LensIdと-FocalLengthのようにフィールドを限定することもできる。

1ファイルを指定するとすべての情報を出すくらいの機能しかないと思っていて、シェルスクリプトでも書いて回すかと思っていたのだが、コマンド一発で済んでしまった。

ステップ2: polarsで集計する

sqliteに入れてもよかったんだけど、Polarsというものが使われているらしいのでせっかくなので試してみた。PandasみたいなノリだけどSQLも受け付けるらしい。

インストールはpip install --user polarsで入れた。

$ ipython3
Python 3.10.12 (main, Nov  6 2024, 20:22:13) [GCC 11.4.0]
Type 'copyright', 'credits' or 'license' for more information
IPython 7.31.1 -- An enhanced Interactive Python. Type '?' for help.

In [1]: import polars as pl

In [2]: pl.Config(tbl_rows=10000)
Out[2]: <polars.config.Config at 0x7e5fd1f90400>

In [3]: df = pl.read_csv('/tmp/summary.csv')

In [4]: df.sql("""
   ...: WITH s AS (SELECT LensID,FocalLength,count(*) AS count FROM self GROUP BY LensID,FocalLength ORDER BY FocalLength),
   ...: a AS (SELECT count(*) as total FROM self)
   ...: SELECT s.LensID, s.FocalLength, s.count::float * 100 / a.total AS percent FROM s CROSS JOIN a
   ...: """)
Out[4]: 
shape: (33, 3)
┌────────────────────────┬─────────────┬───────────┐
│ LensID                 ┆ FocalLength ┆ percent   │
│ ---                    ┆ ---         ┆ ---       │
│ str                    ┆ str         ┆ f64       │
╞════════════════════════╪═════════════╪═══════════╡
│ Sony FE 28-60mm F4-5.6 ┆ 28.0 mm     ┆ 59.369925 │
│ Sony FE 28-60mm F4-5.6 ┆ 29.0 mm     ┆ 3.150373  │
│ Sony FE 28-60mm F4-5.6 ┆ 30.0 mm     ┆ 2.143553  │
│ Sony FE 28-60mm F4-5.6 ┆ 31.0 mm     ┆ 2.273465  │
│ Sony FE 28-60mm F4-5.6 ┆ 32.0 mm     ┆ 2.208509  │
│ Sony FE 28-60mm F4-5.6 ┆ 33.0 mm     ┆ 1.52647   │
│ Sony FE 28-60mm F4-5.6 ┆ 34.0 mm     ┆ 1.721338  │
│ Sony FE 28-60mm F4-5.6 ┆ 35.0 mm     ┆ 2.53329   │
│ Sony FE 28-60mm F4-5.6 ┆ 36.0 mm     ┆ 2.143553  │
│ Sony FE 28-60mm F4-5.6 ┆ 37.0 mm     ┆ 1.85125   │
│ Sony FE 28-60mm F4-5.6 ┆ 38.0 mm     ┆ 2.208509  │
│ Sony FE 28-60mm F4-5.6 ┆ 39.0 mm     ┆ 1.753816  │
│ Sony FE 28-60mm F4-5.6 ┆ 40.0 mm     ┆ 1.623904  │
│ Sony FE 28-60mm F4-5.6 ┆ 41.0 mm     ┆ 0.779474  │
│ Sony FE 28-60mm F4-5.6 ┆ 42.0 mm     ┆ 0.974342  │
│ Sony FE 28-60mm F4-5.6 ┆ 43.0 mm     ┆ 0.779474  │
│ Sony FE 28-60mm F4-5.6 ┆ 44.0 mm     ┆ 0.876908  │
│ Sony FE 28-60mm F4-5.6 ┆ 45.0 mm     ┆ 0.84443   │
│ Sony FE 28-60mm F4-5.6 ┆ 46.0 mm     ┆ 0.552127  │
│ Sony FE 28-60mm F4-5.6 ┆ 47.0 mm     ┆ 0.389737  │
│ Sony FE 28-60mm F4-5.6 ┆ 48.0 mm     ┆ 0.129912  │
│ Sony FE 28-60mm F4-5.6 ┆ 49.0 mm     ┆ 0.584605  │
│ Sony FE 28-60mm F4-5.6 ┆ 50.0 mm     ┆ 0.422215  │
│ Sony FE 28-60mm F4-5.6 ┆ 51.0 mm     ┆ 0.032478  │
│ Sony FE 28-60mm F4-5.6 ┆ 52.0 mm     ┆ 0.324781  │
│ Sony FE 28-60mm F4-5.6 ┆ 53.0 mm     ┆ 0.16239   │
│ Sony FE 28-60mm F4-5.6 ┆ 54.0 mm     ┆ 0.519649  │
│ Sony FE 28-60mm F4-5.6 ┆ 55.0 mm     ┆ 0.16239   │
│ Sony FE 28-60mm F4-5.6 ┆ 56.0 mm     ┆ 0.032478  │
│ Sony FE 28-60mm F4-5.6 ┆ 57.0 mm     ┆ 0.16239   │
│ Sony FE 28-60mm F4-5.6 ┆ 58.0 mm     ┆ 0.129912  │
│ Sony FE 28-60mm F4-5.6 ┆ 59.0 mm     ┆ 0.584605  │
│ Sony FE 28-60mm F4-5.6 ┆ 60.0 mm     ┆ 7.047743  │
└────────────────────────┴─────────────┴───────────┘

サブクエリで怒られたのとFROMにカンマ区切りにしたら怒られたから書き換えたけど、それ以外は自然に書けた。CTEも使えるしすごい。

端以外はサマってみたいので、polarsのやりかたも使ってみる。

In [80]: s2 = s.filter(pl.col("FocalLength").is_in(["28.0 mm", "60.0 mm"]).not_())

In [81]: s3 = s2.select(pl.col("percent"), pl.col("FocalLength").str.extract(r"^(\d)", 1).alias("T"))

In [82]: s3.group_by("T").agg(pl.col("percent").sum())
Out[82]: 
shape: (4, 2)
┌─────┬───────────┐
│ T   ┆ percent   │
│ --- ┆ ---       │
│ str ┆ f64       │
╞═════╪═══════════╡
│ 3   ┆ 20.363754 │
│ 5   ┆ 2.53329   │
│ 2   ┆ 3.150373  │
│ 4   ┆ 7.534914  │
└─────┴───────────┘

filter()とかselect()にはpl.col("name")を使って何かする。これはpolars.Exprpolars.Expr.strを見ればいいっぽい。 

結果を見て

集計した結果以下がわかった。

  • 28mmが約60%。めちゃ使う。
  • 29mm〜40mmはそれぞれ1%以上ある。まあまあ使うらしい。
  • 41mm〜59mmは1%未満。あまり使わないみたい。
    • 30mm台をまとめて20%, 40mm台は7.5%
  • 60mmは約7%。
24mm単焦点でも困らないかもしれないが、40mmまであると便利そうというところか。この結果を見つつ検討したい。

2023-03-21

YAPC::Kyoto 2023に参加してきました

京都観光も兼ねて、YAPC::Kyoto 2023に参加してきました。面白かった。遠征でのカンファレンスの参加は人生初なので、そういう意味でも新鮮な体験だった。

印象に残ったセッション

みんな面白かったのだけど、印象に残ったのはmacopyさんのデプロイ今昔物語だった。


よくわからないホスティング環境を借りてPHP(確か4だったと思う)のプログラムを「ホームページ」に置いてみたりとか、読めないPerlを書いて自分で困ったりしていた時代を思い出して、原点に帰った気分になれてとてもよかった。

遠征での学び

日曜日にYAPCで、京都にせっかく行くのだから土曜日は観光しようと決めていて、土曜日に観光するなら朝から行けたほうがいいよねということで金曜日に行って泊まり、日曜日遅くなるかもしれないから月曜日に帰ろうということで、結果3泊4日になった。歩数計を見ると40km近く歩いていることになっているので、結構満喫したと思う。

旅程に合わせてホテルを変えたので、服は洗濯しちゃえば運ぶの楽じゃないか?と思ってチャレンジしてみたものの、旅から帰ってきたあとコインランドリーに行き来して洗濯するのは結構しんどいというのは学びだった。洗濯と乾燥が一緒ならいいのにな~

荷物まわりはuzullaさんがテクニックをまとめてくれているので、今度参考にして楽をしたい。

俺的!遠方カンファレンスの参加体験向上テク 2023最新版 - uzullaがブログ https://uzulla.hateblo.jp/entry/2023/03/21/132421


2022-11-03

PulseAudioのデバイス接続時自動切替とデフォルト入出力の設定

Ubuntuを再起動すると入力デバイスが変わって音が拾えなくなる問題が起きていたのだけど、デバイス接続時にPulseAudioがデフォルトデバイスを自動切り替えする機能を切ればよくなりそうなので、無効にする方法をメモしておく(この環境では結果的にUSBオーディオデバイスがいくつもぶら下がっている形になっているので、認識順やつないでいる状態で順番が変わったりするのだと思う。バスパワーのハブにつながったカメラが怪しい)

PulseAudio 自動切り替えの無効化

自動切り替えを無効化するには、/etc/pulse/default.pa のmodule-switch-on-connectの記述をコメントアウトする。

### Use hot-plugged devices like Bluetooth or USB automatically (LP: #1702794)
##.ifexists module-switch-on-connect.so
##load-module module-switch-on-connect
##.endif 

デフォルト入出力デバイスの設定

これの前はデフォルトの設定を変える方法を試していたので、それも併せてメモしておく。

1. デバイスのnameを探す

汎用的には、pacmd list-sources / pacmd list-sinks でデバイスのname:を探す。
$ pacmd list-sources
(略)
  * index: 8
        name: <alsa_input.usb-C-Media_Electronics_Inc._MARANTZ_M4U_20190520-00.mono-fallback>
(略)

ここで出てきたname: を後で使う。アスタリスクは今デフォルトに選ばれているもの。

GNOMEの設定の「サウンド」で選択していれば、pactl get-default-source / get-default-sinkでも得られる。

$ pactl get-default-source
alsa_input.usb-C-Media_Electronics_Inc._MARANTZ_M4U_20190520-00.mono-fallback

2. 設定ファイルに記述する

/etc/pulse/default.pa.d/00-default-device.conf に以下のように記述する。
set-default-source alsa_input.usb-C-Media_Electronics_Inc._MARANTZ_M4U_20190520-00.mono-fallback
set-default-sink alsa_output.usb-Resonessence_Labs_Resonessence_Labs_HERUS-00.iec958-stereo
なお、pactlコマンドでも変えることができるので、シェルスクリプトに書くのも可。
$ pactl set-default-source alsa_input.usb-C-Media_Electronics_Inc._MARANTZ_M4U_20190520-00.mono-fallback

参考

安定のArchWiki。

PulseAudio/サンプル - ArchWiki

2021-09-22

Linux/Windowsデュアルブート環境のインストール方針

自分のWindows PCはLinux (Ubuntu) をインストールしてデュアルブートにすることが多い。 端末のタイプによってインストールの仕方が異なり、このやりかたが参考になるかもしれないので書いておく。
  1. 空のディスクが複数ある場合→1台ずつWindows, Linuxとインストール
  2. 空のディスクが1つのみの場合→Windowsをインストール後空いたところにLinuxをインストール
  3. Windowsが入ったディスクがある場合→パーティションを縮小してLinuxをインストール

空のディスクが複数ある場合

複数のディスクがPCに搭載できる/したい場合、それぞれにインストールする。まずWindowsをひとつのディスクにインストールしてから、別のディスクにLinuxをインストールする。 一番無難なかたち。

この場合では、データの受け渡しをするためのNTFSパーティションをLinux側のディスクにちょろっと用意しておくと、Windows側のCドライブをマウントしなくてよくて便利だったりする (NTFSマウントはなんだかんだあることがあるので)

空のディスクが1つのみの場合

ディスクが1つのみの場合、Windowsインストール時に容量を指定して(例えば半分)パーティションを作成し、インストール後、 空いた領域にLinuxをインストールする。 手元にあるPCがこのパターンで、こんな構成。

ディスク /dev/nvme0n1: 931.53 GiB, 1000204886016 バイト, 1953525168 セクタ
Disk model: WDS100T1X0E-00AFY0                      
単位: セクタ (1 * 512 = 512 バイト)
セクタサイズ (論理 / 物理): 512 バイト / 512 バイト
I/O サイズ (最小 / 推奨): 512 バイト / 512 バイト
ディスクラベルのタイプ: gpt
ディスク識別子: B1EA5819-908C-4E4F-8542-CCBD4314BXXX

デバイス        開始位置   最後から    セクタ サイズ タイプ
/dev/nvme0n1p1      2048     206847    204800   100M EFI システム
/dev/nvme0n1p2    206848     239615     32768    16M Microsoft 予約領域
/dev/nvme0n1p3    239616  818331110 818091495 390.1G Microsoft 基本データ
/dev/nvme0n1p4 818331648  819437567   1105920   540M Windows リカバリ環境
/dev/nvme0n1p5 819437568 1600688127 781250560 372.5G Linux ファイルシステム

この順番の理由は、Windowsが作る予約領域をきちんと作成したいというのと、ブートローダ(grub)を持つのはLinuxになるため。

Windowsが入ったディスクがある場合

ノートPCなど、最初からWindowsが入ったディスクの場合、まず回復ディスクを作成し、 その後Windowsのディスクの管理からボリュームを縮小、空のディスクが1つのときと同様に空いた領域にLinuxをインストールする。

2021-08-22

Ubuntu + SoftFM でFMステレオを受信する

RTL2832U + R820T2 のDVB-Tチューナを使うと、FMラジオが聞けるらしいということで買ってあったのだけど、久々にFMラジオが聴きたい機会がでてきたのでやってみた。 RTL-SDR についているrtl_fmというツールを使う話はよくあるのだけど、FMステレオ非対応なので、今回はSoftFMというツールを使ってみた。

インストール

依存パッケージをインストールする。
$ apt install build-essential cmake libasound2-dev librtlsdr-dev
/lib/udev/rules.d/60-librtlsdr0.rulesによると、デバイスを差し込むとplugdevというグループで使えるようになるようなので、 ユーザーをplugdevグループに追加する。
$ sudo usermod -aG plugdev foouser
SoftFMを https://github.com/jorisvr/SoftFM からgit cloneしてビルド
$ git clone https://github.com/jorisvr/SoftFM
$ cd SoftFM
$ mkdir build
$ cd build
$ cmake ..
$ make
$ sudo make install

使い方

softfm -f [freq]で受信できる。TOKYO FMならこんなかんじ。
$ softfm -f 80000000
softfm -f 79950000のほうが音質が良かったりするので試してみたほうがいいかも。-Wでファイルに吐けるが時間指定はなさそうなので、指定したければこんなかんじ。
$ timeout 60 softfm -f 80000000 -W tokyofm.wav

2020-12-21

tmuxで複数の開発サーバーを一括で起動する

Next.jsを複数動かす必要があったり、SPAをwatchしつつバックエンドも起動しておく、みたいな開発環境の場合、ひとつひとつ立ち上げるのは手間なので、tmuxで一括起動するスクリプトを作った。バックエンドは左にでかく、フロント達は右に、みたいなのは考慮せず、縦に並べるだけ。

2020-05-29

Drawio Desktop を使ってコマンドラインで図を画像に変換する

Webブラウザで使えるオープンソースのドローツールdraw.ioは便利なのだけど、図を描いたあと他フォーマットに変換のがちょっと面倒なので変換する話。

drawio-desktopをインストールする

draw.ioにはブラウザ版の他に、Electronで作られたdrawio-desktopというのがある。まずこれをインストールする。snapだと簡単。

# snap install drawio

drawioコマンドで変換する

drawio-desktopの実体であるdrawioコマンドには、コマンドラインオプションを指定すると指定フォーマットでエクスポートする機能がついているので、これを使って変換する。

$ drawio -x -f png *.drawio
シェルスクリプトにしておくと便利。ちなみにデフォルトはpdfらしい。

ヘルプを見るとこんな感じ。
$ drawio --help
Usage: drawio [options] [input file/folder]

Options:
  -V, --version                      output the version number
  -c, --create                       creates a new empty file if no file is passed
  -k, --check                        does not overwrite existing files
  -x, --export                       export the input file/folder based on the given options
  -r, --recursive                    for a folder input, recursively convert all files in sub-folders also
  -o, --output   specify the output file/folder. If omitted, the input file name is used for output with the
                                     specified format as extension
  -f, --format               if output file name extension is specified, this option is ignored (file type is determined from
                                     output extension, possible export formats are pdf, png, jpg, svg, vsdx) (default: "pdf")
  -q, --quality             output image quality for JPEG (default: 90)
  -t, --transparent                  set transparent background for PNG
  -e, --embed-diagram                includes a copy of the diagram (for PNG format only)
  -b, --border               sets the border width around the diagram (default: 0)
  -s, --scale                 scales the diagram size
  --width                     fits the generated image/pdf into the specified width, preserves aspect ratio.
  --height                   fits the generated image/pdf into the specified height, preserves aspect ratio.
  --crop                             crops PDF to diagram size
  -a, --all-pages                    export all pages (for PDF format only)
  -p, --page-index        selects a specific page, if not specified and the format is an image, the first page is selected
  -g, --page-range ..      selects a page range (for PDF format only)
  -h, --help                         display help for command

2016-02-08

PHPが出力するsyslogの「ool www」の謎を追う

PHPからerror_log()でsyslogを吐いたときに出力される「ool www」という謎の文字列が一体なんなのか調べた。いいから回避策が知りたいという人はこちらへ。おまけが本編という人もいるかもしれない。

発端

とあるPHPアプリケーションがエラーログをPHP標準関数error_log()で吐く。それをフィルタしたくなったのだが、error_log()にはたいしたフックポイントもなく、アプリケーション自体には手を入れたくないので、とりあえずsyslogで受けてSyslogサーバーがなんとかするのがよいかと思って「error_log」の設定を「syslog」にしてみると、なぜかこんな感じに……

Feb  7 22:02:36 phptest ool www: LOG CONTENT

……この「ool www」は一体なんなんだ? Out-of-L...うーん。ここの文字列だけなら実害はないが、なんでこんなことになっているのかわからないので調べることにした。

テストファイル

当該のPHPアプリケーションは置いといて、1ファイルでも再現するので、以下のPHPプログラムでテストすることにする。
<?php error_log("ERRORTEST"); ?>

環境はUbuntu 14.04にnginx + php5-fpm。設定値「error_log」を「syslog」にしておく。あまりバージョンに拘っていないので、リンクが適当だったりするが気になるかたは必要なバージョンのソースを見に行ってほしい。

ソースを追う

error_log()関数の流れ

くだんのerror_log()関数をPHPから呼び出すと、こんな感じで動く。

まず、error_logのPHP_FUNCTIONの記述がext/standard/basic_functions.cにある。ここが実体である_php_error_log_ex()を呼び出す。

ext/standard/basic_functions.cの_php_error_log_ex()は、switchで引数に応じたログ出力(メールなど)を行う。今回のmessageのみの呼び出しではdefault: にたどり着き、php_log_err()を呼び出す。

php_log_err()は、main/main.cにあり、ここで「error_log」変数のチェックを行い、値が「syslog」のときはphp_syslog()関数でsyslogに出力している。

if (!strcmp(PG(error_log), "syslog")) {
 php_syslog(LOG_NOTICE, "%s", log_message);
 PG(in_error_log) = 0;
 return;
}

基本的にメッセージを引き回しているだけで、ここまでに変わった文字列処理は見受けられない。

php_syslog()関数とは

であればポイントはphp_syslog()であろうと、php_syslog()の定義をphp_syslog.hに見つけるも、こうなっていた。

なんとphp_syslog()はsyslog()であった。つまり、PHP内で何か特別な処理をすることなく、そのままsyslog()関数にメッセージを投げているだけである。

syslog関数の使い方

こうなってくるとsyslog(3)が大事になる。manがあり、日本語が古くないようなので日本語のsyslog(3)を見てみる。

プロトタイプとしては、こうなっているようだ。
#include <syslog.h>

void openlog(const char *ident, int option, int facility);
void syslog(int priority, const char *format, ...);
void closelog(void); 

呼び出しとあわせると、priorityがLOG_NOTICEで、formatが"%s"、%s部分としてlog_messageが渡ることになる。

説明を読むと
ident で指定した文字列は各メッセージの前に付与される。
とあるので「ool www」の部分はidentのようだ。

処理の中でとくにopenlog()する処理は見受けられなかったので
openlog() は必須ではなく、必要に応じて syslog() から呼び出される。 syslog() が呼び出した場合、 ident のデフォルト値は NULL になる。
の部分に該当し、
通常は ident にはプログラム名が設定される。 ident が NULL の場合、プログラムが ident として使用される
とあるので「ool www」はプログラム名のようだ。しかし「ool www」なんてプログラムはいないぞ?

syslog()関数の実装

こうなると、identがNULLだったときのopenlog()の動きを知りたくなる。そこでglibcを見たところ、openlog()の実体はmisc/syslog.cにある。

openlog()は内部のopenlog_internal()を呼び、ここでidentはLogTag変数に代入される。

LogTagがNULLのときの処理を探してみると、syslog()のログ処理の実体である__vsyslog_chk()に以下の記述があった。

if (LogTag == NULL)
      LogTag = __progname;

__prognameは何かと見ると、頭のほうでexternで宣言されていた。
extern char *__progname; /* Program name, from crt0. */
だいぶあやしい。

「__progname」とは

で、誰が__prognameをセットしているのかと探してみると「misc/init-misc.c」にあった。

void
__init_misc (int argc, char **argv, char **envp)
{
  if (argv && argv[0])
    {
      char *p = strrchr (argv[0], '/');
      if (p == NULL)
         __progname = argv[0];
      else
         __progname = p + 1;
      __progname_full = argv[0];
    }
}
__prognameは「argv[0]の最後の'/'より後」のようだ。なるほどargv[0]の役目を考えると妥当そうに見える。__progname_fullとしてargv[0]も保持されている。

php-fpmのargv[0]ってなんだっけ?

phpが動作するのは、php-fpmプロセス。そういえばphp-fpmは独自設定した文字列がpsで見えてたよなと見てみると、こうなっている。

$ ps -ef | grep '[p]hp-fpm'
root      2664     1  0 22:02 ?        00:00:00 php-fpm: master process (/etc/php5/fpm/php-fpm.conf)                    
www-data  2666  2664  0 22:02 ?        00:00:00 php-fpm: pool www                                                       
www-data  2667  2664  0 22:02 ?        00:00:00 php-fpm: pool www

php-fpmは明らかにargv[0]を書き換えている。勘のいい人ならもうお分かりだろう。一応検証するために、PHPを適当に展開してfpm.cのfpm_init()の頭と末尾(argvが書き換わる前と後)に以下のようにログを仕込んでみる(ヘッダやexternは適宜挿入)と...

zlog(ZLOG_NOTICE, "argv[0]: %s", argv[0]);

zlog(ZLOG_NOTICE, "__progname: %s", __progname);
zlog(ZLOG_NOTICE, "__progname_full: %s", __progname_full);

こんな風になる。

$ sudo ./sapi/fpm/php-fpm -F -y /etc/php5/fpm/php-fpm.conf 
[08-Feb-2016 00:25:23] NOTICE: argv[0]: ./sapi/fpm/php-fpm
[08-Feb-2016 00:25:23] NOTICE: fpm is running, pid 24381
[08-Feb-2016 00:25:23] NOTICE: __progname: ster process (/etc/php5/fpm/php-fpm.conf)
[08-Feb-2016 00:25:23] NOTICE: __progname_full: php-fpm: master process (/etc/php5/fpm/php-fpm.conf)
もう少しわかりやすくすると、こう。


そう、元々あった最後の/までの文字数分、新しい文字列の開始からスキップされている。ログ出力がだるいので例ではmaster processになっているが、pool wwwでどうなるかを想像するのはたやすいと思う。

結論:syslog tagに使われる__prognameを、プログラムパスであるargv[0]からディレクトリ部分を取り除いた文字列へのポインタとしてlibcが保存したあと、php-fpmがargv[0]を書き換えたため、謎の文字列「ool www」が生まれた。

一応Bug reportは作っておいた。PHPが対処すべき問題のように感じるけど、libcが文字列をコピーしておかないのが悪いって言われるとそうすかって感じなので、正直期待できない:https://bugs.php.net/bug.php?id=71544

回避策:identに妥当な値を設定するには?

暗黙のopenlog()をさせなければいい。つまり、期待のプログラムが動く前にopenlog()すればよい。

具体的には、以下のようなファイルを例えば/etc/php5/fpm/fixsyslog.phpと置いて……

php.iniのauto_prepend_fileで参照する。
auto_prepend_file = /etc/php5/fpm/fixsyslog.php

これで常にopenlog()が呼ばれるので現象は起こらない。

感想

原因に関しては、ソースを追うことですっきりさせることができた。オープンソースのありがたみって、やっぱりこういうところにあると思う。解決策は、もやもやが残るままだけど…

おまけ:php-fpmはどのようにargv[0]を書き換えているか?

php-fpmは見ての通りargv[0]を書き換えるのだけど、argvを全部足しても期待の文字数に足りないなんてこともあるはず。そんなときの処理がfpm_env.cに書かれていた。

ざっくり言うと「environがargvとメモリ空間上に連続で置いてあるから、一応確認しつつenvironにどいてもらって、そこをargv[0]として使いましょう」である。グロい!コメントによると、nginxやpureftpdもそうしているらしい。

この記事も最後なので該当コードを貼り付けておく。


2015-11-24

Linuxを使ってUEFIシステムのWindowsをセーフモードで起動させる

しばらくセーフモードのお世話になっていないうちに、セーフモードはF8で起動するものではなくなっていたらしい。セーフモードを構成してからシャットダウンしてセーフモードとか。回復ディスクがなく、Windowsが起動しない場合で、明示的にセーフモードで起動したいときはどうしたらいいんだろう?ということで、Linuxを使ってセーフモードを明示的に構成する方法について今回はまとめた。

注意:PCメーカーやMicrosoftの推奨する方法ではありません。壊れにくいような手順は取っていますが、重要なシステムに適用することはおすすめしません。普通に回復ディスクを使うのが楽だと思います。どちらかというと、仕組みを読み取ってもらえれば幸いです。

手順の概要

最近のWindowsの起動時の設定はBoot Configuration Data (BCD) というところに格納されており、ここにセーフモードで起動するかどうかの設定も入っている。そこで、このBCDをUSBメモリからブートしたUbuntuを使って書き換えることで、Windowsをセーフモードで起動させる。

なおBCD書き換えの際、標準外のツールを使うため、ファイルが壊される可能性を考慮して、セカンダリBCDストアを構成し、そちらの設定を変更することにする。

図にすると以下の通り。

環境/必要なもの

対象の環境は、UEFIで稼動するWindows。Windows 10で検証したが、Vista以降ならいけると思う。

また、必要なものはUbuntu 14.04 (or later) のブータブルUSBメモリ や DVD。すでにデュアルブート環境なら、それでも問題ない。

一応、検証した環境(VirtualBox)でのbcdeditコマンドの出力結果はこんな感じ。
Windows ブート マネージャー
--------------------------------
identifier              {bootmgr}
device                  partition=\Device\HarddiskVolume2
path                    \EFI\Microsoft\Boot\bootmgfw.efi
description             Windows Boot Manager
locale                  ja-JP
inherit                 {globalsettings}
default                 {current}
resumeobject            {43ea4f06-91dc-11e5-8faa-ac88953b08c0}
displayorder            {current}
toolsdisplayorder       {memdiag}
timeout                 30

Windows ブート ローダー
--------------------------------
identifier              {current}
device                  partition=C:
path                    \Windows\system32\winload.efi
description             Windows 10
locale                  ja-JP
inherit                 {bootloadersettings}
recoverysequence        {43ea4f08-91dc-11e5-8faa-ac88953b08c0}
recoveryenabled         Yes
isolatedcontext         Yes
allowedinmemorysettings 0x15000075
osdevice                partition=C:
systemroot              \Windows
resumeobject            {43ea4f06-91dc-11e5-8faa-ac88953b08c0}
nx                      OptIn
bootmenupolicy          Standard

手順

1) セカンダリブートパス・セカンダリBCDストアの構成

1-1) Ubuntuの起動

ブータブルUSBメモリなどでUbuntuを起動する。

1-2) EFI システムパーティション (ESP) のマウント

Ubuntuが起動したら、EFI システムパーティションをどこか適当な場所にマウントする。マウントしたら、EFI/Microsoft/Boot/BCDが存在することを確認しておく。
$ sudo parted /dev/sda print
モデル: ATA VBOX HARDDISK (scsi)
ディスク /dev/sda: 34.4GB
セクタサイズ (論理/物理): 512B/512B
パーティションテーブル: gpt

番号  開始    終了    サイズ  ファイルシステム  名前                          フラグ
 1    1049kB  473MB   472MB   ntfs              Basic data partition          hidden, diag
 2    473MB   578MB   105MB   fat32             EFI system partition          boot
 3    578MB   595MB   16.8MB                    Microsoft reserved partition  msftres
 4    595MB   34.4GB  33.8GB  ntfs              Basic data partition          msftdata

$ sudo mkdir /mnt/efi
$ sudo mount /dev/sda2 /mnt/efi
$ find /mnt/efi -name BCD
/mnt/efi/EFI/Microsoft/Boot/BCD     ←Microsoft/Boot/BCDが存在することを確認
/mnt/efi/EFI/Microsoft/Recovery/BCD

1-3) Microsoftディレクトリのコピー

EFIアプリケーションであるWindows Boot Manager (bootmgfw.efi)及びその設定であるBoot Configuration Data (BCD)が含まれるMicrosoftディレクトリをコピーする。
$ cd /mnt/efi/EFI
$ sudo cp -pr Microsoft MicrosoftSafeMode

手順参考:Windows RE を起動するためのハードウェア回復ボタンの追加

2) セカンダリBCDストアのBCDの書き換え

2-1) hivexregeditツールのインストール

設定が格納されているBCDは、レジストリハイブの形式をとっている。そこで、レジストリハイブを編集できる「hivexregedit」ツールを使えるようにする。

「hivexregedit」ツールは、libwin-hivex-perlパッケージに入っているので、これをインストールすればOK。
$ sudo apt-get install libwin-hivex-perl
なお、他にもレジストリハイブを扱うツールがlibhivex-binパッケージにいくつか入っているので、必要があれば入れる。

2-2) BCDファイルをレジストリエディタ形式にエクスポート

hivexregeditの--exportオプションを使って、BCDの内容を*.regファイルでおなじみの形式にエクスポートする。
$ hivexregedit --unsafe-printable-strings --export /mnt/efi/EFI/MicrosoftSafeMode/Boot/BCD '\' > /tmp/bcd.reg
これにより、テキストエディタやビューワで表示が可能になった。--unsafe-printable-stringsをつけないと、文字列が軒並みhex表記になって見れたもんじゃない。

2-3) Windows Boot Managerのobjectを探す

@junichia氏のブログエントリ「ブート構成データ(BCD)ストアを理解すれば VHDブート は簡単」に図があるので見て欲しいのだけど、BCDにはひとつのWindows Boot Managerのobjectと、複数のWindows Boot Loaderのobjectがある。Boot Loader側にsafe modeの設定もあるのだけど、まずはどれがdefaultで利用されているBoot Loaderか確認するため、Boot Manager側のobjectを探し出す。

boot managerのオブジェクトを探し出すには「bootmgfw.efi」を文字列検索するのが簡単。
$ less /tmp/bcd.reg
(/で検索)
[\Objects\{9dea862c-5cdd-4e70-acc1-f32b344d4795}\Elements\12000002]
"Element"=str(1):"\EFI\Microsoft\Boot\bootmgfw.efi^@"

[\Objects\{9dea862c-5cdd-4e70-acc1-f32b344d4795}\Elements\12000004]
"Element"=str(1):"Windows Boot Manager"

これで、boot managerのオブジェクトのGUIDが「{9dea862c-5cdd-4e70-acc1-f32b344d4795}」であることがわかった。

なお、各項目はElements\以下の数値で何か判断することが可能である。上記における「12000002」は「BcdLibraryString_ApplicationPath」でbcdeditコマンドでは「path」として表示される。同様に「12000004」は「BcdLibraryString_Description」でbceditコマンドでは「description」である。それぞれの数値が何を意味するかは、MSDNにも記述があるのだが、Geoff Chappell氏の「BCD Elements」が見やすくておすすめである。

2-4) Widnows Boot Managerのobjectから「default」を探し出す

Boot Managerがわかったので、そのGUIDを利用して、デフォルトのBoot Loaderの指定を探し出す。デフォルトのBoot Loaderが記述されているのはBcdBootMgrObject_DefaultObject (default)で「23000003」である。

$ less /tmp/bcd.reg
(/で検索)
[\Objects\{9dea862c-5cdd-4e70-acc1-f32b344d4795}\Elements\23000003]
"Element"=str(1):"{43ea4f07-91dc-11e5-8faa-ac88953b08c0}"

ここでデフォルトのBoot Loaderが{43ea4f07-91dc-11e5-8faa-ac88953b08c0}であることがわかった。一応、{43ea4f07-91dc-11e5-8faa-ac88953b08c0} のBcdLibraryString_ApplicationPath (path) 「12000002」及び BcdLibraryString_Description (description) 「12000004」を確認しておく。
$ less /tmp/bcd.reg
(/で検索)
[\Objects\{43ea4f07-91dc-11e5-8faa-ac88953b08c0}\Elements\12000002]
"Element"=str(1):"\Windows\system32\winload.efi"

[\Objects\{43ea4f07-91dc-11e5-8faa-ac88953b08c0}\Elements\12000004]
"Element"=str(1):"Windows 10"
これでいいようだ。

2-5) Boot Loader objectにセーフモード設定を組み込む

Boot Loaderのobjectにセーフモード関連のElementを追加する*.regファイルを作り、それをhivexregeditの--mergeオプションを使って組み込む。

項目がいくつかあるので後の表を見て欲しいが、例えばmsconfigで言うところのセーフモード: 代替シェルで起動する記述は以下の通りである。生成したbcd.regをコピーして作るのが楽。
$ cp /tmp/bcd.reg /tmp/bcd-safe.reg
$ vi /tmp/bcd-safe.reg
~~~
Windows Registry Editor Version 5.00

[\Objects\{43ea4f07-91dc-11e5-8faa-ac88953b08c0}\Elements\25000080]
"Element"=hex(3):00,00,00,00,00,00,00,00

[\Objects\{43ea4f07-91dc-11e5-8faa-ac88953b08c0}\Elements\26000081]
"Element"=hex(3):01
~~~
上記の例ではBcdOSLoaderInteger_SafeBoot (safeboot) 「25000080」を「Minimal」にし、BcdOSLoaderBoolean_SafeBootAlternateShell (やsafebootalternateshell) 「26000081」をtrueにしている。

以下は検証したわけではないが、msconfig.exeの「ブート」タブの表示(以下参考)とbcdeditの出力を見ながら、「BCD Elements」とつきあわせて表にまとめたものが以下となる。


msconfigの項目bcdeditの項目名称Element備考
pathBcdLibraryString_ApplicationPath 12000002string
descriptionBcdLibraryString_Description 12000004string
defaultBcdBootMgrObject_DefaultObject23000003GUID
セーフブートsafebootBcdOSLoaderInteger_SafeBoot 250000800: 最小(Minimal) /
1: ネットワーク(Network) /
2: Active Directory修復(DsRepair)
(代替シェルのときのみ)safebootalternateshellBcdOSLoaderBoolean_SafeBootAlternateShell26000081boolean
GUI ブートなしquietbootBcdOSLoaderBoolean_DisableBootDisplay26000041boolean
ブートログbootlogBcdOSLoaderBoolean_BootLogInitialization 26000090boolean
基本ビデオvgaBcdOSLoaderBoolean_UseVgaDriver26000040boolean
OS ブート情報sosBcdOSLoaderBoolean_VerboseObjectLoadMode26000091boolean

regファイルを作ったら、--mergeでmergeする。
$ sudo hivexregedit --merge /mnt/efi/EFI/MicrosoftSafeMode/Boot/BCD /tmp/bcd-safe.reg

再度hivexregedit --exportしてみて、組み込まれていればOK。

2-6) umount

言うまでもないかもしれないが、umountしておく。
$ sudo umount /mnt/efi

3) grubから新しいBCDを使って起動

UbuntuをEFIで起動しているということは、EFIで動作するgrubが使えるということである。そこで、そのgrubを利用して上記で作ったセカンダリブートパス・BCDストアを選択して起動する。

再びUbuntu ブータブルUSBメモリで再起動し、grubメニューが現れたら、「c」キーを押しコンソールモードに切り替える。切り替わったら、以下のように入力して、新しいbootmgfw.efiを使って起動する。

grub> set root=(hd0,gpt2)
grub> chainloader /efi/MicrosoftSafeMode/Boot/bootmgfw.efi
grub> boot

hdのパスはPCによって異なるはずなので、以下の図のようにTAB補完を活用しながら入力するとよい。


これでセーフモードで立ち上がってくれば成功。とくにEFI firmware側の設定は変更していないので、ブータブルUSBメモリを外して普通に起動すれば元通り。気になるようであれば、EFIシステムパーティションから今回作ったディレクトリを削除しておけばOKである。


2015-07-13

bsdtarでzip展開時の先頭ディレクトリを削る

zipに限らずアーカイブにはこんな形でファイルが入っていることがある。
hoge-0.1.2/README.md
hoge-0.1.2/configure
hoge-0.1.2/src/
hoge-0.1.2/src/main.d

これをunzipで普通に展開すると、当然ながらhoge-0.1.2ができるが、バージョンが入っているのがうれしくないことがある。ここで、tarの「--strip-components」オプションと、zipを展開できるbsdtarを組み合わせることで、hoge-0.1.2を外して展開することができる。
$ bsdtar xvf hoge-0.1.2.zip --strip-components 1
x README.md
x configure
x src/
x src/main.d

どうでもいいTipsでした。

2015-05-11

boot2docker を Linux + KVM で使う

管理の都合上Docker用のVMの上でDockerコンテナを動かしたいなー、Dockerコンテナ動かすだけなら薄いやつがいいだろうからboot2dockerかなーと思ったのだけど、WindowsとMac OS Xで動かす例ばかりでLinuxで動かしている話を見かけない。使えないわけではなく、isoから入れれば使えそうなのでやってみた。なお今回の前提はKVMだけども、boot2dockerをカスタマイズしたいとなれば、Hyper-VやVirtualBoxでも設定の部分は参考になるかも。

手順の概要

概ねやりかたは以下の通り。
  • 永続化データ領域を確保・設定する。
  • virt-installでVMを作成する。
  • IPアドレスやSSHなどの設定をする。
  • リモート接続用の鍵を取り出して使う。

環境

ハイパーバイザ
Ubuntu 14.04 (libvirt/KVM設定済)
boot2docker
1.6.1
ここで試したのはUbuntuだけど、今回の例ではvirt-installが使えればだいたい一緒だと思う。

注意

boot2dockerはデフォルトではかなりinsecure (固定デフォルトパスワードあり・SSHパスワードログイン可) なので、信頼できないネットワーク、例えばインターネットに直接晒されるような環境で作業しないこと。

データ保存領域の設定

boot2dockerはOS部分となる揮発性の領域と、永続的なデータ保存領域(dockerイメージ格納領域を含む)で動作する。そのため、VMを立ち上げる前にデータ保存領域を事前に準備する。揮発性の部分の領域も用意してもいいのだが、今回はしないことにする。

やりかたとしては、qemu-imgで領域を確保し、partedでパーティションを切り、mkfs.ext4でファイルシステムを作成する。このとき、"boot2docker-data"というラベルを設定することがポイント。boot2dockerはこのラベルを見てマウントしに来る。

コマンドライン例は、以下の通り。2Gと書いてみたけど、dockerコンテナのデータが全て来るので、適切なサイズに設定する。
# qemu-img create -f raw /home/images/boot2docker-data.img 2G
# chmod 600 boot2docker-data.img

# losetup -f
/dev/loop0
# losetup /dev/loop0 boot2docker-data.img
# parted --script /dev/loop0 'mklabel msdos'
# parted --script /dev/loop0 'mkpart primary 0% 100%'
# mkfs.ext4 -L boot2docker-data /dev/loop0p1 
# losetup -d /dev/loop0

mkpartで、0の代わりに0%とすると、アラインメントをよしなにしてくれる(警告はなくなるが意味はあるか?)。コマンドが短いのでpartedを使っただけで、fdiskでもいいし、再起動回数が増えるけどもboot2dockerが上がってきた後fdiskを使うとlosetupはしなくていい。

なお、LVMを使うなら、qemu-imgの代わりに、lvcreateでこんな感じ。
# lvcreate -L 8G -n boot2docker-data vg0

virt-installでVM作成

まず、boot2dockerのisoをどこかにダウンロードする。ずっと使うのでうっかり消さなそうな場所に。ここでは、/home/images/boot2docker.isoにダウンロードしてきたのを想定する。

そして、libvirtでVMを作成する。libvirtのXMLを自前で生成する例を見かけたけども、virt-installがやってくれるので、virt-installで作る。

例としては、こんな感じ。
# virt-install --name=boot2dockertest --ram=1024 --vcpus=1 \
   --os-type=linux --os-variant=virtio26 \
   --disk path=/home/images/boot2docker-data.img \
   --network bridge:br0 \
   --livecd --cdrom=/home/images/boot2docker.iso 
この--livecdをつけないと、初回だけCDがある状態になって、2回目の起動以降起動できなくなる。

さまざまな設定

これでboot2dockerが立ち上がってくる。virsh console VM名(ここではboot2dockertest)とすれば、コンソールに接続でき、dockerなどとユーザー名を入れればログインできる。poweroffコマンドでシャットダウン。

このように、/var/lib/{boot2docker,docker}が/mnt/vda1に向いていれば、データ領域の準備もOK。
docker@boot2docker:~$ df
Filesystem                Size      Used Available Use% Mounted on
rootfs                  445.5M     87.5M    358.0M  20% /
tmpfs                   445.5M     87.5M    358.0M  20% /
tmpfs                   247.5M         0    247.5M   0% /dev/shm
/dev/vda1                 1.9G     18.0M      1.8G   1% /mnt/vda1
cgroup                  247.5M         0    247.5M   0% /sys/fs/cgroup
/dev/vda1                 1.9G     18.0M      1.8G   1% /mnt/vda1/var/lib/docker/aufs
docker@boot2docker:~$ ls -al /var/lib
total 0
drwxrwxr-x    3 root     staff          100 May  9 15:26 ./
drwxrwxr-x    8 root     staff          180 May  9 15:26 ../
lrwxrwxrwx    1 root     root            29 May  9 15:26 boot2docker -> /mnt/vda1/var/lib/boot2docker/
lrwxrwxrwx    1 root     root            24 May  9 15:26 docker -> /mnt/vda1/var/lib/docker/
drwxr-xr-x    4 root     root           160 May  9 15:26 nfs/

ここから、いろいろと設定していく。

hostnameの設定

/var/lib/boot2docker/etc/hostname を変更する。データ保存領域にあるので、書き換えればOK.
$ sudo vi /var/lib/boot2docker/etc/hostname 

sshd_configの設定

パスワードログインできないように設定する。こちらも書き換えればOK.
$ sudo vi /var/lib/boot2docker/ssh/sshd_config
(以下を変更/追記)
PasswordAuthentication no 

ssh 公開鍵の配置

ssh ログインできるよう、公開鍵を配置する。作業用ユーザである「docker」のホームディレクトリは永続化されないので、boot2dockerの「/var/lib/boot2docker/userdata.tarを置いておくとホームディレクトリに展開される」という機能を利用して、SSH鍵を配置することにする。

公開鍵の取り方はなんでもいいが、ここではgithub.com/{username}.keysにアクセスするとそのユーザの公開鍵が得られるという方法で鍵を取ってみる例を載せる。

$ cd /var/lib/boot2docker
$ sudo mkdir userdata
$ sudo chown docker userdata
$ cd userdata
$ mkdir .ssh
$ chmod 700 .ssh
$ cd .ssh
$ curl -o authorized_keys https://github.com/{username}.keys
$ cd /var/lib/boot2docker/userdata
$ tar cf ../userdata.tar .

起動時スクリプトの設定(IPアドレス他の設定)

デフォルトでは、dhcpでIPアドレスをもらうだけなので、固定IPアドレスにしたいときは、設定が必要になる。boot2dockerの「起動時に/var/lib/boot2docker/bootsync.shとbootlocal.shが呼び出される」という機能を利用して、スクリプトにIPアドレス他を設定するコマンドをいれておくことにする。bootsync.shは/etc/rc.d/dockerが起動される前、bootlocal.shは後に動くので、先に動くbootsync.shに設定する。

例は以下の通り。boot2dockerのVMのIPアドレスは192.168.1.100と仮定する。
$ sudo vi /var/lib/boot2docker/bootsync.sh
(以下を記述)
#!/bin/sh
/etc/init.d/services/dhcp stop
ip addr flush eth0
ip addr add 192.168.1.100/24 dev eth0
ip route add default via 192.168.1.254
echo "nameserver 192.168.1.254" >> /etc/resolv.conf

$ sudo chmod 755 /var/lib/boot2docker/bootsync.sh

リモート接続用の鍵の再生成

初回起動時に/var/lib/boot2docker/tls/にリモート接続用の鍵が生成されて、必要なものが~/.dockerにコピーされているが、IPアドレスが含まれているので、再生成する。生成の方法は/etc/rc.d/dockerに書いてあるが、簡単にするため、いったん消して再起動時に作らせてしまうことにする。
$ sudo rm -rf /var/lib/boot2docker/tls

あとは、rebootで再起動して設定が反映されていることを確認する。

リモート接続用の鍵の取り出し

このままboot2dockerのVMでdockerコマンドを叩いても使えるが、作業用のPCからdockerコマンドで接続できるようにすると便利。先ほど生成させたキーを、作業用のPCにscpでもらってくる。

$ mkdir -p ~/.boot2docker/certs/boot2dockertest
$ scp 'docker@192.168.1.100:~/.docker/*.pem' ~/.boot2docker/certs/boot2dockertest

あとは、ここに接続するための環境変数を設定するスクリプトを用意して、dot sourceして使えばOK。
$ vi ~/bin/use-boot2dockertest
export DOCKER_CERT_PATH=~/.boot2docker/certs/boot2dockertest
export DOCKER_TLS_VERIFY=1
export DOCKER_HOST=tcp://192.168.1.100:2376
$ . ~/bin/use-boot2dockertest

これで一通りの設定が終了。例えばこんな風に動かして、VM上でdocker psすると、boot2docker VMで動いていることがわかる。dockerのバージョンが合ってないとつながらなかったりするので注意。
$ boot2docker run ubuntu:14.04 -i -t /bin/bash

今後の展開

もう少しスクリプト化することもできるのだろうけど、それはDocker Machineの仕事だよねと思ってやめた。Docker Machineがlibvirtに対応すれば、ほとんどの作業を一通りやってくれるはずなので期待して続報を待とう。

2014-09-19

一部のSSDとext4でWriteが激しく遅い問題に対処する

結論:dbench 5 -D .で3MB/s程度しか出ないときは、barrier=0マウントオプションを検討する。

背景

Windows8.1の上のVirtualBoxにUbuntuを乗せて開発をしていたのだけど、開いたウィンドウがフルスクリーンの裏/表に回っちゃうとか、3D性能が下がるせいでウィンドウマネージャが限定されて効率が悪いといった不満があったので、今のご時勢にデュアルブートにすることにした。

それ自体はまぁいつもの感じでできて、グラフィックがサクサクになって期待通りの感じだったのだけど、ディスクI/Oが異常に遅い。apt-getすると「Unpacking~」で数十分とか待たされて、apt-get upgradeが全然終わらない。

これでは使い物にならないので、なんとかすることにした。
 

対処

使っているSSDはcrucialの「CT256V4SSD2」。ファームウェアが更新されていて、変更内容がパフォーマンス改善だったので、更新してみるものの、改善されない(なお、ディスクは消去されるのでインストールし直した)。

遅くなる事例がないかなと思って調べてみると、どうやら廉価なモデルらしく、painfully slowとか言われていて、機種のせいかとあきらめかけたら、まさかの日本のAmazonレビューに気になる記述を発見。

※2013/2/14追記
公式フォーラムでの情報を参考に、Linux ext4ファイルシステム環境において
ほぼ完全な動作を得られましたので一応追記しておきます。

mountオプションとしてnoatime,nodiratime,data=writeback,barrier=0,commit=180,nobhを指定することでフリーズを回避できるようです。
CD/USBブート後当SSDに対してtune2fs -o journal_data_writeback /dev/sd**を実行、
fstabのオプションフィールドに上記パラメータを追加することで、ようやく完全にSSDとしての性能を発揮するようになりました。
http://www.amazon.co.jp/product-reviews/B0092LHA7Y

この中ではbarrier=0が気になったのでmount -o remount,rw,barrier=0 /でマウントしたらapt-getのひっかかりがなくなった。

計測

もう体感では明らかに速くなっているのでこれでもいいのだけど、検索してみると同じ症状ではないかと思われる人がベンチマークを取っている(ext4のボトルネック除去:(SSDの)命懸けベンチマーク編)ので真似して「dbench 5 -t 30 -D .」で1回だけざっくり測ってみた結果がこちら。

Crucial CT256V4SSD2 / dbench 5 -t 30 -D . / 1回
Throughputmax_latency
未設定3.29658 MB/sec1222.288 ms
barrier=0106.397 MB/sec841.859 ms
barrier=13.29504 MB/sec1100.531 ms

体感と一致するありえないぐらいの遅さが数字に出ていた。3MB/sって光回線より遅いよ...

barrier=1がいつでもここまでインパクトを与えるかというとそうではなく、ずっと使っているノートPCのSSD(Crucial C300-CTFDDAC128M)では以下の通り。

Crucial C300-CTFDDAC128M / dbench 5 -t 30 -D . / 1回
Throughputmax_latency
未設定118.525 MB/sec52.287 ms
barrier=0144.024 MB/sec29.145 ms
barrier=1118.909 MB/sec44.737 ms

確かに遅くはあるのだけど、桁が変わるほどではない。max_latencyを見ると、さっきのSSDは投げ捨てろって感じだけどね...

ついでなので、SSDにTRIMコマンドを発行するようになる「discard」オプションをつけて測ってみたが、特に変化はない様子。

結局

上記の結果と、たいして重要なデータを置いていない作業用PCだということを踏まえて「barrier=0,discard」を/etc/fstabにマウントオプションとして追記することにした。

大事なデータを扱っている機械なら、こんな個体に当たったら使うのをやめるべきと思う。

2014-07-03

KVMの仮想マシンをWebブラウザから管理する

自分用にLinux KVM (Kernel-based Virtual Machine) で検証用他の仮想マシン達を立てている。その仮想マシンの管理に、今まではvirshコマンドと仮想マシンマネージャー (virt-manager)を使っていたが、各VMの画面が見たいとなると、VNCでXにログインして... という感じで煩わしかった。そこで、ハイパーバイザとなるPCのリプレースを契機に、Web管理ツールのWebVirtMgrを使って、Webブラウザから管理できるようにしてみた。

こんな感じの画面がWebブラウザから使えるようになる。


環境/前提

  • Ubuntu 14.04 Server
  • libvirtでKVMの仮想マシンを起動できる状態
    • (BIOS設定他+apt-get install qemu-kvm libvirt-bin的な状態)

Web側の設定


WebVirtMgrはPython + Djangoで書かれたWebアプリケーションなので、それが動くようにする。基本的には、https://github.com/retspen/webvirtmgr/wiki/Install-WebVirtMgr に書かれている手順の通り。このページには、RedHat/CentOS/Debianなんかのときの方法も書いてある。

Python他のインストール

apt-getで入れる。

$ sudo apt-get install git python-pip python-libvirt python-libxml2 novnc supervisor nginx 

アプリケーションのダウンロードと設定

gitで落としてきて、いくつか設定する。
$ git clone git://github.com/retspen/webvirtmgr.git
$ cd webvirtmgr

ここで本来の手順は「sudo pip install -r requirements.txt」なのだけど、パッケージ管理されないファイルが増えてしまう。requirements.txtを見ると、書いてあるライブラリがUbuntu 14.04のバージョン的にいけそうだったのでパッケージで入れることにした。
$ sudo apt-get install python-django gunicorn python-lockfile

スクリプトで初期設定をする。
$ ./manage.py syncdb
You just installed Django's auth system, which means you don't have any superusers defined.
Would you like to create one now? (yes/no): yes
Username (leave blank to use 'ユーザ名'): 
Email address: user@example.org
Password: パスワード
Password (again): パスワード
Superuser created successfully.
$ ./manage.py collectstatic

ディレクトリをまるっと移動する。
$ sudo mkdir /var/www
$ cd ..
$ sudo mv webvirtmgr /var/www/
$ sudo chown -R www-data:www-data /var/www/webvirtmgr

バックグラウンド起動の設定

バックグラウンドで起動するようにsupervisorを設定する。
/etc/supervisor/conf.d/webvirtmgr.confに以下の内容を書く。
[program:webvirtmgr]
command=/usr/bin/python /var/www/webvirtmgr/manage.py run_gunicorn -c /var/www/webvirtmgr/conf/gunicorn.conf.py
directory=/var/www/webvirtmgr
autostart=true
autorestart=true
stdout_logfile=/var/log/supervisor/webvirtmgr.log
redirect_stderr=true
user=www-data

supervisorのリスタート(これで8000/tcpで立ち上がる)
$ sudo service supervisor restart 

フロントのWebサーバの設定

フロントに立つnginxを設定する。プロキシしているだけなので、Apacheでもいいと思うが、サンプルがこうなっているのでそうした。

/etc/nginx/sites-available/webvirtmgrに以下を記述する。
server {
    listen 8008 default_server;

    server_name $hostname;
    #access_log /var/log/nginx/webvirtmgr_access_log; 

    location / {
        proxy_pass http://127.0.0.1:8000;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-for $proxy_add_x_forwarded_for;
        proxy_set_header Host $host:$server_port;
        proxy_set_header X-Forwarded-Proto $remote_addr;
    }
}

$ sudo ln -s /etc/nginx/sites-available/webvirtmgr /etc/nginx/sites-enabled/webvirtmgr
$ service nginx restart 

これで http://hostname:8008/ にアクセスすると画面が見えるようになる。

novncの設定

ブラウザからコンソールを使うためのnovncの設定。この設定ファイルもwebvirtmgrに入っているので、使うだけでOK.

$ cd /var/www/webvirtmgr
$ sudo cp conf/initd/webvirtmgr-novnc-ubuntu /etc/init.d/webvirtmgr-novnc
$ sudo service webvirtmgr-novnc start
$ sudo update-rc.d webvirtmgr-novnc defaults

(コメントにnova-novncproxyとか書いてあったのでOpenStack Novaから持ってきたのであろう...)

libvirtの設定

WebVirtMgrはlibvirtdにTCP接続するので、その設定をする(なので、ハイパーバイザは別ホストでもOK)
ここの設定は https://www.webvirtmgr.net/docs/ を参考にした(証明書切れてる...)。

/etc/default/libvirt-binのlibvirtd_optsの行に-lを足して、tcpでlistenするようにする。
libvirtd_opts="-d -l"

/etc/libvirt/libvirtd.conf を以下のように編集する。今回はハイパーバイザが同一ホストなので127.0.0.1でlistenさせる。
listen_tls = 0
listen_tcp = 1
listen_addr = "127.0.0.1"

libvirt用のパスワードを設定する。
$ sudo apt-get install sasl2-bin
$ sudo saslpasswd2 -a libvirt ユーザ名
Password: 
Again (for verification): 
$ sudo sasldblistusers2 -f /etc/libvirt/passwd.db 
ユーザ名@ホスト名: userPassword

libvirtdを再起動する。
$ service libvirt-bin restart

以下のようにして接続できることを確認する。

$ virsh -c qemu+tcp://127.0.0.1/system nodeinfo
Please enter your authentication name: ユーザ名@ホスト名
Please enter your password: 
CPU model:           x86_64
CPU(s):              8
CPU frequency:       800 MHz
CPU socket(s):       1
Core(s) per socket:  4
Thread(s) per core:  2
NUMA cell(s):        1
Memory size:         16318576 KiB

Web画面にログインしての設定

http://hostname:8008/ にアクセスすると、ログイン画面が表示されるので、最初の「syncdb」を実行したときのユーザ/パスワードでログインする。

あとは画面ベースなので詳細は省略するが、以下のように設定すれば使えるようになる。

  • Servers List画面で127.0.0.1を追加する。
  • Storage Pools画面で、仮想マシン用のディレクトリorLVMプールとISOを配置するディレクトリを追加する。
  • Networks画面でネットワークを追加する。
  • Instances画面でインスタンスを追加する。

感想

OpenStackをこのようにVMを管理するために使おうと検証してみたのだけど、LVMのストレージやOpen vSwitchのブリッジを自前でIDを振って管理したりしていて、トラブルがあったときに追いづらくなるデメリットのほうが大きそうだったのでやめた。一方で、このWebVirtMgrはlibvirtやLVMをそのまま見せる設計思想なので、何かあったときにも追いやすく、1台~数台の管理ならかなり向いていそうだった。

2014-07-01

GPT パーティションテーブルを別のディスクにコピーする

sdaからsdbにパーティションテーブルをコピーするには、sgdiskを使うのが簡単。コマンドを先に書くとこう。不用意に実行するとさっくり壊れるので注意。
# sgdisk -R=/dev/sdb /dev/sda
# sgdisk -G /dev/sdb
sgdisk --backup sda.table /dev/sdaとかしておいたほうが安心かも?
参考: http://askubuntu.com/questions/57908/how-can-i-quickly-copy-a-gpt-partition-scheme-from-one-hard-drive-to-another

sgdiskがないとき

GPT版のfdiskであるところのgdiskをインストールする。
# apt-get install gdisk
cfdisk, sfdiskがあるように、cgdisk, sgdiskも入ってくる。ただし、コマンドラインオプションは違うので注意。
正直デフォルトで入っていて欲しい...

コマンドの説明

前提として、こんなディスクが二つあるとする。
$ sudo sgdisk -p /dev/sda
Disk /dev/sda: 16777216 sectors, 8.0 GiB
Logical sector size: 512 bytes
Disk identifier (GUID): 1519615B-B82D-4CA6-9849-28F3198A90E7
Partition table holds up to 128 entries
First usable sector is 34, last usable sector is 16777182
Partitions will be aligned on 2048-sector boundaries
Total free space is 3102653 sectors (1.5 GiB)

Number  Start (sector)    End (sector)  Size       Code  Name
   1            2048            4095   1024.0 KiB  EF02
   2            4096        13676543   6.5 GiB     FD00

$ sudo sgdisk -p /dev/sdb
Creating new GPT entries.
Disk /dev/sdb: 16777216 sectors, 8.0 GiB
Logical sector size: 512 bytes
Disk identifier (GUID): 65906D3D-B672-4373-AB55-F0C473EC52ED
Partition table holds up to 128 entries
First usable sector is 34, last usable sector is 16777182
Partitions will be aligned on 2048-sector boundaries
Total free space is 16777149 sectors (8.0 GiB)

Number  Start (sector)    End (sector)  Size       Code  Name

ここで以下を実行することでパーティションテーブルをコピーできる。
$ sudo sgdisk -R=/dev/sdb /dev/sda
The operation has completed successfully.

これだけでコピーされているのだが、ディスクとパーティションに振られた固有IDまでコピーされてしまっている。

以下はディスク本体と2番目のパーティションの状態。
$ sudo sgdisk /dev/sda -p -i 2
Disk /dev/sda: 16777216 sectors, 8.0 GiB
Logical sector size: 512 bytes
Disk identifier (GUID): 1519615B-B82D-4CA6-9849-28F3198A90E7
Partition table holds up to 128 entries
First usable sector is 34, last usable sector is 16777182
Partitions will be aligned on 2048-sector boundaries
Total free space is 3102653 sectors (1.5 GiB)

Number  Start (sector)    End (sector)  Size       Code  Name
   1            2048            4095   1024.0 KiB  EF02
   2            4096        13676543   6.5 GiB     FD00
Partition GUID code: A19D880F-05FC-4D3B-A006-743F0F84911E (Linux RAID)
Partition unique GUID: CEA27178-6FB4-4358-B921-C75352CE33D7
First sector: 4096 (at 2.0 MiB)
Last sector: 13676543 (at 6.5 GiB)
Partition size: 13672448 sectors (6.5 GiB)
Attribute flags: 0000000000000000
Partition name: ''

$ sudo sgdisk /dev/sdb -p -i 2
Disk /dev/sdb: 16777216 sectors, 8.0 GiB
Logical sector size: 512 bytes
Disk identifier (GUID): 1519615B-B82D-4CA6-9849-28F3198A90E7 ←同じ
Partition table holds up to 128 entries
First usable sector is 34, last usable sector is 16777182
Partitions will be aligned on 2048-sector boundaries
Total free space is 3102653 sectors (1.5 GiB)

Number  Start (sector)    End (sector)  Size       Code  Name
   1            2048            4095   1024.0 KiB  EF02
   2            4096        13676543   6.5 GiB     FD00
Partition GUID code: A19D880F-05FC-4D3B-A006-743F0F84911E (Linux RAID)
Partition unique GUID: CEA27178-6FB4-4358-B921-C75352CE33D7 ←同じ
First sector: 4096 (at 2.0 MiB)
Last sector: 13676543 (at 6.5 GiB)
Partition size: 13672448 sectors (6.5 GiB)
Attribute flags: 0000000000000000
Partition name: ''

そこで、sgdisk -Gで振りなおす。

$ sgdisk -G /dev/sdb

そうすると、IDが新しくなっていることがわかる。
$ sudo sgdisk /dev/sdb -p -i 2
Disk /dev/sdb: 16777216 sectors, 8.0 GiB
Logical sector size: 512 bytes
Disk identifier (GUID): CCC50747-7D41-4662-9FFE-93E7EE7644A3 ←変わっている
Partition table holds up to 128 entries
First usable sector is 34, last usable sector is 16777182
Partitions will be aligned on 2048-sector boundaries
Total free space is 3102653 sectors (1.5 GiB)

Number  Start (sector)    End (sector)  Size       Code  Name
   1            2048            4095   1024.0 KiB  EF02
   2            4096        13676543   6.5 GiB     FD00
Partition GUID code: A19D880F-05FC-4D3B-A006-743F0F84911E (Linux RAID)
Partition unique GUID: 29394950-7433-4913-8BA9-64E36B979394 ←変わっている
First sector: 4096 (at 2.0 MiB)
Last sector: 13676543 (at 6.5 GiB)
Partition size: 13672448 sectors (6.5 GiB)
Attribute flags: 0000000000000000
Partition name: ''

2013-01-27

Loqui 0.5.4 released (bug fix for Solaris11)

久々に出しました。Loqui 0.5.4です。ほぼほぼSolaris11で動くようにしただけなので、すでに動いてる環境の方々は別に上げなくても大丈夫です。具体例がSolaris11ですが、特殊対応をしたわけではなく、「これだと裏でunsigned使われてたらうまくいかないよなぁ」という感じのものを直しただけなので、もしかしたら前は動かなかったけどこれで動くようになったプラットフォームがあるかもしれません。

aboutの画面を書きかえたときに、「Copyright (C) 2002-2013」というのを見て、ブランクがあるとはいえ結構な時間たってるんだなーと思ってちょっと驚いた。もう10年以上経つのね...

2012-12-23

Linux boxにUSBストレージを接続したとき勝手に同期するようにする

同期とはなんだか難しそうだと思ったあなた!正しいと思います。

そんな「同期」ですが、今日の話は難しくありません。
簡単に書くと、udevデーモンにデバイス固有の設定を入れて、つないだときにrsyncを走らせましょうという話です。

必要なもの

  • 適当なLinux box(今回はDebian squeeze)
  • 適当なUSB storage
今回はSheevaPlugとKobo gloを利用。
udevは若干変わっているので、最近のUbuntuでも大丈夫だと思うが、RHEL(特に5)だとちょっとわからない。
ただし、GNOMEが動いていたりすると、そっちで勝手にマウントされそうなので、無効にしておいたほうがいいかも。

デバイスを特定する

つないだら、まずは普通にdmesgコマンドでログを見るか、fdisk -lでデバイスを調べる。
今回のkoboはsdaとして認識された。
(このSheevaPlugはルートがSDカードだからmmcblk0なのだけど、sdaがシステムになっているデバイスが多いと思うので、この下のコマンド達をパスまでコピペしないよう注意。)

/dev/sdaがデバイスだとわかったので、devadmコマンドのinfo -q path -n /dev/XXXを使って、sysfsでのパスを特定する。
$ sudo udevadm info -q path -n /dev/sda
/devices/platform/orion-ehci.0/usb1/1-1/1-1:1.0/host11/target11:0:0/11:0:0:0/block/sda

そうしたら、udevadmコマンドのinfo -p [パス] -aを使って、デバイスに関する情報を表示する。
(もちろん、この2つのコマンドは$()とかを使って1行で実行してもよい)
$ sudo udevadm info -p /devices/platform/orion-ehci.0/usb1/1-1/1-1:1.0/host11/target11:0:0/11:0:0:0/block/sda -a

Udevadm info starts with the device specified by the devpath and then
walks up the chain of parent devices. It prints for every device
found, all possible attributes in the udev rules key format.
A rule to match, can be composed by the attributes of the device
and the attributes from one single parent device.

  looking at device '/devices/platform/orion-ehci.0/usb1/1-1/1-1:1.0/host11/target11:0:0/11:0:0:0/block/sda':
    KERNEL=="sda"
    SUBSYSTEM=="block"
(略)
  looking at parent device '/devices/platform/orion-ehci.0/usb1/1-1':
    KERNELS=="1-1"
    SUBSYSTEMS=="usb"
    DRIVERS=="usb"
    ATTRS{configuration}=="Self-powered"
    ATTRS{bNumInterfaces}==" 1"
    ATTRS{bConfigurationValue}=="1"
    ATTRS{bmAttributes}=="c0"
    ATTRS{bMaxPower}=="  2mA"
    ATTRS{urbnum}=="229"
    ATTRS{idVendor}=="2237"
    ATTRS{idProduct}=="4173"
    ATTRS{bcdDevice}=="0110"
    ATTRS{bDeviceClass}=="00"
    ATTRS{bDeviceSubClass}=="00"
    ATTRS{bDeviceProtocol}=="00"
    ATTRS{bNumConfigurations}=="1"
    ATTRS{bMaxPacketSize0}=="64"
    ATTRS{speed}=="480"
    ATTRS{busnum}=="1"
    ATTRS{devnum}=="13"
    ATTRS{version}==" 2.00"
    ATTRS{maxchild}=="0"
    ATTRS{quirks}=="0x0"
    ATTRS{authorized}=="1"
    ATTRS{manufacturer}=="Kobo"
    ATTRS{product}=="eReader"
    ATTRS{serial}=="XXXXXXXXXXXXX"
(略)

  looking at parent device '/devices/platform/orion-ehci.0':
    KERNELS=="orion-ehci.0"
    SUBSYSTEMS=="platform"
    DRIVERS=="orion-ehci"
    ATTRS{modalias}=="platform:orion-ehci"

  looking at parent device '/devices/platform':
    KERNELS=="platform"
    SUBSYSTEMS==""
    DRIVERS==""
重要なのは「ATTRS{serial}」というところ。これをキーにデバイスを決定する。
ただし、万が一でも誤爆しないよう念の為「ATTRS{manufacturer}」と「ATTRS{product}=="eReader"」も使うことにした。なので、今回使う情報は以下。
  • ATTRS{manufacturer}=="Kobo"
  • ATTRS{product}=="eReader"
  • ATTRS{serial}=="XXXXXXXXXXXXX"

同じ種類のデバイスをひとつしか持っていないときは、USBデバイスの種類を特定するかぎである「ATTRS{idVendor}」「ATTRS{idProduct}」
を使ってもよいと思う。

udevの設定をする(1) - デバイスに名前をつける

まずは、調べた情報を使って、/dev以下に勝手なシンボリックリンクを置く。今回はkoboなので/dev/kobo。

/etc/udev/rules.dに以下のように設定ファイルを配置すればOK。

/etc/udev/rules.d/92-kobo.rules
KERNEL=="sd*", ACTION=="add", \
  ATTRS{manufacturer}=="Kobo", ATTRS{product}=="eReader", \
  ATTRS{serial}=="XXXXXXXXXXXXX" \
  NAME="%k", SYMLINK="kobo"

あとは、デバイスをつなぐと、/dev/koboが作られる。
$ ls -l /dev/kobo /dev/sda
lrwxrwxrwx 1 root root      3 Dec 23 18:19 /dev/kobo -> sda
brw-rw---- 1 root floppy 8, 0 Dec 23 18:19 /dev/sda

udevadmのman pageを見ると「The udev daemon detects changes automatically」って書いてあるけど、もし認識しなかったら以下のコマンドを叩いてみるといいかも。
$ sudo udevadm control --reload-rules

fstabの設定

/dev/koboがつながったときに、マウントできるようにする。

まずマウントするためのディレクトリを作る。
$ sudo mkdir /media/kobo

koboの内部ストレージは、パーティションなしでデバイスまるまるマウントすればいいので、以下のような行を
/etc/fstabに追加する。

/dev/kobo        /media/kobo     vfat   rw,codepage=932,iocharset=utf8 0 0

マウントできることを確かめる。
$ sudo mount /media/kobo
$ df -h

ディスク容量が表示されたら、umountしてデバイスを外す(本当は切り離しもやったほうがいいんだろうけど…)
$ sudo umount /media/kobo

自動同期するスクリプトを用意する

マウントしてrsyncし、アンマウントしてメールを送るだけの簡単なスクリプトを用意して、
/usr/local/bin/synckobo.shに配置する。


デバイスをつないで、マウントされてない状態で、スクリプトを叩いて同期できたらOK.
見慣れない"--modify-window=1"については、「rsync FAQ」を参照。vfatならつけておくのが幸せだと思う。

udevの設定をする(2) - 接続時にスクリプトを実行する

さきほど作った設定ファイルに「RUN」を追加する。

KERNEL=="sd*", ACTION=="add", \
  ATTRS{manufacturer}=="Kobo", ATTRS{product}=="eReader", \
  ATTRS{serial}=="XXXXXXXXXXXXX" \
  NAME="%k", SYMLINK="kobo", \
  RUN="/usr/local/bin/synckobo.sh"

接続して同期されれば成功!

2012-12-02

自室の照明をスマートフォンからOn/Offする

寝たいんだけど、電気消しに行くのめんどくさくて寝れない。そんなことありませんか?今回はそんな人向けのソリューション。でも、うまく動かなくて勝手についたり、チカチカしちゃったりしても自己責任でお願いします。

ちなみに、おおかたの期待を裏切り、今回はハードウェア工作はナシ。既製品だけ。

仕組み

「電気を消したいだけなのに複雑だ」と言われたので絵にしてみた。


複雑だろうか?ちなみに、結構昔からある方法。

使った物

  1. 天井照明器具専用 リモコンスイッチOCR-04 (\1,500くらい)
  2. パソコン用学習リモコン PC-OP-RS1 (\4,500くらい)
  3. Debian GNU/Linuxの入った箱:今回は玄箱PRO (\不明:今ならGuruPlug (\20,000くらい)や、Raspberry Pi (\4,000くらい)がいいかも)
OCR-04以外は家にあったものなのでかかったのは1,500くらいだけ。

作り方

Step1. OCR-04を設置する

OCR-04はこんな形をしたもの。箱に書いてある通り、照明器具と天井の引っ掛けシーリングの間に受信部をセットするだけ。元々のスイッチはOnにしっぱなしにしないと使いものにならないので、ついてきたリモコンは、スイッチがあったところの隣に置いておく。

Step2. Linux boxとPC-OP-RS1をつなぐ

今回はNASとして稼動していた玄箱PROを使ってしまったが、USBがついたLinuxの箱ならなんでもいいはず。LinuxからPC-OP-RS1は、USBシリアルデバイスとして認識できるが、VendorID/ProductIDが登録されていないので、指示してあげる必要がある。コマンドは以下の通り。
# modprobe ftdi_sio vendor=0x0411 product=0x00b3
これで/dev/ttyUSBXができたらひとまずOK。再起動しても認識される&あとで使うための権限が調整できるよう、以下の設定を行う。

まずvid/pidの設定。

/etc/modprobe.d/remosta.conf作成
alias remosta ftdi_sio
options ftdi_sio vendor=0x0411 product=0x00b3

起動時にロードされるようにする。

/etc/modules追記
remosta

このデバイスが使える人のグループを作る。
# groupadd remosta

このデバイスを/dev/remostaXにし、グループを変更する。

/etc/udev/rules.d/92-remosta.rules作成(92なのは91でこのデバイスのグループ変更がされていたから)
KERNEL=="ttyUSB*", ACTION=="add", \
  ATTRS{idVendor}=="0411", ATTRS{idProduct}=="00b3", \
  MODE="0660", GROUP="remosta", NAME="%k", SYMLINK="remosta%n"
ここの設定は間違えるとOSが起動しなくなるから注意!(一回やってHDD抜き出した…)

再起動して、以下の状態になっていればOK。
$ ls -al /dev/remosta* /dev/ttyUSB0
lrwxrwxrwx 1 root root         7 Nov 23 20:58 /dev/remosta0 -> ttyUSB0
crw-rw---- 1 root remosta 188, 0 Dec  1 22:59 /dev/ttyUSB0

Step3. コマンドラインで送受信してみる

Rubyでスクリプトを書いたので、それを使う。

Ruby/serialportが必要なので、インストールしておく。
# apt-get install libserialport-ruby

あとはこのスクリプトをダウンロードして、以下の通りで動く。

受信
$ ruby remosta.rb recv
Receiving. →ここでPC-OP-RS1に対して、リモコンで送ってみる
fffff070000e03fc07f80f.... →受信した信号
Received.

送信
$ ruby remosta.rb send fffff070000e03fc07f80f....(信号)
Sending.
Sent.

これで「A」と書いてあるほうの黄色いシールがついてるLEDから信号が発信される。

Step3. Web UIをつける

凝るほどでもないので、レガシーにRuby/CGIでWebを作った。
https://github.com/sunnyone/lightremo

スクリプトを/var/www/lightremoに配置したら、以下の感じで設定する。
<Directory /var/www/lightremo>
            AddHandler cgi-script .cgi
            AllowOverride None
            Options +ExecCGI
        </Directory>
今回は自宅の無線LANでしか接続できないところに置いたから、CGIとしてはとくに認証もしていないけど、必要ならかける。

www-dataを"remosta"グループに所属させるのを忘れない。

Apacheの設定をリロードして、http://[SERVER]/lightremo/lightremo.cgiにアクセスすれば以下の画面がでてくるはず。


onやoffを押して実際に照明がOn/Offされたら成功!





2012-10-10

自炊向け本画像処理スクリプト群bkimgproc公開

前回のエントリで予告した、ImageMagickのwrapper script群を公開しました。
https://github.com/sunnyone/bkimgproc
git cloneするか、"ZIP"のボタンを押してzipを落として展開してください。

本1冊ごとにディレクトリを作って連番の素材を入れていき、最後にスクリプトを実行するとまとめてくれるツールです。詳しい使い方は、READMEに載せました。

Ubuntu/DebianであればREADMEの方法で使えますが、普通のshell scriptなので、もしかしたらCygwinでも動くかもしれません。

特徴としては、以下の通りです。
  • pdf, cbz出力(PDFは見開き方向指示に対応)
  • 横向きスキャンデータの回転
  • 白レベル自動判定による白レベル調整
  • kobo向けリサイズ(kobo plugin)
  • 余白の自動カット(kobo plugin)
  • テキストのボールド化(kobo plugin)
使い方にも書いてありますが、白レベル判定は、配置したPDFを単位に行うことで、確実性を高めています。なお、PDFの中でも、偶数と奇数ページで白さが違うことがあったので、別々に判定して対応しています。

PDFの見開き方向指示は、こちらの方法を利用させていただいています。

ちなみにうちでは、convall.shの起動は、Jenkinsを使っています。

2012-03-12

SheevaPlug復旧の巻(debootstrapによる環境構築とSDカード向け設定)

前エントリの通り、SheevaPlugの/だったSDカードが壊れてしまったので、構築しなおすことにする(muninで監視情報を書き込み続けたらそれは壊れるよな、と思いつつ同じ環境を作る)

<準備>
・armelなDebianの入ったデバイス
・SDカードリーダ/ライタ
・SDカード(以下では16GBを利用)

1. パーティション作成
普通にパーティションを切る(以下では/dev/sdbにつながったと想定)
# fdisk /dev/sdb
 以下のように作った。
/dev/sdb1 100M  (/boot用)
/dev/sdb2 10G  (/用)
/dev/sdb3 1G  (swap用)
/dev/sdb4 残り (/home用)

2. ファイルシステム作成
# mkfs.ext3 /dev/sdb1
# mkfs.ext4 -O ^has_journal /dev/sdb2
# mkswap /dev/sdb3
# mkfs.ext4 -O ^has_journal /dev/sdb4
 どうやらSDカードではext4が速いらしいので、/boot以外に採用。
 そしてジャーナルなしが速いらしいので、ジャーナルは無効化する。
電源断に備えても、書き込みが増えて壊れては意味が無いしね。
 /bootはU-Bootが読めなかったら困るので、ext3のままにしておく。
# tune2fs -l /dev/sdb1 | grep features
Filesystem features:      has_journal ext_attr resize_inode dir_index filetype sparse_super
# tune2fs -l /dev/sdb2 | grep features
Filesystem features:      ext_attr resize_inode dir_index filetype extent flex_bg sparse_super large_file huge_file uninit_bg dir_nlink extra_isize
 has_journalがなくなっていることを確認。

3.  debootstrap
debootstrapで初期環境を作る。同一アーキテクチャなので、--foreign と--second-stageに分割する必要はない。
# mount /dev/sdb2 /mnt
# debootstrap --arch=armel --include=linux-image-2.6-kirkwood,module-init-tools,udev,netbase,net-tools,ifupdown,iproute,whiptail,vim-basic squeeze /mnt http://ftp.jp.debian.org/debian/
4. 諸設定
# cd /mnt 
(ファイルシステム設定)

# vi etc/fstab

~~~
#              
proc            /proc           proc    defaults        0       0
/dev/mmcblk0p2 /               ext4    errors=remount-ro,commit=21600,noatime 0       1
/dev/mmcblk0p1 /boot           ext3    defaults        0       2
/dev/mmcblk0p3  none           swap    sw              0       0
/dev/mmcblk0p4 /home           ext4    defaults        0       2
tmpfs          /tmp tmpfs defaults 0 0
~~~
(ホスト名設定)
# echo myhost > etc/hostname
(ネットワーク設定)
# vi etc/network/interfaces
~~~
# This file describes the network interfaces available on your system
# and how to activate them. For more information, see interfaces(5).

# The loopback network interface
auto lo
iface lo inet loopback

# The primary network interface
allow-hotplug eth0
iface eth0 inet static
  address 192.168.1.100
  netmask 255.255.255.0
  network 192.168.1.0
  broadcast 192.168.1.255
  gateway 192.168.1.254
  dns-nameservers 192.168.1.254
  dns-search localdomain.local
~~~
 (コンソール設定)
# vi etc/inittab
一部編集
~~~
##1:2345:respawn:/sbin/getty 38400 tty1
##2:23:respawn:/sbin/getty 38400 tty2
##3:23:respawn:/sbin/getty 38400 tty3
##4:23:respawn:/sbin/getty 38400 tty4
##5:23:respawn:/sbin/getty 38400 tty5
##6:23:respawn:/sbin/getty 38400 tty6
T0:2345:respawn:/sbin/getty -L ttyS0 115200 linux
~~~

てな感じ。

5. おまじない
/etcにあまり書かないようにする。
# ln -s /dev/null etc/blkid.tab
# ln -s /proc/mounts etc/mtab

作っているホストのNIC情報でeth0が固定されてしまうっぽいので、消しておく。
# echo > /etc/udev/rules.d/70-persistent-net.rules
あまり書かないように設定。
# vi etc/sysctl.conf
~~~追記~~~
vm.dirty_background_ratio = 30
vm.dirty_ratio = 60
vm.dirty_expire_centisecs = 500000
vm.dirty_writeback_centisecs = 600000
~~~
6. パスワード設定
# chroot /mnt /bin/bash
# passwd root
7. ブートイメージの作成
chrootの流れで。
# apt-get update
# apt-get install uboot-mkimage
# cd /boot
# mkimage -A arm -O linux -T kernel  -C none -a 0x00008000 -e 0x00008000 \
  -n Linux-2.6.32-5 -d vmlinuz-2.6.32-5-kirkwood uImage
# mkimage -A arm -O linux -T ramdisk -C gzip -a 0x00000000 -e 0x00000000 \
  -n initramfs -d initrd.img-2.6.32-5-kirkwood uInitrd
8. リブート
終わったらいよいよSDカードを差してリブート。U-Bootのカウントダウンで止める。

9. U-Boot環境変数設定
printenvして、期待する値になっていなかったら、以下のようにセットしてしまう。

Marvell>> setenv bootargs_mmc 'console=ttyS0,115200 root=/dev/mmcblk0p2 rw'
Marvell>> setenv bootcmd_mmc 'mmcinit; ext2load mmc 0:1 0x01100000 /uInitrd; ext2load mmc 0:1 0x00800000 /uImage'
Marvell>> setenv bootcmd 'setenv bootargs $(bootargs_mmc); run bootcmd_mmc; bootm 0x00800000 0x01100000'
Marvell>> printenv
Marvell>> saveenv

終わったらbootする。

10. 起動後設定
# date
Thu Nov  5 21:59:39 UTC 1908
さすがにおかしすぎる・・・いったん設定する
# date 031100592012
dateコマンドでセットするときはmmddHHMMyyyyなんだよね。

あとはいつもの感じで設定。
(ロケール設定)
# apt-get install locales
# vi /etc/locale.gen
~~~
ja_JP.UTF-8 UTF-8
~~~
# locale-gen
(スタンダードパッケージをインストール)
# tasksel install standard
(タイムゾーン設定)
# dpkg-reconfigure tzdata
→Tokyo
あとはお好みでsudoを入れたり、openssh-serverを入れたりすれば使えるようになる。

【参考】
http://blog.bofh.it/debian/id_265
http://www.cyrius.com/debian/kirkwood/sheevaplug/install.html
http://d.hatena.ne.jp/yamanetoshi/20090114

Linuxのddで処理状況を定期的に出力する

SheevaPlug につないでいたSDカード(16GB)が壊れてしまったっぽいので、まずはddの丸ごとコピーで復旧を試みる(べつに再インストールしても良いのだが)

# dd if=/dev/sdb of=siva1.bin bs=4096 conv=sync,noerror
これだと進行状況が見えないが、LinuxのddはSIGUSR1をぶつければ終了時に出てくるアレが表示される。なので、whileで送り続ければ、一定間隔で状態を表示できる。
# while true; do sudo kill -USR1 [PID] ; sleep 10; done
しばらくの間10秒間ごとに進行状況を表示していたが・・・
dd: reading `/dev/sdb': Input/output error
1714494+126135 records in
1840629+0 records out
7539216384 bytes (7.5 GB) copied, 674.537 s, 11.2 MB/s
dd: reading `/dev/sdb': Input/output error
1714494+126136 records in
1840630+0 records out
7539220480 bytes (7.5 GB) copied, 674.537 s, 11.2 MB/s
dd: reading `/dev/sdb': Input/output error
1714494+126137 records in
1840631+0 records out
7539224576 bytes (7.5 GB) copied, 674.537 s, 11.2 MB/s

 ・・・(以下最後まで)
だめだー。