Cinnamon の remote で carriage return が…

(2013/05/24追記あり)

デプロイツールとして Cinnamon がカジュアルで良いかなぁと思って使い始めているのですが、remote で実行するコマンドが \r\r\n 以外で出力するとその後の出力がうまく拾えないという問題が発生しました。

\r がどんな場合に出力されるかというと、progress メーターみたいに % 表示する場合ですね。

for my $i (0 .. 10) {
    my $percent = sprintf "\r%3d%%", $i * 10;
    syswrite(STDOUT, $percent, length($percent));
    sleep 1;
}
print "\n";

maven で依存ライブラリをダウンロードするところで引っかかりました。これは mvn -B と batch mode にすることで回避することができます。curl で -s を付けない場合にも download の進捗が表示されますね。

そんで、コードを追っかけてみようかなと。Cinnamon の当該部分は HandleManager.pm の start_async_read() の中

            on_read => sub {
                $handle->push_read(line => sub {
                    my $line = $_[1];
                    push @{$info->{output_lines}}, $line;
                    log info => sprintf "[%s :: %s] %s",
                        $self->{host}, $name, $line;
                });
            },

で push_read の type が line なのは AnyEvent::Handle の↓この部分かな。

register_read_type line => sub {
   my ($self, $cb, $eol) = @_;

   if (@_ < 3) {
      # this is more than twice as fast as the generic code below
      sub {
         $_[0]{rbuf} =~ s/^([^\015\012]*)(\015?\012)// or return;

         $cb->($_[0], "$1", "$2");
         1
      }
   } else {
      $eol = quotemeta $eol unless ref $eol;
      $eol = qr|^(.*?)($eol)|s;

      sub {
         $_[0]{rbuf} =~ s/$eol// or return;

         $cb->($_[0], "$1", "$2");
         1
      }
   }
};

ふむふむ Cinnamon::HandleManager をこう書き換えると期待通りかな?

                $handle->push_read(line => sub {
                            ↓
                $handle->push_read(line => qr|\r?\n|, sub {

これは AnyEvnet::Handle の問題なんでしょうか?

※ 2013/05/24 追記

Cinnamon への Pull Request を merge してもらいました。
https://github.com/kentaro/cinnamon/pull/23

(続)PukiWiki から DokuWiki にデータ移行

先日、「PukiWiki から DokuWiki にデータ移行」を書いた後にもいろいろ改善をすすめたので改めて整理しておく。添付ファイルに対応しました。さらにその後、UTF-8対応の PullRequest をもらってマージしました。

データ移行スクリプトは Github https://github.com/yteraoka/puki2doku に。

PukiWki が /var/www/html/pukiwiki に、DokuWiki が /var/www/html/dokuwiki にある前提とする。
各データもそれぞれのデフォルトのフォルダを使用している前提。

添付ファイルのコピー

添付ファイルは PukiWiki では attach/ フォルダに、DokuWiki では data/media/ フォルダに保存される。
PukiWiki は各ページに対して添付するという考え方だが、DokuWiki はページとは独立した名前空間にファイルをアップロードする。データコピーの方法として、ページ名と同じ階層でディレクトリを作成し、そこへコピーすることとした。PukiWiki では attach フォルダに {ページ名}_{ファイル名} というファイル名で作成されている。これを {ページ名}/{ファイル名} とする。{ページ名} にも / が含まれる。それぞれ、ファイルシステム上はページと同様のエンコーディングが採用されている。

$ puki2doku.pl -v -A \
  -s /var/www/html/pukiwiki/attach \
  -d /var/www/html/dokuwiki/data/media

課題
PukiWiki はページに結びついているので、ページ内にリンクが書かれていなくても添付ファルへアクセスできるのだが、DokuWiki ではページ内にリンクがないと迷子になってしまう。ページに添付ファルのリストを追記すべきかもしれない。

wiki ページの変換

$ puki2doku.pl \
  -v \
  --font-color \
  --indexmenu \
  --ignore-unknown-macro \
  -s /var/www/html/pukiwiki/wiki \
  -d /var/www/html/dokuwiki/data/pages

前回は --font-size で &size(N){str}; を fontsize plugin を利用して引き継ぐようにしていたが、このプラグインは他のテキスト装飾を入れ子にできないという問題があったため無効とした。

検索インデックス

インデックスを作成しないと検索できないので次の手順で作成します。

$ cd /var/www/html/dokuwiki/bin
$ php indexer.pp

この作業やデータ移行を DokuWiki の実行ユーザー以外で実行した場合は
/var/www/html/dokuwiki/data 配下を全部 DokuWiki 実行ユーザーで書き換え可能にしましょう。

キャッシュとかメタデータ

DokuWiki は data/cache/, data/meta/ などにキャッシュを作成するので、移行手順をやり直す場合などはこのあたりのファイルを消してやる必要があります。

LDAP認証

DokuWiki は標準で LDAP 認証をサポートしている。

LDAP サーバーとして以前取り上げた(その1その2) OpenDJ を使っている場合は
dokuwiki/conf/local.php に次のように書くことで

$conf['useacl'] = 1;
$conf['authtype'] = 'ldap';
$conf['openregister'] = '0';
$conf['auth']['ldap']['server'] = 'ldap://ldap.example.com:1389';
$conf['auth']['ldap']['usertree'] = 'ou=People,dc=example,dc=com';
$conf['auth']['ldap']['grouptree'] = 'ou=Group,dc=example,dc=com';
$conf['auth']['ldap']['userfilter'] = '(&(uid=%{user})(objectClass=inetorgperson))';
$conf['auth']['ldap']['groupfilter'] = '(&(objectClass=groupOfUniqueNames)(uniqueMember=%{dn}))';

dokuwiki/conf/acl.auth.php@グループ名 としてグループでの権限設定も可能となる。

矢印とかの自動置換

dokuwiki/conf/entities.conf

<->     ↔
->      →
<-      ←
<=>     ⇔
=>      ⇒
<=      ⇐
>>      »
<<      «
---     —
--      –
(c)     ©
(tm)    ™
(r)     ®
...     …

というページ表示時に自動的に置換して表示する機能がありますが、技術系の文書を書いてる場合「--」とか「<<」などを置換されるとウザイので全部コメントアウトしてしまいましょう。

諸設定

スーパーユーザーに設定されたアカウントであれば管理ページにアクセスしてページ名やデザインやプラグインの有効化・無効化などいろいろな設定ができます。
スーパーユーザー設定は conf/local.php

$conf['superuser'] = 'user1,user2';

と設定します。複数人指定する場合はカンマ区切りで指定。
前回書いたファイル名のエンコーディングはここで変更可能でした。UTF-8でそのままファイルを作成することも出来るようですが、プラグインなどの互換性の問題で推奨されてません。
トップページを start から PukiWiki と同じ FrontPage にすることもできますが、こちらも互換性の問題で変更は推奨されていませんでした。

これで一段落かな。(もうちょっとかっこいい Template ないかなぁ...)

PukiWiki から DokuWiki にデータ移行

PukiWiki から DokuWiki にデータ移行するメモです。
移行されるかたは是非続編もご覧ください。

DokuWiki Plugiin

DokuWiki は plugin なしでは PukiWiki よりも表現力が劣るので、次の Plugin を導入しました。

ファイル名の命名規則

PukiWiki と DokuWiki ではファイル名の命名規則が異なります。

  • PukiWiki はページ名を EUC-JP で、アルファベットも記号も込で全部を16進のコードにしてしまいます
  • 「/」も16進にするため、wiki ディレクトリにフラットに全てのファイルが配置されます
  • DokuWiki ネームスペースを「/」で区切るため、ネームスペースがディレクトリになります
  • 文字コードは UTF-8 で、記号(マルチバイトのものも一部含む)は「_」に置き換え、アルファベットは小文字に統一します
  • 連続する「_」はひとつにまとめ、ディレクトリ、ファイル名の末尾の「_」は削ります
  • マルチバイト文字は URL エンコードされます。EUC-JP から UTF-8 への変更でほぼ2バイトだったものが3バイト以上になり、ただの HEX だったものが「%」+ HEX となり50%増量で、日本語ページ名のファイル名は結構な長さになります
  • 256バイトを超えて、そのままでは移行できないページが発生しました
  • アルファベットばかりのページ名であれば短くなります

PukiWiki も DokuWiki も拡張子「.txt」が付きます。

PHP で dokuwiki のファイル名生成関数を使うのが素直だと思いますが、PHP得意じゃないのでデータの変換処理書くのに時間がかかりそうだったから Perl で書きました。

script

./puki2doku.pl -C -S -I \
  -s pukiwiki/wiki -d dokuwiki/data/pages

綺麗なコードではないですが、公開しておきます。
puki2doku.pl

2013/04/19
いくつか Bug を修正して、Git に移しました。

  • Table Cell の色付けには対応してません
  • FrontPage を start に置換します
  • 「- - - -\n#contents\n- - - -\n」という私がよく使っていた TOC のためのコードを消す特殊処理が入ってます
  • touch コマンドで元のファイルの timestamp をコピーします

添付ファイル

対応してません。(やりかけた形跡が残ってますけど...)

後に対応しました。「(続)PukiWiki から DokuWiki にデータ移行

検索インデックス

データ移行後に

cd dokuwiki/bin
php indexer.php

で検索インデックスを作成してください。
Web からではなくファイルを直接変更した場合はこの処理が必要です。

今日はこれまで。

2013/04/20 追記
fontsize plugin は color や bold 装飾と入れ子で使う場合一番内側に置かないと他の装飾が効かなし「**」とかがそのまま表示されてしまう・・・これは厄介。fontsize は無効にするかな。

libc の buffer と perl の buffer

libc の buffer と perl の buffer は違うんだよという話を聞いたのでちょい調べてみた。

libc の buffer は grep を pipe で複数つなぐとなかなか表示されないやつ

$ while : ; do echo hoge; sleep 1; done | grep hoge

↑これはすぐに hoge が表示されますが

$ while : ; do echo hoge; sleep 1; done | grep hoge | grep hoge

↑こっちはずーっと待ってないと出力されません。出力先が端末でない場合、fwrite とかは buffering されるんですね。すぐに出力したい場合は次のように

$ while : ; do echo hoge; sleep 1; done | grep --line-buffered hoge | grep hoge

"--line-buffered" オプションをつけることで、毎行 fflush() が実行されてすぐさま出力されます。もうひとつ

$ while : ; do echo hoge; sleep 1; done | stdbuf -o0 grep hoge | grep hoge

と、stdbuf で LD_PRELOAD を使って buffer をコントロールするという方法もあるようです。(How to fix stdio buffering)

で、Perl もこんな仕組みで buffering されてるんだろうと思ってたら違うんですね。
7.12. Flushing Output

$ perl -e 'while (1) { print "hoge\n"; sleep 1; }' | grep hoge
$ stdbuf -o0 perl -e 'while (1) { print "hoge\n"; sleep 1; }' | grep hoge

このどちらもすぐには出力されません。
次のようにするしかないようです。(IO::Handle 使っても良い)

$ perl -e '$|=1; while (1) { print "hoge\n"; sleep 1; }' | grep hoge

なるほどねぇ。

awk には fflush() っていう関数があって、sed には --unbuffered というオプションがあるらしい。

grep, awk, sed でバッファしない方法

いまさら cpanm

もともと Perl メインでプログラミングしてたのですが、CPAN が肥大化(ただメール送るためだけに Email::Sender::Simple を入れようとしたらすげー依存で大量の CPAN モジュールがインストールされて Enter 打つだけで疲れた、あと yum で入る奴とそうでないのが混ざって気持ち悪い)してたのでなんか面倒で避けてたのですが、リターンメールの処理に BounceHammer を使ってみようかなと思って重い腰を上げてみた。

自分でコード書くときはできるだけ依存させないように、標準モジュールでおさまるようにしてた。大したコードではないけれど。もちろん車輪の再発明もある。

cpanm すら避けていたが、Rails が bundler でかなりイケてる感じなので Perl にも似たようなのないかなと思ったら、やっぱりあるんですね Carton !!
あれ、でもα版で止まってる???

Carton 使ってたけど cpanm に戻したっていう blog もあったので cpanm だけで行く事に。

$ cpanm -l /path/to/install ModuleName

って超便利!! なぜ今まで試さなかったんでしょうか。
こんなに簡単にインストールできるのに

$ cd ~/bin
$ curl -LO http://xrl.us/cpanm
$ chmod +x cpanm

あと、これ

$ export PERL5OPT=-I$HOME/perl5/lib/perl5

404 Blog Not Found:perl - の@INCを実行寸前に変更する

@miyagawa さんありがとう!!