2012-12-16

Python: cElementTree returns non open object

xml.etree.cElementTree は、xml.etree.ElementTree の C 実装だ。

ElementTree クラスはエレメントの構造を包み込み、それと XML を行き来するのに使えます。

この API の C 実装である xml.etree.cElementTree も使用可能です。

C 実装であること以外、ElementTree と cElementTree の違いについては述べられていない。違いがないなら、C 実装の方を使わない手はない。しかし、

$ python
Python 2.6.6 (r266:84292, Sep 12 2011, 14:03:14)
[GCC 4.4.5 20110214 (Red Hat 4.4.5-6)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import xml.etree.ElementTree
>>> html = '<html></html>'
>>> tree = xml.etree.ElementTree.fromstring(html)
>>> tree.html = html
>>> tree.html
'<html></html>'
$ python
Python 2.6.6 (r266:84292, Sep 12 2011, 14:03:14)
[GCC 4.4.5 20110214 (Red Hat 4.4.5-6)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import xml.etree.cElementTree
>>> html = '<html></html>'
>>> tree = xml.etree.cElementTree.fromstring(html)
>>> tree.html = html
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: html

cElementTree が返すオブジェクトはオープン・オブジェクトではないらしく、属性を追加することができない。C 実装されたモジュールとはそういうもんか、とも思ったが、

$ python
Python 2.6.6 (r266:84292, Sep 12 2011, 14:03:14)
[GCC 4.4.5 20110214 (Red Hat 4.4.5-6)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import cProfile
>>> p = cProfile.Profile()
>>> p.foo = 'foo'
>>> p.foo
'foo'

同じ C 実装モジュールの cProfile では問題ない。想像するに、ElementTree の場合、木構造で大量のオブジェクトを使用するためメモリをケチってるのかも知れない。

ともかく、RHEL5 + elementtree で作ったスクリプトを RHEL6 で動そうと喜んで cElementTree を使った私にとっては、この上ないぬか喜びだった。

2012-11-21

psycopg2: Can't mogrify `None' in a tuple to `NULL'

RHEL6 付属の psycopg2 で、

# rpm -qa | grep psycopg
python-psycopg2-2.0.13-2.el6_1.1.x86_64

tuple 中にある None を mogrify するとエラーになる。

test1.py:

#!/usr/bin/python

import psycopg2.extensions

def main():
    conn = psycopg2.connect('dbname=testdb user=testuser')
    cur = conn.cursor()
    for param in [0, [0], (0,), '', [''], ('',), None, [None], (None,)]:
        print '%s\t->' % repr(param), cur.mogrify('%s', [param])

if __name__ == '__main__':
    main()
$ python test1.py
0       -> 0
[0]     -> ARRAY[0]
(0,)    -> (0)
''      -> E''
['']    -> ARRAY[E'']
('',)   -> (E'')
None    -> NULL
[None]  -> ARRAY[NULL]
(None,) ->
Traceback (most recent call last):
  File "/root/tmp/test1.py", line 12, in <module>
    main()
  File "/root/tmp/test1.py", line 9, in main
    print '%s\t->' % repr(param), cur.mogrify('%s', [param])
  File "/usr/lib64/python2.6/site-packages/psycopg2/extensions.py", line 100, in getquoted
    qobjs = [str(o.getquoted()) for o in pobjs]
AttributeError: 'str' object has no attribute 'getquoted'

落ちている場所は、

/usr/lib64/python2.6/site-packages/psycopg2/extensions.py:

# The SQL_IN class is the official adapter for tuples starting from 2.0.6.
class SQL_IN(object):
    """Adapt any iterable to an SQL quotable object."""

    def __init__(self, seq):
        self._seq = seq

    def prepare(self, conn):
        self._conn = conn

    def getquoted(self):
        # this is the important line: note how every object in the
        # list is adapted and then how getquoted() is called on it
        pobjs = [adapt(o) for o in self._seq]
        for obj in pobjs:
            if hasattr(obj, 'prepare'):
                obj.prepare(self._conn)
        qobjs = [str(o.getquoted()) for o in pobjs]
        return '(' + ', '.join(qobjs) + ')'

    __str__ = getquoted

register_adapter(tuple, SQL_IN)

print デバッグすると直ぐに分かるが、このとき o には 'NULL' という文字列(str 型)が入っている。当然、単なる文字列に getquoted なんてメソッドがあるはずがない。そりゃ落ちるわ。

何がどうあるべきなのかは分からないが、「動かないシステムに価値はない」の持論に従い、ともかく動くようにする。

test2.py:

#!/usr/bin/python

import psycopg2.extensions

# Fix a bug that can't mogrify `None' in a tuple to `NULL'
class FIXED_SQL_IN(object):
    def __init__(self, seq):
        self._seq = seq

    def prepare(self, conn):
        self._conn = conn

    def getquoted(self):
        adapt = psycopg2.extensions.adapt
        pobjs = [adapt(o) for o in self._seq]
        for obj in pobjs:
            if hasattr(obj, 'prepare'):
                obj.prepare(self._conn)
        qobjs = [getattr(o, 'getquoted', o.__str__)() for o in pobjs]
        return '(' + ', '.join(qobjs) + ')'

    __str__ = getquoted

psycopg2.extensions.register_adapter(tuple, FIXED_SQL_IN)

def main():
    conn = psycopg2.connect('dbname=testdb user=testuser')
    cur = conn.cursor()
    for param in [0, [0], (0,), '', [''], ('',), None, [None], (None,)]:
        print '%s\t->' % repr(param), cur.mogrify('%s', [param])

if __name__ == '__main__':
    main()
$ python test2.py
0       -> 0
[0]     -> ARRAY[0]
(0,)    -> (0)
''      -> E''
['']    -> ARRAY[E'']
('',)   -> (E'')
None    -> NULL
[None]  -> ARRAY[NULL]
(None,) -> (NULL)

ということを、実は 2 年くらい前に RHEL5 + psycopg2-2.0.12 (from rpmforge)という組み合わせでやっていたのだが、最近になって検索すると次が出てきた。

本件は psycopg2-2.4 で直っている模様。しかし私が試した限り、この変更部分を 2.0 系に持ってきても動かない。None に対してアダプターを登録しても、全く動く気配がない。None に対するアダプターが動くようになる変更が 2.4 までの何処かに入ったのかも知れないが、私的にはもう上述の方法で解決しているのでこれ以上は調べる気なし。

2012-10-31

Animes in the 3rd quarter of 2012

アニメは IT エンジニアの必須科目です。 ということで簡単なレビューを。

前期は面白く無かったのに、第二期が始まってしまった。何とか面白いこと言おうと一生懸命なのは伝わってくるが、殆ど空回るので見てて痛々しい。ただ、黄・紫・メガネの掛け合いは良かった。ていうか「コミック百合姫」とかあるんですか? つくづく日本人って凄いと思う。確かに女子しか出てこないし、タイトルの意味もよく分かった。

最初はどうなることかと思ったが、我慢して見てたら後半は普通に見れた。キャラ付けもできてると思うし、意外なことに AKB が当てている声も慣れればそれっぽく聞こえた(一部を除く)。ただ、それなら襲名メンバーにプロ声優を当てたのが中途半端だと思う。突っ込み所を数えれば切りが無いが、結果としてこれはこれでアリだった気がする。

最初の一人目、幼馴染とはいえ、さっきまでの敵同士が手のひら返して「オレたち親友」とか言ってる所からもうダメだった。その後も仲良しごっこ、恋愛ごっこ、主人公のコンプレックス丸出し言動など、色々とイタいところが多すぎる。せっかく世界観はなかなか良いと思うのに、脚本で全てが台無しになるという良い例だった。

主人公がイライラするのは前作と同様。個人的にはエレナがツボったが、エウレカスイッチでヒステリーになるのが残念。前作もそうだったが、このシリーズはヒステリー持ちが多くて楽しくない。全体的な点としては、エウレカの存在感が大きすぎて今作キャラがすっかり霞んでしまった感がある。寧ろエウレカは出さない方が良かったのでは。

前回はイマイチだったが、今回も同様。ロボットものなのに途中からロボットに乗るの放棄するし、いったい何処に向かっているのか。敵味方が行使する力の理屈も世界観もさっぱり伝わってこないので、楽しむものがキャラくらいしか無い。しかしキャラの魅力も今ひとつなので詰んでる。絵は綺麗なだけに、色々と勿体ない作品だった。

基本、一話毎に新キャラが出るので、キャラを覚えられない。これは構成のミスだと思う。数あるキャラでは、秀吉がお気に入り。米を食って幸せ一杯の「ウマー」にやられた。声が名塚佳織というのも意外で良かった。少年サンデーでも短期連載があったが、両者の中身は全くの別物。笑えるという意味では漫画の方が勢いがあって良かった。

前作に引き続き、話がさっぱり分からない。もうこれは「要、予習・復習」とかいうレベルではなく、これがカッコイイと勘違いしているのでは。せめて重要そうな「歴史再現」の説明はもっと kwsk。訳の分からなさは徹底しているので、後はキャラを楽しむだけ。もう少しビギナーへの配慮が欲しかった。普通は Wikipedia とか読まないからね。

サンライズにしては絵がチャチく感じる。チラ見した限りは原作の方が綺麗に見える。中身は典型的なドタバタ劇で、花澤香菜(ヒロインの中の人)の頑張りが伝わってくる。が、それでも枠の中に収まっている感が否めない。他作品と比べるのは良くないが、同じサンライズとして銀魂・SKET DANCE くらいを期待してしまうのは酷だろうか。

主人公は苦もなく強力な力を手に入れる -> 強いのでモテる、というご都合ハーレム展開。一応、神々との戦いでは何か理屈が働いてるっぽいのだが、神話マニアじゃないから良く分からん。取り巻きに好みさえ居れば、まあ見れる。私は青い人(喜多村英梨)が真ん中だったので無問題だった。もし続きがあれば、青い人目当てで見ると思う。

武将を女子化するのはもういい加減に食傷ぎみだが、絵は好みの部類。しかしヒロインが大してイケてない主人公にデレるとか、ぜんぜん信長っぽくない。史実の改変を許容しているので、話もどうしてもご都合展開になってしまう。加えて主人公は典型的な「気持ち」だけの人で努力の描写が欠如している。残念だが私には楽しむのは無理だった。

視聴前の印象よりは良かったが、その後の期待には及ばなかった感じ。個人的には第二話がピーク。合唱がテーマの割には合唱シーンが少ない。その合唱も全員が同じパートを歌うのではなく、少なくとも(キーが異なるのだから)男女でパートを分けた歌をもっと聞きたかった。と細かい不満を言いたくなるくらい、個人的には惜しい作品。

果たして「超訳」は言い過ぎだったと思うが、まあ確かに今風のノリとツッコミでアレンジされている。ワクワクする面白さは無いが、退屈することもない。逆に、百人一首に思い入れがあると受け付けないかも知れない。ところで毎度こういうのを見るにつけ、つくづく人間って千年経っても 1 ミリも進歩してないんだなあと実感する。

原作はどうもに読む気にならないが、アニメ版は素直に面白かった。テンポが良いのと、何より白石涼子(姫子)の力が大きい。何でこの人はこんなにも方言キャラにハマるのだろうか。もし別の声優が当たっていたら、今よりずっとつまらなかったと思う。後釜は「銀魂」とのことで、銀魂 -> SKET DANCE の無限ループが見えた気がする。

アサルトライフルを女子化とか誰が考えた。挙句に「T バック」とか「撃鉄落ちちゃう」とか、バカじゃねーの? もう、同じ日本人として誇りに思うよ:-D。メインキャラには萌えなかった(高等部の方が良かった)が、その他でお腹一杯になったので良し。ライフルへの愛が伝わってくる作品ではあるが、一般人にはなかなか勧められない作品。

前期は未見、原作は既読。そのせいかも知れないが、これはアニメ化する意味があったのだろうか。本作の魅力は女子キャラと発酵のうんちくだと思っているが、アニメになったせいで後者が著しく損なわれてしまった。かといって前者が立っているかというと、何か声優がミスマッチのような気がするし。まあ原作の復習としては丁度良い感じ。

少女マンガ原作だが、結果はイマイチ。まず個人的な好みの問題として、ヒロインに萌えなかったのが大きい。そして中盤から主人公が精神世界に行きっぱなしになり、ずっと支離滅裂な世界を見せられた後、現実に戻ったらハッピーエンドを通り越してお葬式。って感情移入する暇が全く無いんだけど。原作もこうなの? ただ ED は良かった。

やっぱり「ガンダム」の冠は重いよね、という予想通りの結果。AGE の意味が分かったときには新鮮な驚きはあったが、親子三代はやり過ぎだろう。作風から低年齢が主なターゲットに見えるが、ガンダムファンはみんな高年齢なので評価されるのは難しいと思う。とはいえ SEED の例もあるし、そのうち当たりもあるだろうと次に期待。

前作に続き視聴。キャラは倍に増えたが、話はますます暗くなった。どいつもこいつもコンプレックスの固まりで、見てるこっちが陰鬱になってしまう。パズルの楽しさは何処にいった。あと、やっぱり個人的に萌えるキャラが居ない。エレナは相変わらず出番が少ないし。何を思ったか更に 3 期に続くようだが、このままでは期待できる要素が無い。

ジャンプのスポーツ漫画らしく、必殺技がデフォルト。まだ本作は人間の範疇に見えなくもないので我慢できるが、そのうち影分身とかするようにならないか心配。必殺技に目をつむって人間関係だけを見れば、普通に面白いと思う。個人的には、もっと(本作にも無い訳ではないが)正しい努力の積み重ねが結果に結びつくという描写が欲しい。

思わず見とれてしまう程の、絵の美しさ。これだけでも見る価値はある。話の方はグッとくる面白さは無いが、地味に面白い。絵のせいもあり独特な雰囲気があるので、深夜にしんみり雰囲気に浸って見るのがお勧め。個人的には色々と納得いかない推理も多かったが、それはきっと私がバカだからだろう。あと、1st OP は良かった。

OP や初話のノリで期待して見ると、実は結構な重い話に裏切られる。各キャラが個々のイベントにマジレスし過ぎ。そりゃ本当に人格が入れ替わったら大変だよ。心を壊す輩も出てくるさ。しかしその葛藤をアニメにする意味が分からん。ハッピーエンドになるのは分かってるが、道中もハッピーにして欲しかった。キャラが魅力的なだけに残念。

しかしこう書き連ねてみると、いい加減にアニメ見過ぎだな。もう少し絞らないと時間がいくらあっても足りない。しかしアタリかハズレかなんて見ないと分からないしなあ。

2012-09-24

RHEL6: multi NICs in the same subnet

タイトルは「NIC」としているが、どちらかと言えば「インターフェイス」の意味。要は複数 LAN ポートを同一サブネットに繋ぎたい。

例として、次のように設定する。

  • eth0: 192.168.1.11/24 (00-0c-29-11-11-11)
  • eth1: 192.168.1.12/24 (00-0c-29-22-22-22)

このとき双方に ping を打つと、実際には eth0 だけが応答する。

>ping 192.168.1.11
>ping 192.168.1.12
>arp -a
インターフェイス: 192.168.1.101 --- 0xc
  インターネット アドレス      物理アドレス      種類
  192.168.1.11          00-0c-29-11-11-11     動的
  192.168.1.12          00-0c-29-11-11-11     動的

これは Linux ネットワークの仕様。RHEL5 だと、次を設定することで両方から ping が返るようになる。

net.ipv4.conf.all.arp_announce = 2
net.ipv4.conf.all.arp_ignore = 1
>ping 192.168.1.11
>ping 192.168.1.12
>arp -a
インターフェイス: 192.168.1.101 --- 0xc
  インターネット アドレス      物理アドレス      種類
  192.168.1.11          00-0c-29-11-11-11     動的
  192.168.1.12          00-0c-29-22-22-22     動的

しかし、RHEL6 で同じ設定をしても動かない。具体的には、eth1 への ping が返ってこず、ARP テーブルにも登録されない。

>ping 192.168.1.11
>ping 192.168.1.12
>arp -a
インターフェイス: 192.168.1.101 --- 0xc
  インターネット アドレス      物理アドレス      種類
  192.168.1.11          00-0c-29-11-11-11     動的

この理由がずっと分からなかったが、どうやら RHEL6 から rp_filter の挙動が変わったようだ。結論から言うと、次の設定で RHEL6 でも動くようになる。

net.ipv4.conf.all.arp_announce = 2
net.ipv4.conf.all.arp_ignore = 1
net.ipv4.conf.all.rp_filter = 2

理由はこの辺りを参照。

RHEL5 では、conf.all.rp_filter と conf.<interface>.rp_filter との論理積(AND)が rp_filter の実効値だったが、RHEL6 では最大値(MAX)が実効値になった。

RHEL5 / RHEL6 のどちらにも、/etc/sysctl.conf に次の設定がある。

# Controls source route verification
net.ipv4.conf.default.rp_filter = 1

よって RHEL5 の場合、rp_filter の値は次のようになる。

[rhel5]# sysctl -a | grep '\.rp_filter' | sort
net.ipv4.conf.all.rp_filter = 0
net.ipv4.conf.default.rp_filter = 1
net.ipv4.conf.eth0.rp_filter = 1
net.ipv4.conf.eth1.rp_filter = 1
net.ipv4.conf.lo.rp_filter = 0

rp_filter の実効値は、eth0 であれば all.rp_filter AND eth0.rp_filter (=0)となり、eth0 の rp_filter は無効となる。つまり /etc/sysctl.conf にそれらしい設定がありながら、実は 全く効いていなかった ということ。騙された。

RHEL6 の場合は、

[rhel6]# sysctl -a | grep '\.rp_filter' | sort
net.ipv4.conf.all.rp_filter = 0
net.ipv4.conf.default.rp_filter = 1
net.ipv4.conf.eth0.rp_filter = 1
net.ipv4.conf.eth1.rp_filter = 1
net.ipv4.conf.lo.rp_filter = 1

今度は MAX(all.rp_filter, eth0.rp_filter) (=1)となり、rp_filter は有効となる。RHEL5 と全く同じ動作にしたいなら、/etc/sysctl.conf で net.ipv4.conf.default.rp_filter = 0 と設定する必要がある。

今回の目的(同一サブネットに複数インターフェイス)では、rp_filter = 2 にすれば良い。これは loose mode というらしく、いずれかのインターフェイスから到達可能な宛先であればパケットを受信する、というモードのようだ。RFC3704 なんて読む気ないので詳しくは知らない。

そもそも今回の件で rp_filter が影響することが分かり辛いが、極力プライマリインターフェイスで通信しようとする Linux の特性を考えると、何となく理解できなくもない。今回の場合、プライマリ側である eth0 が ARP パケットを処理する際に rp_filter が効くのだろう(と思う)。が、やはり Linux のこの特性は分かりにくい(直感的でない)と思う。

以上のように、Linux で複数インターフェイスを同一サブネットに繋ごうとすると、非常に微妙な領域に足を突っ込むことになる。やるなら 1 個のインターフェイスに仮想 IP アドレスを付ける方法(仮想インターフェイス)をお勧めする。じゃあ私は何故そうしないのかと言うと、現場 SE に「仮想 IP アドレス」とか言っても通じないから。少なくとも自力で設定できるとは思えない。しかし「1 つの LAN ポートに 1 つの IP アドレス」であれば、彼等にも理解してもらえる確率がグッと上がる。:-)

2012-09-02

Tomcat: Cannot stop in wrong hosts configuration

あるサーバーだけ、Tomcat の停止に失敗する。

2012/08/29 15:56:51 org.apache.coyote.http11.Http11Protocol pause
情報: Coyote HTTP/1.1を http-8080 で一時停止します
2012/08/29 16:00:00 org.apache.catalina.connector.Connector pause
致命的: プロトコルハンドラの一時停止に失敗しました
java.net.ConnectException: Connection timed out
	at java.net.PlainSocketImpl.socketConnect(Native Method)
	at java.net.PlainSocketImpl.doConnect(PlainSocketImpl.java:351)
	at java.net.PlainSocketImpl.connectToAddress(PlainSocketImpl.java:213)
	at java.net.PlainSocketImpl.connect(PlainSocketImpl.java:200)
	at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:366)
	at java.net.Socket.connect(Socket.java:529)
	at java.net.Socket.connect(Socket.java:478)
	at java.net.Socket.<init>(Socket.java:375)
	at java.net.Socket.<init>(Socket.java:218)
	at org.apache.jk.common.ChannelSocket.unLockSocket(ChannelSocket.java:492)
	at org.apache.jk.common.ChannelSocket.pause(ChannelSocket.java:288)
	at org.apache.jk.server.JkMain.pause(JkMain.java:726)
	at org.apache.jk.server.JkCoyoteHandler.pause(JkCoyoteHandler.java:153)
	at org.apache.catalina.connector.Connector.pause(Connector.java:1064)
	at org.apache.catalina.core.StandardService.stop(StandardService.java:578)
	at org.apache.catalina.core.StandardServer.stop(StandardServer.java:788)
	at org.apache.catalina.startup.Catalina.stop(Catalina.java:662)
	at org.apache.catalina.startup.Catalina$CatalinaShutdownHook.run(Catalina.java:706)
2012/08/29 16:00:01 org.apache.catalina.core.StandardService stop
情報: サービス Catalina を停止します

Tomcat バージョンも示しておくが、多分どのバージョンでも起こる。

[appserver]# service tomcat6 version | grep version
Server version: Apache Tomcat/6.0.35

ちなみにタイトルは「Cannot stop」としているが、正確には上記のタイムアウトに時間が掛かる(3 ~ 5 分)だけで、待ってさえいれば停止する。

最初は、何でシャットダウンするのに socket が timed out するのかと思ったが、自身のシャットダウンポートへ通信しているのではと勘付けば、ネットワークに問題がありそうだと察しが付く。(ソースコードは見てないので、実際のところは知らない)

[appserver]# ifconfig | grep 'inet addr'
          inet addr:172.16.1.10  Bcast:172.16.1.255  Mask:255.255.255.0
          inet addr:127.0.0.1  Mask:255.0.0.0

/etc/hosts:

# Do not remove the following line, or various programs
# that require network functionality will fail.
127.0.0.1		localhost.localdomain localhost
::1		localhost6.localdomain6 localhost6
192.168.10.12	appserver

案の定、実際の IP アドレス(172.16.1.10)と /etc/hosts (192.168.10.12)が食い違っている。私が試した限り、それぞれが別ネットワーク(サブネット)だと今回のエラーになるようだ。食い違いがあっても、同じネットワークだと起こらない。

/etc/hosts を修正して、ネットワークを再起動すれば解決する。

# Do not remove the following line, or various programs
# that require network functionality will fail.
127.0.0.1		appserver localhost.localdomain localhost
::1		localhost6.localdomain6 localhost6

所詮、現場 SE の程度なんてこんなもんだ。IP アドレスを変更する際に /etc/hosts に気を払うこともできない。ちなみに今回の場合、上記に加え /etc/resolve.conf も /etc/sysconfig/network もボロボロだったことを付け加えておこう。

そしてこういうことがあるから、特に理由のない限り、自ホスト名は「127.0.0.1」に登録するのが正しい。これについては以前のエントリで述べた通り。

127.0.0.1 ではなくサーバーの IP アドレスで登録すべき、と言われそうだが、それでは DHCP 環境で使えない。固定 IP アドレス環境でも、永遠に IP アドレスを変えないと保証できるはずもない。その際に IP アドレスを変更する SE が、/etc/hosts に気を回すスキルを持っていると期待するほど、私はお人好しじゃない。

2012-08-07

Animes in the 2nd quarter of 2012

アニメは IT エンジニアの必須科目です。 ということで簡単なレビューを。

前作は最初のインパクトが強すぎて以降が霞んでしまったが、今回もその流れのままブレイクには至らず。キャラも順調に増えていっているのに、イマイチ突き抜けられないのは何故だろう。更に続きそうな終わり方だったが、次回があっても期待薄か。ただ、毎話末にある妄想ユウ劇場は良かった。ED も、悔しいけど好き。

元ネタを知らないので、暫くは女子向けだと気付かなかった。しかしそれを差し引いても何が面白いのかサッパリ分からんかった。これって女子が見ても面白いの? ヒロインもヒーロー(イケメン)もひたすら弱いし、最後の逆転もインチキっぽいし。良かったのは OP だけだった。

この作品の失敗は 2 つ。1 つは OP にオバさんを使ったこと。まずここで拒絶反応。素直に沢城みゆきで良かった。もう 1 つは作風を劇画調でシリアスにしたこと。ルパンといったらビビッドカラーのコミカルに決まってる。原作派には寧ろ原作に近いと高評価だったみたいだが、殆どの人はアニメのルパンしか知らないから。

前作は何処か野暮ったく、それが作品の独特な雰囲気だったのだが、今作はすっかりオシャレになってしまった。悪く言えば普通になった。相変わらず普通じゃないのは男女合体と曲(菅野よう子)。但し個人的には、前作の主題歌のインパクトは超えられなかった。あと、総司令の「全てお見通し」感がウザいのも相変わらず。

OP が Kalafina だったのでよく見たら、前作(第一期)から音楽が梶浦由紀。OP/ED を聞いてつくづく思うのは、See-Saw のボーカルの人は偉大だったな、と。特に ED は彼女が歌っていれば神曲になったに違いない。中身の方は、正直なところ前作の方が楽しめた。元ネタに思い入れがあれば、また違う結果になったかも知れない。

前作は不勉強ながら未見。前作の英雄が上官になって部下を指導とか、DESTINY でヘタれたアスランを髣髴とさせる。その「ヘタれ」はリミッターにより正当化されているが、それが「新キャラの成長」と「前作キャラの活躍」のどっちつかずを生んでしまった気がする。いっそ新キャラメインに割り切った方が良かったのでは。

少女マンガ原作にしては、ジャズとか時代設定とか渋い。しかも音楽が菅野よう子。私はジャズのことはさっぱり分からないが、彼女の懐の深さには舌を巻く。加えて、YUKI・秦基博に曲を提供とかやり過ぎじゃね? 肝心の話の方は良く纏まっていると思う。終わりも綺麗だったし。ただちょっと綺麗すぎたという感もある。

なかなかオリジナリティーの高い作品。それを認めつつ、宇宙人設定を差し引いたとしても、ハルのズレた性格がどうにも好きになれなかった。あとコンプレックス主人公も。彼等さえ受け入れられたら、もっと高評価だったと思う。OP も良かったし、「ダック」の決めポーズもぶっちゃけ好きだ。

今期一番の伏兵。ヒロインがかなり個性的だが、頼もし過ぎる仲間の存在とで見事にバランスしている。ヒロインの中の人(伊藤静)には最初こそ違和感を感じたが、結果的にはハマり役だったと思う。早々に第二期が確定したが、それも当然か。寧ろこれだけ面白いのに何で最初から 24 話で作らなかったのかと言いたい。

インパクトは今期一番。OP は一度は聞くべき。もう初話からから飛ばしまくりで、途中で息切れしないか見てるこっちが心配になる。好き嫌いは分かれそうだが、ネタとして見ておくことを推奨。私はクトゥルーは知らないが、他にもパロディーネタが多く、分からない台詞を調べたりしてなかなか勉強になった :-)。

Sphere のために企画・製作されたらしい。サンライズだし絵も綺麗だが、万人受けは難しいか。私は結構好きだったが、それは私が年をとり過ぎたせいで、彼女たちの過ごす時間に羨望の情を抱いたせいだろう。逆に若い世代にはちっとも面白くない可能性がある。Sphere は初めてまともに聞いたが、OP はかなり良かった。

最近のアニメっぽくない古風な絵柄だが、直ぐにそれ以上のインパクト(涎)で掻き消される。絵にはそのうち慣れる、というか色気さえ感じるようになる。涎に限らずヒロインは変態だが、それを受け入れられれば後は普通のうぶな恋愛物語。というかこの作品から変態を取ったら何も残らないので、この作品に限っては変態は正義。

一部で前評判が高いので何かと思ったら、何か原作者がそこそこ有名らしい。まずパッと見で全く期待してなかったが、世界観はなかなか独特。ただ全体を通して「ヒロインにイイ格好させたい」臭が鼻に付く。難題が発生しても結局ヒロインが一人で解決してしまうので、逆につまらなく感じる。もっと年相応にデキない子でいいのに。

サイドストーリーにしては、色々と前作に依存し過ぎているような気がする。逆に前作を見ていれば、色々とニヤニヤしながら楽しめる。ただ問題は、余りに短い。なぜ最近のアニメは続きをやるの分かってるのに 12 話で作るのか。どんな理由があったとしても、試合の途中で話を切って良いはずが無い。

前作は特に面白いとは思わなかったのだが、第二期が始まってしまった。相変わらずマイペースでのっぺりした印象。前作でも思ったが、これは男女どちらをターゲットにしているのだろう。掲載誌から考えると男だが、男子が見るには退屈すぎる。どちらかというと女子に受けそうな内容のような気がする。

元ネタを知らないのでてっきり冒険ファンタジーかと思ったら、ずっとパンばっかり焼いててビックリした。一応最後の方でバトルがあるが、唐突に始まってアッという間に終わるのでまたビックリ。最後はまたパンを焼いてハッピーエンド。って何これ。これ見て誰が喜ぶのだろうか。唯一、斉藤千和(猫娘)の「にゃー」が素晴らしかった。

2012-07-29

RHEL6.3: rsyslog no longer supports `reload'

自作インストーラーが RHEL6.3 から動かなくなった。調べると、rsyslog reload が失敗している。

[rhel63]# service rsyslog reload ; echo $?
3

上記には、「rsyslog は reload をサポートしなくなったから代わりに restart を使ってね」とあるが、それだけ言われても釈然としない。しかし実は、以前から身近なところに答えはあった。

[rhel62]# man rsyslogd

SIGNALS
       <...snip...>

       HUP    This  lets rsyslogd perform close all open files.  Also, in v3 a
              full restart will be done in order to read changed configuration
              files.   Note  that  this means a full rsyslogd restart is done.
              This has, among others, the consequence that TCP and other  con-
              nections  are  torn down. Also, if any queues are not running in
              disk assisted mode or are not set to persist data  on  shutdown,
              queue  data  is  lost. HUPing rsyslogd is an extremely expensive
              operation and should only be done when actually necessary. Actu-
              ally,  it  is a rsyslgod stop immediately followed by a restart.
              Future versions will remove this restart  functionality  of  HUP
              (it  will  go  away in v5). So it is advised to use HUP only for
              closing files, and a  "real  restart"  (e.g.  /etc/rc.d/rsyslogd
              restart) to activate configuration changes.

何か未来を予見してたような manpage 出た。「HUP で設定を再読み込みする機能は v5 から無くなるんだからね!」とのこと。どうやらこれは今更の話ではなく、rsyslog かねてよりの固い意志だったらしい。HUP 時に設定を再読み込みする $HUPisRestart という設定項目も、v5 になって消えたようだ。

The $HUPisRestart directive is supported by some early v5 versions, but has been removed in 5.1.3 and above.

ちなみに RHEL6.2 → RHEL6.3 でパッケージバージョンを比べると、

[rhel62]# rpm -q rsyslog
rsyslog-4.6.2-12.el6.x86_64
[rhel63]# rpm -q rsyslog
rsyslog-5.8.10-2.el6.x86_64

同じ RHEL6 中でパッケージのメジャーバージョンが上がるのって反則じゃね? 全ての元凶はここにある気がするのだが。

ともかく、ここに至ってようやく前述の Bugzilla での議論が理解できる。

reload: reload the configuration of the service without actually stopping and restarting the service (if the service does not support this, do nothing)

reload の定義は、「サービスを再起動することなく設定を再読み込みする」となっている。つまり、もはや rsyslog v5 の initscript には reload を実装する術がない。reload をサポートしなくなったというより、サポートできなくなったのだ。だから reload は未実装となり、これまでの流れを考えると、恐らく今後も復活することはないだろう。

結論として、rsyslog に関しては(どんなに不満でも)以下を受け入れるしかない。

  • ファイルハンドルを閉じたいなら HUP シグナル。(kill -HUP <PID>)
  • 設定を再読み込みさせたいなら再起動。(service rsyslog restart)

2012-07-24

Emacs 24: Gnus: `smtpmail-auth-credentials' was removed

いつの間にか Emacs 24 がリリースされていたので、好奇心で使い始めた。Emacs 23 での設定をそのまま引き継いだが、問題なく動く。楽勝じゃん! て調子で仕事してたら、その日の終わりにメールが送れなくなってることに気が付いた。orz

具体的には、Gnus でメール送信時に「SMTP user name for <HOSTNAME>:」って聞かれる。何でこんな基本的なところが動かなくなるんだよ、と恨めしく思いながら調べると、これだ。

** SMTPmail

*** SMTPmail now uses encrypted connections (via STARTTLS) by default
if the mail server supports them.  This uses either built-in GnuTLS
support, or the starttls.el library.  Customize `smtpmail-stream-type'
to change this.

*** The variable `smtpmail-auth-credentials' has been removed.
By default, the information is now stored in the file ~/.authinfo.
This was the default value of smtpmail-auth-credentials.  If you had
customized smtpmail-auth-credentials to a list of user names and
passwords, those settings are not used.  During your first connection
to the smtp server, Emacs will prompt for the user name and password,
and offer to save them to ~/.authinfo.  Or you can manually copy the
credentials to ~/.authinfo.  For example, if you had

  (setq smtpmail-auth-credentials
        '(("mail.example.org" 25 "jim" "s!cret")))

then the equivalent line in ~/.authinfo would be

  machine mail.example.org port 25 login jim password s!cret

See the auth-source manual for more information, e.g. on encrypting
the credentials file.

*** The variable `smtpmail-starttls-credentials' has been removed.
If you had that set, you need to put

  machine smtp.whatever.foo port 25 key "~/.my_smtp_tls.key" cert "~/.my_smtp_tls.cert"

in your ~/.authinfo file instead.

smtpmail-{auth,starttls}-credentials は無くなったので代わりに ~/.authinfo を使え、と。しかもデフォルトで TLS にまで対応してくれちゃうらしい。TLS は使ってないので知らないが、~/.authinfo の方は作ったらサクッと動いた。

しかしこれ、みんな知ってるんかな? 検索しても目ぼしいものが出てこないけど。まあ、Gnus 使ってる人はこれくらい自己解決してるのが当たり前、ということなんだろう。

2012-07-22

Upstart + runit: stop: Job failed while stopping

Upstart で管理している runit を終了させると、エラーが発生。

# stop runsvdir ; echo $?
stop: Job failed while stopping
1

/var/log/messages には、

Jul 17 12:13:38 rhel6 init: runsvdir post-stop process (3686) terminated with status 1

どうやら post-stop 処理がエラーになった模様。runsvdir の Upstart 設定は次の通り。

/etc/init/runsvdir.conf:

# for runit - manage /sbin/runsvdir-start
start on runlevel [2345]
stop on runlevel [016]
respawn
exec /sbin/runsvdir-start
post-stop exec pkill -x runsv

実際、pkill はプロセスが見つからない場合に 1 を返す。どうせここで pkill が失敗したところでどうしようもないし、post-stop を次のように変更して対処した。

post-stop script
	pkill -x runsv || true
end script

2012-06-27

Slony-I: duplicate key value violates unique constraint "sl_nodelock-pkey", part 2

Slony-I (slon)が起動しなくなることがある。ログには、

2012-05-16 14:08:46.60711 FATAL  localListenThread: "select "_slony1".cleanupNodelock(); insert into "_slony1".sl_nodelock values (    1, 0, "pg_catalog".pg_backend_pid()); " - ERROR:  duplicate key value violates unique constraint "sl_nodelock-pkey"

こうなった時の復旧方法は、以前のエントリを参照。

以前から年に 1 回くらい起こっていて、サーバー再起動で復旧した後はパッタリ起こらなくなるため、そのうち(Slony-I 側で)直るだろうと気楽に考えていた。しかしいくら待っても一向に直る気配がなく、もういい加減に我慢ならなくなったので、ようやく重い腰を上げて調べることにした。

結果から先に言うと、Apache httpd, PostgreSQL での already running 現象と原因は同じ。これらの詳細については以前のエントリを参照。

Slony-I は、ファイルではなくデータベース(sl_nodelock テーブル)に PID を保存する。そして、slon 起動時にその cleanup 処理を行っている。ソースコードでは次の部分。(Slony-I 2.0.7 以降は使ってないので知らない)

slony1-2.0.6/src/backend/slony1_funcs.sql:

create or replace function @NAMESPACE@.cleanupNodelock ()
returns int4
as $$
declare
    v_row        record;
begin
    for v_row in select nl_nodeid, nl_conncnt, nl_backendpid
            from @NAMESPACE@.sl_nodelock
            for update
    loop
        if @NAMESPACE@.killBackend(v_row.nl_backendpid, 'NULL') < 0 then
            raise notice 'Slony-I: cleanup stale sl_nodelock entry for pid=%',
                    v_row.nl_backendpid;
            delete from @NAMESPACE@.sl_nodelock where
                    nl_nodeid = v_row.nl_nodeid and
                    nl_conncnt = v_row.nl_conncnt;
        end if;
    end loop;

    return 0;
end;
$$ language plpgsql;

killBackend(v_row.nl_backendpid, 'NULL') の結果が負になれば、行が delete される。killBackend の実装は C で書かれていて、

slony1-2.0.6/src/backend/slony1_funcs.c:

Datum
_Slony_I_killBackend(PG_FUNCTION_ARGS)
{
    int32        pid;
    int32        signo;
    text       *signame;

    if (!superuser())
        elog(ERROR, "Slony-I: insufficient privilege for killBackend");

    pid = PG_GETARG_INT32(0);
    signame = PG_GETARG_TEXT_P(1);

    if (VARSIZE(signame) == VARHDRSZ + 4 &&
        memcmp(VARDATA(signame), "NULL", 0) == 0)
    {
        signo = 0;
    }
    else if (VARSIZE(signame) == VARHDRSZ + 4 &&
             memcmp(VARDATA(signame), "TERM", 0) == 0)
    {
        signo = SIGTERM;
    }
    else
    {
        signo = 0;
        elog(ERROR, "Slony-I: unsupported signal");
    }

    if (kill(pid, signo) < 0)
        PG_RETURN_INT32(-1);

    PG_RETURN_INT32(0);
}

第 2 引数が "NULL" だと kill -0 が実行される。よってこの関数の返値は、プロセスが存在すれば 0、存在しなければ -1 となる。そのプロセスが slon であるかは関係ない。その結果、プロセスが存在すると sl_nodelock に行が残り、その後 slon が自身の PID を insert する際に duplicate error が発生することになる。

これは Apache httpd と同じロジックだ。しかし、Slony-I は httpd よりも 重大な問題 を抱えている。

  • slon は、例え正常終了であっても、終了時にロックを削除しない。
  • ロックはデータベース内にあるため、サーバー再起動では決して削除されない。

この合わせ技はかなり凶悪だ。想像してみよう。サーバーを再起動したとする。slon は正常終了するがロックは残ったままだ。次回 slon 起動時、ロックに残っている PID を持つプロセスが 存在しなければ slon は起動する。

怖すぎる。いくらサーバー起動時のプロセス起動順は滅多に変わらないと言っても、これでは「起動する方が運が良い」と言っていい。今まで知らずに slon を普通に起動していたかと思うと、背筋が寒くなる。

まさか Slony-I がこんなお粗末な二重起動チェックを行っているとは思わなかった。信じてたのに。せめてエラーログがもっと分かり易ければ早くに気付けたが、duplicate error でそれに気付けというのは難度が高過ぎだろう。

ということで、やはり起動スクリプトでロックを削除することになる。ロックの削除は、例えばこんな感じ。

DBHOST=localhost
DBUSER=postgres
DBNAME=testdb
CLUSTER=slony1

cleanup_local_nodelock() {
    echo 'cleanup sl_nodelock entry for local node'
    psql -h $DBHOST -U $DBUSER -d $DBNAME <<EOF
DELETE FROM _$CLUSTER.sl_nodelock
 WHERE nl_nodeid = (SELECT _$CLUSTER.getLocalNodeId('_$CLUSTER'))
   AND nl_conncnt = 0;
EOF
}

当然、ロックを削除する前には自力で二重起動をチェックする必要がある。この辺は起動スクリプトの内容にも依存するため、各自で頑張って実装して欲しい。私の知る限り Slony-I には 2 つの起動スクリプトが同梱(redhat/slony1.init, tools/start_slon.sh)されているし、起動スクリプトを自作していることも多いだろう。私の場合は、例によって runit なので(略)。

さて、今回の一連(Apache httpd, PostgreSQL, Slony-I)から得られた教訓は、

標準の二重起動チェックを信用するな。

PID ファイルを使って二重起動チェックを行うものは多いが、もし重要なサービスがあるなら、存在するプロセスの PID で PID ファイルを偽造してみて、サービスが起動するかどうか確認してみることをお勧めする。

2012-06-15

PostgreSQL: lock file "postmaster.pid" already exists

PostgreSQL が起動しなくなることがある。ログには、

2012-06-15 09:52:59 GMT  [6133]: FATAL:  lock file "postmaster.pid" already exists
2012-06-15 09:52:59 GMT  [6133]: HINT:  Is another postmaster (PID 2225) running in data directory "/var/lib/pgsql/data"?

原因は、Apache httpd の時と同じ。

ソースコードでは次の部分。(PostgreSQL 9.0 以降は使ってないので知らない)

postgresql-8.4.11/src/backend/utils/init/miscinit.c:

static void
CreateLockFile(const char *filename, bool amPostmaster,
               bool isDDLock, const char *refName)
{

    /* ...snip... */

        /*
         * Check to see if the other process still exists
         *
         * If the PID in the lockfile is our own PID or our parent's PID, then
         * the file must be stale (probably left over from a previous system
         * boot cycle).  We need this test because of the likelihood that a
         * reboot will assign exactly the same PID as we had in the previous
         * reboot.    Also, if there is just one more process launch in this
         * reboot than in the previous one, the lockfile might mention our
         * parent's PID.  We can reject that since we'd never be launched
         * directly by a competing postmaster.    We can't detect grandparent
         * processes unfortunately, but if the init script is written
         * carefully then all but the immediate parent shell will be
         * root-owned processes and so the kill test will fail with EPERM.
         *
         * We can treat the EPERM-error case as okay because that error
         * implies that the existing process has a different userid than we
         * do, which means it cannot be a competing postmaster.  A postmaster
         * cannot successfully attach to a data directory owned by a userid
         * other than its own.    (This is now checked directly in
         * checkDataDir(), but has been true for a long time because of the
         * restriction that the data directory isn't group- or
         * world-accessible.)  Also, since we create the lockfiles mode 600,
         * we'd have failed above if the lockfile belonged to another userid
         * --- which means that whatever process kill() is reporting about
         * isn't the one that made the lockfile.  (NOTE: this last
         * consideration is the only one that keeps us from blowing away a
         * Unix socket file belonging to an instance of Postgres being run by
         * someone else, at least on machines where /tmp hasn't got a
         * stickybit.)
         *
         * Windows hasn't got getppid(), but doesn't need it since it's not
         * using real kill() either...
         *
         * Normally kill() will fail with ESRCH if the given PID doesn't
         * exist.
         */
        if (other_pid != my_pid
#ifndef WIN32
            && other_pid != getppid()
#endif
            )
        {
            if (kill(other_pid, 0) == 0 ||
                (errno != ESRCH && errno != EPERM))
            {
                /* lockfile belongs to a live process */
                ereport(FATAL,
                        (errcode(ERRCODE_LOCK_FILE_EXISTS),
                         errmsg("lock file \"%s\" already exists",
                                filename),
                         isDDLock ?
                         (encoded_pid < 0 ?
                          errhint("Is another postgres (PID %d) running in data directory \"%s\"?",
                                  (int) other_pid, refName) :
                          errhint("Is another postmaster (PID %d) running in data directory \"%s\"?",
                                  (int) other_pid, refName)) :
                         (encoded_pid < 0 ?
                          errhint("Is another postgres (PID %d) using socket file \"%s\"?",
                                  (int) other_pid, refName) :
                          errhint("Is another postmaster (PID %d) using socket file \"%s\"?",
                                  (int) other_pid, refName))));
            }
        }

ちょっと長いが、全てはコメント部に書いてある。やはり PID ファイルにあるプロセスの存在をチェックしているが、httpd との違いはプロセスの所有者まで見ていること。プロセスの所有者が異なれば、二重起動とは見なさない。

PostgreSQL が postgres ユーザーで起動するようになっている場合、postgres ユーザーで起動するプロセスは基本的に PostgreSQL しかないはず。よって PostgreSQL を複数起動しているのでもない限り、この現象に遭遇することはまずない。私の場合、PostgreSQL を起動している runit の logger プロセス(svlogd)を postgres ユーザーで起動しており、運良く:-)この現象に遭遇した。

PostgreSQL を複数起動させたり、postgres ユーザーで他にプロセスを起動しているのであれば、httpd の時と同じく起動スクリプトで PID ファイルを削除した方が良いだろう。

その場合、やはり二重起動チェックを自力で行うことになるが、pg_ctl status はその用途には役に立たない(たぶん上記のコードを通るのだと思われる)。その代わり PostgreSQL は $PGDATA が一意となるので、プロセスのコマンドラインや環境変数を見る(/proc/<PID>/{cmdline,environ})と良いかも知れない。

例によって私は runit を使っていて二重起動チェックとは無縁なので、実際に動くコードは載せられない。

2012-06-11

Apache: httpd (pid ####) already running

Apache httpd が起動しなくなることがある。

# /usr/local/apache2/bin/httpd -k start
httpd (pid 2369) already running

大抵はサーバー再起動で復旧するが、運が悪いと何度再起動しても復旧しなくなる。

ソースコードでは次の部分。(httpd 2.4 は使ってないので知らない)

httpd-2.2.22/server/mpm_common.c:

int ap_signal_server(int *exit_status, apr_pool_t *pconf)
{
    apr_status_t rv;
    pid_t otherpid;
    int running = 0;
    const char *status;

    *exit_status = 0;

    rv = ap_read_pid(pconf, ap_pid_fname, &otherpid);
    if (rv != APR_SUCCESS) {
        if (rv != APR_ENOENT) {
            ap_log_error(APLOG_MARK, APLOG_STARTUP, rv, NULL,
                         "Error retrieving pid file %s", ap_pid_fname);
            ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
                         "Remove it before continuing if it is corrupted.");
            *exit_status = 1;
            return 1;
        }
        status = "httpd (no pid file) not running";
    }
    else {
        if (kill(otherpid, 0) == 0) {
            running = 1;
            status = apr_psprintf(pconf,
                                  "httpd (pid %" APR_PID_T_FMT ") already "
                                  "running", otherpid);
        }
        else {
            status = apr_psprintf(pconf,
                                  "httpd (pid %" APR_PID_T_FMT "?) not running",
                                  otherpid);
        }
    }

PID ファイルに書かれている PID へ kill -0 が成功すれば、つまりプロセスが存在すれば、already running となる。そのプロセスが httpd であるかは関係ない。通常、PID ファイルは httpd 停止時に削除されるが、SIGKILL やサーバーリセットなどで httpd が死ぬと、PID ファイルは残ったままになる。

httpd をソースからビルドしている場合、PID ファイルの場所を /var/run 下などに変更していない限り、サーバーを再起動しても PID ファイルは消えない。故に、運が悪いと何度サーバーを再起動しても復旧しなくなる。サーバー起動時のプロセス起動順なんて、そうそう変わらないからだ。(もっとも、Upstart や Systemd で殆んどのプロセスが並列起動するようになっていれば、この限りではないかも知れない)

要は PID ファイルを消しさえすれば復旧するのだが、システムが客先などにあってコマンドを叩ける人がいない場合、サーバー再起動で復旧しないのは致命的だ。

これがビルドではなくパッケージインストールした httpd の場合だと現象が違って、例えば RHEL6 だと、

# service httpd start
Starting httpd:

起動スクリプト内で処理が止められ、already running すら出ずに起動に失敗する。但し、この場合はサーバーを再起動すれば確実に復旧する。パッケージインストールした httpd の PID ファイルは、例えば RHEL6 なら /var/run/httpd/httpd.pid になるが、/var/run 下はサーバー起動時に必ず削除されるからだ。

最悪はサーバー再起動で復旧しさえすれば何とかなる。しかし欲を言えば、httpd 再起動だけで復旧して欲しい。つまり、httpd の起動前に PID ファイルを削除すれば良い。具体的には、httpd の起動スクリプトでゴニョゴニョすることになるだろう。

気を付けることは、この挙動(already running)自体が二重起動を防止する機構なので、PID ファイルを消す前に自力で二重起動をチェックする必要がある。RHEL なら /etc/init.d/function 中の status 関数、更に言えばその中の __pids_pidof 関数が使えそうに見えるが、httpd なら単純にポート 80 が LISTEN されているか調べる方が簡単かも知れない。但し、いずれの場合も実際に私はやったことがないので、動くコードは載せられない。

私自身は httpd 起動に runit を使っており、二重起動のチェックはそれこそ runit の仕事。runit が起動スクリプト(run)を実行する時点で二重起動でないことが保証されるので、単に起動スクリプトの先頭で rm -f <pidfile> すれば良いだけ。今まで runit には散々苦労させられてきたから、こんな時くらい楽させて貰わないと。:-)

2012-06-01

Apache httpd: not `reload' but `graceful' when logrotate

RHEL で Apache httpd パッケージをインストールすると、logrotate 設定は次のようになる。

RHEL6.2: /etc/logrotate.d/httpd:

/var/log/httpd/*log {
    missingok
    notifempty
    sharedscripts
    delaycompress
    postrotate
        /sbin/service httpd reload > /dev/null 2>/dev/null || true
    endscript
}

これで負荷試験を行うと、logrotate 時に接続が切れてクライアント側でエラーになる。httpd 起動スクリプトの実装を見てみると、

RHEL6.2: /etc/init.d/httpd:

reload() {
    echo -n $"Reloading $prog: "
    if ! LANG=$HTTPD_LANG $httpd $OPTIONS -t >&/dev/null; then
        RETVAL=6
        echo $"not reloading due to configuration syntax error"
        failure $"not reloading $httpd due to configuration syntax error"
    else
        # Force LSB behaviour from killproc
        LSB=1 killproc -p ${pidfile} $httpd -HUP
        RETVAL=$?
        if [ $RETVAL -eq 7 ]; then
            failure $"httpd shutdown"
        fi
    fi
    echo
}

普通に HUP を送っている。HUP を受けると httpd は即座に子プロセスを再起動するので、クライアントの接続が切れるのは当たり前。少なくとも、logrotate 時は graceful reload するべきだろう。

    postrotate
        /sbin/service httpd graceful > /dev/null 2>/dev/null || true
    endscript

参考までに Ubuntu 12.04 を見てみると、RHEL と同様に logrotate 時に reload しているが、起動スクリプト側の reload の実装が graceful reload するようになっているので問題ない。

RHEL と言えども過信は禁物。

2012-05-14

RHEL6: runit with Upstart

久しぶりにサーバーを再起動すると、

これはひどい。サーバーが故、滅多に再起動しないから気が付かなかった。umount できないと文句を言われたパーティションは、runit で起動したプロセスが使用している。なので runit が原因に違いない。

runit の Upstart 用設定(/etc/init/runsvdir)は、runit 公式サイトからコピペしたものを使っている。

# for runit - manage /sbin/runsvdir-start
start on runlevel [2345]
stop on shutdown
respawn
exec /sbin/runsvdir-start

まず考えるべきは、runsvdir は SIGTREM を受けたとき、子プロセス(runsv)をどうするのか? とはいえさっきのエラーを見れば想像はつく。

If runsvdir receives a TERM signal, it exits with 0 immediately. If runsvdir receives a HUP signal, it sends a TERM signal to each runsv(8) process it is monitoring and then exits with 111.

runsvdir は SIGTERM を受けると自身が終了するだけで、子プロセスはそのまま残ってしまう。これが冒頭のエラーの原因だろう。子プロセスも終了させるには、SIGHUP を使う必要がある。では Upstart に SIGHUP を送らせるにはどうするか。

まあ予想はしていたが、はい動きませんでしたー :-p。案の定、RHEL6 の Upstart では未実装の様子。まして start-stop-daemon なんてある訳ない。暫く悩んで、post-stop で子プロセスを終了させることにした。pre-stop だと、終了した傍から runsvdir が復活させてしまう可能性がある。

# for runit - manage /sbin/runsvdir-start
start on runlevel [2345]
stop on shutdown
respawn
exec /sbin/runsvdir-start
post-stop script
	pkill -x runsv || true
end script

ううむ、まだ変だ。子プロセスは終了できたみたいだが、そもそも runsvdir が respawn される状況がおかしい。

と、ここまで来てようやく事の本質に気が付いた。何も疑わず runit 公式サイトからコピペしてきたが、そもそも「shutdown」などというイベントは存在するのか? 結論から言うと、公式サイトが嘘。素直に Upstart cookbook に従うべし。

# for runit - manage /sbin/runsvdir-start
start on runlevel [2345]
stop on runlevel [016]
respawn
exec /sbin/runsvdir-start
post-stop script
	pkill -x runsv || true
end script

うん、満足。:-)

2012-05-06

RHEL6: Samba is slow to open

RHEL6 のファイルサーバーが遅い。具体的には、ファイルを開くのに 5 秒かかる。RHEL5 の方は遅くないので、RHEL6 で何かが変わったに違いない。smb.conf のパラメーターを見ても見当がつかないので、debug ログを出してみる。

[2012/03/28 14:25:03.863549,  3] libsmb/ntlmssp.c:65(debug_ntlmssp_flags)
  Got NTLMSSP neg_flags=0xa2088207
    NTLMSSP_NEGOTIATE_UNICODE
    NTLMSSP_NEGOTIATE_OEM
    NTLMSSP_REQUEST_TARGET
    NTLMSSP_NEGOTIATE_NTLM
    NTLMSSP_NEGOTIATE_ALWAYS_SIGN
    NTLMSSP_NEGOTIATE_NTLM2
    NTLMSSP_NEGOTIATE_VERSION
    NTLMSSP_NEGOTIATE_128
    NTLMSSP_NEGOTIATE_56
[2012/03/28 14:25:08.871119,  3] ../lib/util/util_net.c:68(interpret_string_addr_internal)
  interpret_string_addr_internal: getaddrinfo failed for name rhel6 [名前またはサービスが不明です]
[2012/03/28 14:25:08.871364,  3] lib/util_sock.c:1805(get_mydnsfullname)
  get_mydnsfullname: getaddrinfo failed for name rhel6 [未知のエラー]

自分のホスト名が解決できていない。何でこんなことに。/etc/hosts を RHEL5 と比較すると、

RHEL5.8: /etc/hosts:

# Do not remove the following line, or various programs
# that require network functionality will fail.
127.0.0.1		rhel5 localhost.localdomain localhost
::1		localhost6.localdomain6 localhost6

RHEL6.2: /etc/hosts:

127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4
::1         localhost localhost.localdomain localhost6 localhost6.localdomain6

両方とも同じようにインストールしたのに、RHEL6 の方には自ホスト名(rhel6)が入っていない。何でこんなことに。

ここでの議論はおおよそ、

  • NetworkManager を起動すると IPA が動かなくなる。
  • NetworkManager が /etc/hosts の ::1 に自ホスト名を追加するのが原因。
  • /etc/hosts の ::1 に自ホスト名があると動かないプログラムが他にもある。
  • なので、/etc/hosts を一切編集しないように NetworkManager を修正した。

という感じか。NetworkManager が /etc/hosts の面倒を見なくなったのなら、OS インストール時に /etc/hosts を適切に設定してくれても良いと思うのだが。普通に OS インストールしても自ホスト名が解決できないというのは、不親切極まりないと思う。

何がどうであるべきかは私には分からない。他に何か良い方法があるのかも知れないが、「動かないシステムに価値はない」の持論に従い、動くように /etc/hosts を編集する。

/etc/hosts:

127.0.0.1   rhel6 localhost localhost.localdomain localhost4 localhost4.localdomain4
::1         localhost localhost.localdomain localhost6 localhost6.localdomain6

127.0.0.1 ではなくサーバーの IP アドレスで登録すべき、と言われそうだが、それでは DHCP 環境で使えない。固定 IP アドレス環境でも、永遠に IP アドレスを変えないと保証できるはずもない。その際に IP アドレスを変更する SE が、/etc/hosts に気を回すスキルを持っていると期待するほど、私はお人好しじゃない。

そういえば Emacs の起動が遅かったのもこれが原因か。RHEL6 になって、ネットワーク周りは本当に面倒になったと思う。というか、未だにどうするのが RHEL6 的に「正しい」ことなのか良く分からん。


2017-05-03 追記

今更の情報だが、RHEL5 においても、RHEL5.9 から /etc/hosts に自ホスト名が入らなくなった。

2012-04-30

Home server via plala's network

外から自宅サーバーの Bazaar リポジトリに push しようとすると、

Run command: bzr push
Connected (version 2.0, client OpenSSH_5.3p1)
Using saved push location: sftp://piyolian@example.com:10022/~/repo
Authentication (password) successful!
Secsh channel 1 opened.
[chan 1] Opened sftp connection (server version 3)
Traceback (most recent call last):
  File "logging\__init__.pyo", line 799, in emit
UnicodeDecodeError: 'ascii' codec can't decode byte 0x8a in position 18: ordinal not in range(128)
bzr: ERROR (ignored): 
bzr: ERROR: paramiko.SSHException: Server connection dropped: 

Traceback (most recent call last):
  File "bzrlib\commands.pyo", line 927, in exception_to_return_code
<...snip...>
  File "bzrlib\btree_index.pyo", line 1547, in _read_nodes
  File "bzrlib\transport\__init__.pyo", line 659, in readv
  File "bzrlib\transport\sftp.pyo", line 466, in _readv
  File "bzrlib\transport\sftp.pyo", line 728, in _translate_io_exception
SSHException: Server connection dropped: 

bzr 2.3.4 on python 2.6.6 (Windows-XP-5.1.2600-SP3)
arguments: ['C:\\Program Files\\Bazaar\\bzr.exe', 'qsubprocess', '--bencode',
    'l4:pushe']
plugins: bzrtools[2.3.1], colo[0.2.1], explorer[1.1.4], fastimport[0.10.0],
    launchpad[2.3.4], loom[2.2.1dev], netrc_credential_store[2.3.4],
    news_merge[2.3.4], pipeline[1.1.0], qbzr[0.20.1], rewrite[0.6.2dev],
    svn[1.0.5dev], upload[1.0.0], xmloutput[0.8.7.dev]
encoding: 'cp932', fsenc: 'mbcs', lang: None

*** Bazaar has encountered an internal error.  This probably indicates a
    bug in Bazaar.  You can help us fix it by filing a bug report at
        https://bugs.launchpad.net/bzr/+filebug
    including this traceback and a description of the problem.

何か変なエラー出た。何度やっても結果は同じ。pull の方は動く。更に変なことに、調査のために SSH で自宅サーバーに繋ぐと、1 分そこらで接続が切れてしまう。何度やっても結果は同じ。

暫く悩んでいると、思い出した。

少し前に、プロバイダーを切り替えた。今まで「玄人っぽい」という理由だけで *****.com だったが、最近になってやたらネットがブチブチ切れるようになった(気がする)ので頭に来た。プロバイダーなのか ADSL 回線なのか切り分けるため、まずはプロバイダーを plala に変えてみた。効果がなかったとしても、今より安くなるので問題ない。NTT 料金と合算できるのも良い。

試しに(検証用に残しておいた) *****.com に戻してみると、push できるようになった。SSH も、少なくとも数分で切れるということはない。もう plala 何してくれちゃってんの? それとも最近の大手プロバイダーってみんなこうなの? とか思いながら速攻 plala を解約しようと思ったら、ちょっと待った。

plala はデフォルトでフィルターが掛かっていて、会員サイトでその設定を変更できる、と。

早速「パケットフィルタレベル」を「0 (OFF)」にしたら、全ての問題が解決した。

結論: plala で自宅サーバーを立てるなら、「ネットバリア」の「パケットフィルタ」を切れ。

ちなみにネットが切れる件は、・・・あんまり変わらんかも。

2012-04-24

RHEL6: Disable screen saver on console

コンソールのスクリーンセーバーを切りたい。つまり画面がブラックアウトしないようにしたい。RHEL5 では、/etc/rc.d/rc.local に次の 1 行を入れていた。

setterm -blank 0 -powerdown 0

しかし RHEL6 では動かない。恐らく Upstart になったことで、rc.local を実行する端末が tty1 ではなくなったのだろう。方法は幾つかあるだろうが、次を参考に、

SystemV 形式の init script を登録することにする。端末を明示的に指定しているので、どの端末から実行しようと確実にコンソールに作用する。

/etc/rc.d/init.d/mytty:

#!/bin/bash
# mytty  This is the init script for tty configuration
#
# chkconfig: 2345 99 01
# description: Configures tty

# Source function library.
. /etc/rc.d/init.d/functions

# Find the name of the script
NAME=$(basename $0)
if [ ${NAME:0:1} = "S" -o ${NAME:0:1} = "K" ] ; then
    NAME=${NAME:3}
fi

start() {
    local errors=0
    echo -n "Starting $NAME: "
    local term
    for term in /dev/tty[1-6] ; do
	[ -e "$term" ] || continue
	setterm -blank 0 -powerdown 0 >$term <$term || let errors+=1
    done
    if [ $errors -eq 0 ] ; then
	success
    else
	failure
    fi
    echo
    return $errors
}

# See how we were called.
case "$1" in
    start) $1 ;;
    *) ! echo "Usage: $0 {start}" ;;
esac

この理屈でいけば、rc.local に次を書くだけでも良いが、

setterm -blank 0 -powerdown 0 >/dev/tty1 </dev/tty1

ファイルを「編集」するよりもファイルを「追加」する方が、私の好み。その方が作業の自動化という面で都合が良い。

「編集」の場合、インストール(追加)は良くてもアンインストール(削除)が難しい。人が手で同種の設定を追加していた場合、それは削除せずにそのまま残すのが望ましい(でないと「勝手に消された」と言われる)が、それを確実に判断する方法がない。「追加」の場合は、追加したファイルを削除するだけで済む。

同様の処理を Upstart (/etc/init)に登録する方法もあるが、わざわざ SystemV (/etc/rc.d/init.d)に登録するのは、RHEL5 でも動くようにするため。


2017-05-09 追記

残念ながら、以上は RHEL7 では動かない。

May 09 16:17:23 rhel7 systemd[1]: Starting SYSV: Configures tty...
May 09 16:17:23 rhel7 mytty[1357]: Starting mytty: setterm: $TERM is not defined.
May 09 16:17:23 rhel7 mytty[1357]: setterm: $TERM is not defined.
May 09 16:17:23 rhel7 mytty[1357]: setterm: $TERM is not defined.
May 09 16:17:23 rhel7 mytty[1357]: setterm: $TERM is not defined.
May 09 16:17:23 rhel7 mytty[1357]: setterm: $TERM is not defined.
May 09 16:17:23 rhel7 mytty[1357]: setterm: $TERM is not defined.
May 09 16:17:23 rhel7 mytty[1357]: [失敗]
May 09 16:17:23 rhel7 systemd[1]: mytty.service: control process exited, code=exited status=6
May 09 16:17:23 rhel7 systemd[1]: Failed to start SYSV: Configures tty.
May 09 16:17:23 rhel7 systemd[1]: Unit mytty.service entered failed state.
May 09 16:17:23 rhel7 systemd[1]: mytty.service failed.

TERM が未定義、と言われて setterm が失敗する。これはオプション -term linux で解決できるが、

May 09 16:25:19 rhel7 systemd[1]: Stopping SYSV: Configures tty...
May 09 16:25:19 rhel7 mytty[3513]: Usage: /etc/rc.d/init.d/mytty {start}
May 09 16:25:19 rhel7 systemd[1]: mytty.service: control process exited, code=exited status=1
May 09 16:25:19 rhel7 systemd[1]: Stopped SYSV: Configures tty.
May 09 16:25:19 rhel7 systemd[1]: Unit mytty.service entered failed state.
May 09 16:25:19 rhel7 systemd[1]: mytty.service failed.

今度はシャットダウン時に未実装の stop を呼ばれて失敗する。なので stop も実装する必要がある。

以下、RHEL7 対応版。

/etc/rc.d/init.d/mytty:

#!/bin/bash
# mytty  This is the init script for tty configuration
#
# chkconfig: 2345 99 01
# description: Configures tty

# Source function library.
. /etc/rc.d/init.d/functions

# Find the name of the script
NAME=$(basename $0)
if [ ${NAME:0:1} = "S" -o ${NAME:0:1} = "K" ] ; then
    NAME=${NAME:3}
fi

start() {
    local errors=0
    echo -n "Starting $NAME: "
    local term
    for term in /dev/tty[1-6] ; do
	[ -e "$term" ] || continue
	setterm -term linux -blank 0 -powerdown 0 >$term <$term \
	    || let errors+=1
    done
    if [ $errors -eq 0 ] ; then
	success
    else
	failure
    fi
    echo
    return $errors
}

# See how we were called.
case "$1" in
    start) $1 ;;
    stop) : ;;
    *) ! echo "Usage: $0 {start|stop}" ;;
esac

2012-04-18

Animes in the 1st quarter of 2012

アニメは IT エンジニアの必須科目です。 ということで簡単なレビューを。

3 年目突入の頃から見ている。アニメから入って原作も読むようになったが、テンポはアニメの方が良いと思う。取りあえず一旦ここで終了ということだが、変に延命してどっかの人気アニメみたくテンポが台無しになるよりはマシ。今まで散々笑わせてもらったので、大いに満足。またいつかの復活を期待したい。

前作は私が「シャフト」を知ることになった名作。ノリはそのままなので、前作を楽しめるキャパがあるなら問題なし。しかしボリューム不足の感は否めない。話数(24 -> 12)以上に、何か足りない。前作キャラの濃さに比べると、シスターズはイマイチだった感がある。あと OP/ED も期待はずれ。尤も前作は ED が傑作過ぎたので、比べるのは酷か。

第 2 期以外は見てるが、だんだんつまらなくなってきた。初期は心温まる話が多かったが、最近は暗めのが多くないだろうか。そろそろ主人公のコンプレックスがウザい。そう何度も幼少の暗い話なんか聞きたくないっつーの。少なくとも最近のは子供に見せたいような作品じゃなくなった。まあシリーズが続く限りは見続けるとは思う。

「少女マンガ原作のアニメにハズレ無し」のジンクス通り、今期一番のアタリ。努力の正しい方向性を描いている(同種には「capeta」「モンキーターン」等がある)。子供が居るなら是非見せておきたい。ヒロインはその一生懸命さが眩しくて惚れる。中の人(瀬戸麻沙美)は初見だが、キャラに合った絶妙な配役だったと思う。OP も良で、総じて素晴らしい。

前シリーズから引き続いて熱い。なので内容に関しては特に言うことは無し。しかし今シリーズは ED が残念だった。前シリーズは ED が 2 つとも良かっただけに、今作も期待していたのだが。既に第 3 シリーズが決まっているようなので、次回に期待。

手間は掛かってそうだが、何かイマイチ。原因を考えるに、私の萌えポイントに欠けてたんだと思う。真っ先に萌えられそうなアナが男という設定が最大の敗因では。次点はエレナだが、出番が少なすぎて全然足りない。ノノハに萌えるのは、ゴメンちょっと無理だった。既に第 2 期が始まったようだが、たぶん同じ結果になりそうな予感。

何といっても京都アニメーション。OP を見るだけで只者でないセンスを感じる。しかし今回は分が悪かったか。もしくは原作(見たことないけど)のアレンジの方向性を間違えた気がする。正直、ノリがウザかった。個人的には博士・ナノ・坂本が居れば十分、他キャラの必要性を余り感じなかった。

こっちの「日常」はサンライズ。しかしこちらも役不足。わざわざ大御所が作らなくても、スクエニが自前で作った方がコスト的にも良かったんじゃないのか、と思う。有名声優は多数出ているが、キャラが声優に負けている気がするんだが。中身もおおよそつまらなかったが、文学少女とリンゴちゃんは良かった。

何かヒロインの声が好きなんだわ。えーと、石原夏織か。覚えた。前述の瀬戸麻沙美と出所が同じらしいが、演技は要練習か。話の内容は、特に盛り上がりもなく終わった。素材は良いのに、活かしきれていない感がする。あと、マルとかワンとかカシコマリーとか、推すんだったらもう少し押さないと。マルとワンは絶対に流行らんと思うけど。

不勉強ながら前作は未見。第一話で映像に魅せられた。これだけで見る価値はあった。残念ながら、話はその初回がピークで後は尻すぼみ。最後には、敵の大将は一体何がしたかったんだ、と。前作を見た輩は概ね酷評している様子だが、同意せざるを得ない。きっと前作の方がずっと面白かったに違いない。機会があれば前作を見てみたい。

集英社の圧倒的メディアミックス戦略により、漫画版は至る所で読んだ。原作は未読。何が集英社をそこまで駆り立てるのか謎。個人的に次女の中の人(喜多村英梨)に期待したが、不発。話も特に面白い訳でもない。が、これだけは認めねばなるまい。ヒナたんが可愛い・・・。ベタな狙いにまんまとハマりやがって、という侮蔑は甘んじて受けよう。

とある女子が妄想した、戦国時代を都合よく改変して自分好みのキャラと声優をいっぱい出してみました、的な。ノリも如何にも女子が好みそうで、書いた人はさぞかしハァハァしたかも知れないが、男子が見ても面白くない。個人的に、主役二人の掛け合いを受け付けない時点で終わった。

画質がいかにも J.C.STAFF。正直期待してなかったが、思ったよりは良かった。とはいえ、話から恋愛を取ったら宇宙人くらいしか残らないので、甘酸っぱい話にキュンキュンする年頃でもない私には大して効果が無い。ただ、ここでも石原夏織が登場。彼女の声にはキュンキュンする。ラグランジェよりもキャラと合っていて良い。あと、OP/ED がどちらも秀逸。

普段ホラーは見ないので、怖かったらどうしよう、と心配だったが大して怖くなくて安心。私に推理の才能は無いので、最後まで死人は分からなかった。すると困るのは、この手の話は途中に伏線が散りばめられているため、確認でもう一度見ないといけない。まあ女子キャラの絵が好みなので、もう一度見るのは問題ない。

またツンデレ主人公の話か、と思ったら存外に良かった。それぞれのキャラがいい味出してる。何といっても頻繁に出てくるデフォルメ絵が可愛い過ぎる。話の本筋をやる程つまらなくなるが、大体はバカやってるので楽しめた。OP も良。個人的に、題名にある狐キャラが一番要らなかった。

2012-04-08

SELinux: file redirection is denied

logwatch のデフォルト設定では、レポートをメールで送信する。しかし不要なサービスは起動しないのがセオリー、logwatch の為だけにメールサーバーを起動したくない。代わりにレポートを /var/log 下に保存するため、cron に下記のようなスクリプトを登録する。

#!/bin/bash
LOGWATCH=/usr/sbin/logwatch
if [ -x "$LOGWATCH" ] ; then
    mkdir -p /var/log/logwatch
    $LOGWATCH -save /var/log/logwatch/logwatch-$(date +%Y%m%d)
fi

RHEL6 でこれを実行すると、SELinux に引っ掛かる。/var/log/audit/audit.log には、

type=AVC msg=audit(1332785643.501:68869): avc:  denied  { write } for  pid=23193 comm="logwatch" name="logwatch" dev=sda5 ino=258088 scontext=system_u:system_r:logwatch_t:s0-s0:c0.c1023 tcontext=system_u:object_r:var_t:s0 tclass=dir
type=SYSCALL msg=audit(1332785643.501:68869): arch=c000003e syscall=2 success=no exit=-13 a0=3037430 a1=441 a2=1b6 a3=3f5311dba0 items=0 ppid=23178 pid=23193 auid=0 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=(none) ses=7979 comm="logwatch" exe="/usr/bin/perl" subj=system_u:system_r:logwatch_t:s0-s0:c0.c1023 key=(null)

logwatch_t は var_t に書き込む権限がない、と言っている。なのでやり方を変えてみる。

$LOGWATCH -print >>/var/log/logwatch/logwatch-$(date +%Y%m%d)

結果は、

type=AVC msg=audit(1332873421.565:77913): avc:  denied  { append } for  pid=4424 comm="logwatch" path="/var/local/pearl/log/logwatch/logwatch-20120328" dev=sda5 ino=258164 scontext=system_u:system_r:logwatch_t:s0-s0:c0.c1023 tcontext=system_u:object_r:var_t:s0 tclass=file
type=SYSCALL msg=audit(1332873421.565:77913): arch=c000003e syscall=59 success=yes exit=0 a0=17cd9f0 a1=17caea0 a2=17cacf0 a3=18 items=0 ppid=4409 pid=4424 auid=0 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=(none) ses=9452 comm="logwatch" exe="/usr/bin/perl" subj=system_u:system_r:logwatch_t:s0-s0:c0.c1023 key=(null)

じゃどうすりゃいいんだよ、と途方に暮れていると、

記事の内容は logwatch とは関係ないが、試してみる価値はある。

$LOGWATCH -print | cat >>/var/log/logwatch/logwatch-$(date +%Y%m%d)

ビンゴ。:-D

結論: ファイルにリダイレクトする際は、取りあえず cat を通しとけ。

そうすれば、SELinux に呪いの言葉を吐く回数が減る。

2012-04-05

RHEL6: JDK: /lib/ld-linux.so.2: bad ELF interpreter

RHEL6 に JDK をインストールしようとすると、エラーが発生。

[rhel6]# ./jdk-6u31-linux-i586.bin
Unpacking...
Checksumming...
Extracting...
./jdk-6u31-linux-i586.bin: ./install.sfx.10908: /lib/ld-linux.so.2: bad ELF interpreter: No such file or directory
Failed to extract the files.  Please refer to the Troubleshooting section of the Installation Instructions on the download page for more information.

Google 先生によると、ld-linux.so.2 は libstd++ パッケージに入っているらしい。

[rhel6]# rpm -q libstdc++
libstdc++-4.4.6-3.el6.x86_64

既に入ってますが。RHEL5 ではどうだったかというと、

[rhel5]# rpm -q libstdc++
libstdc++-4.1.2-46.el5
libstdc++-4.1.2-46.el5

ああ、分かった。64bit OS に 32bit JDK を入れようとしたからだ。つまり、

  • 64bit RHEL に 32bit JDK を入れる場合、32bit libstdc++ パッケージが必要。
  • 64bit RHEL5 には 32bit libstdc++ が入っているが、64bit RHEL6 には入っていない。

ならば RHEL6 DVD から入れれば解決・・・、

[rhel6]# rpm -ivh libstdc++-4.4.6-3.el6.i686.rpm
warning: libstdc++-4.4.6-3.el6.i686.rpm: Header V3 RSA/SHA256 Signature, key ID fd431d51: NOKEY
error: Failed dependencies:
        ld-linux.so.2 is needed by libstdc++-4.4.6-3.el6.i686
        ld-linux.so.2(GLIBC_2.3) is needed by libstdc++-4.4.6-3.el6.i686
        libc.so.6 is needed by libstdc++-4.4.6-3.el6.i686
        libc.so.6(GLIBC_2.0) is needed by libstdc++-4.4.6-3.el6.i686
        libc.so.6(GLIBC_2.1) is needed by libstdc++-4.4.6-3.el6.i686
        libc.so.6(GLIBC_2.1.3) is needed by libstdc++-4.4.6-3.el6.i686
        libc.so.6(GLIBC_2.2) is needed by libstdc++-4.4.6-3.el6.i686
        libc.so.6(GLIBC_2.3) is needed by libstdc++-4.4.6-3.el6.i686
        libc.so.6(GLIBC_2.3.2) is needed by libstdc++-4.4.6-3.el6.i686
        libc.so.6(GLIBC_2.4) is needed by libstdc++-4.4.6-3.el6.i686
        libgcc_s.so.1 is needed by libstdc++-4.4.6-3.el6.i686
        libgcc_s.so.1(GCC_3.0) is needed by libstdc++-4.4.6-3.el6.i686
        libgcc_s.so.1(GCC_3.3) is needed by libstdc++-4.4.6-3.el6.i686
        libgcc_s.so.1(GCC_4.2.0) is needed by libstdc++-4.4.6-3.el6.i686
        libgcc_s.so.1(GLIBC_2.0) is needed by libstdc++-4.4.6-3.el6.i686
        libm.so.6 is needed by libstdc++-4.4.6-3.el6.i686
        libm.so.6(GLIBC_2.0) is needed by libstdc++-4.4.6-3.el6.i686

依存関係が うぜえ。速攻で諦めて、素直に 64bit JDK を入れることにした。

JDK はサイズが大きいから自作インストールメディアに 32bit 版と 64 bit 版の両方を入れるのが嫌だったが、諦めた。現場 SE が自力で libstdc++ を入れられると期待するほど、私はお人好しじゃない。


2017-05-07 追記

今まで放ったらかしにしておいて申し訳ないが、このエントリは嘘です

Google 先生によると、ld-linux.so.2 は libstd++ パッケージに入っているらしい。

当時のことを思い出せるはずもないが、実際に JDK が動作するシステムで下記のように調べれば、

# cat /etc/redhat-release
Red Hat Enterprise Linux Server release 6.8 (Santiago)

# rpm -q --whatprovides /lib64/ld-linux-x86-64.so.2
glibc-2.12-1.192.el6.x86_64

ld-linux.so.2 は glibc パッケージに入っていることが分かる。glibc であれば、依存関係は(私の環境では) nss-softokn-freebl だけになる。それに OS メディアがあるなら、複雑な依存関係であったとしても、yum に解決してもらえる。

# yum -c /media/media.repo --setopt InstallMedia.baseurl=file:///media install glibc.i686
Setting up Install Process
InstallMedia                                             | 4.1 kB     00:00 ...
InstallMedia/primary_db                                  | 3.1 MB     00:03 ...
Resolving Dependencies
--> Running transaction check
---> Package glibc.i686 0:2.12-1.192.el6 will be installed
--> Processing Dependency: libfreebl3.so for package: glibc-2.12-1.192.el6.i686
--> Processing Dependency: libfreebl3.so(NSSRAWHASH_3.12.3) for package: glibc-2.12-1.192.el6.i686
--> Running transaction check
---> Package nss-softokn-freebl.i686 0:3.14.3-23.el6_7 will be installed
--> Finished Dependency Resolution

Dependencies Resolved

================================================================================
 Package                 Arch      Version              Repository         Size
================================================================================
Installing:
 glibc                   i686      2.12-1.192.el6       InstallMedia      4.4 M
Installing for dependencies:
 nss-softokn-freebl      i686      3.14.3-23.el6_7      InstallMedia      157 k

Transaction Summary
================================================================================
Install       2 Package(s)

Total download size: 4.5 M
Installed size: 14 M
Is this ok [y/N]: y
Downloading Packages:
--------------------------------------------------------------------------------
Total                                           4.7 MB/s | 4.5 MB     00:00
Running rpm_check_debug
Running Transaction Test
Transaction Test Succeeded
Running Transaction
  Installing : glibc-2.12-1.192.el6.i686                                    1/2
  Installing : nss-softokn-freebl-3.14.3-23.el6_7.i686                      2/2
  Verifying  : nss-softokn-freebl-3.14.3-23.el6_7.i686                      1/2
  Verifying  : glibc-2.12-1.192.el6.i686                                    2/2

Installed:
  glibc.i686 0:2.12-1.192.el6

Dependency Installed:
  nss-softokn-freebl.i686 0:3.14.3-23.el6_7

Complete!

2012-04-02

diff RHEL5/samba RHEL6/samba

RHEL6 になって、今まで使っていた smbpasswd のラッパースクリプトが動かなくなった。スクリプト中では smbpasswd ファイルを直接ごにょごにょしているのだが、そのファイルの場所が変わったらしい。

[rhel5]# testparm -sv | grep 'smb passwd file'
Load smb config files from /etc/samba/smb.conf
<...snip...>
Loaded services file OK.
Server role: ROLE_STANDALONE
        smb passwd file = /etc/samba/smbpasswd
[rhel6]# testparm -sv | grep 'smb passwd file'
Load smb config files from /etc/samba/smb.conf
rlimit_max: increasing rlimit_max (1024) to minimum Windows limit (16384)
<...snip...>
Loaded services file OK.
Server role: ROLE_STANDALONE
        smb passwd file = /var/lib/samba/private/smbpasswd

他にも幾つかデフォルト値が変わっている。特に lanman auth や wide links などはハマる人が居るかも知れない。まあ smbpasswd の場所が変わってハマるのは私くらいだろうけどな。

ところで、上では気になるメッセージも出ている。

rlimit_max: increasing rlimit_max (1024) to minimum Windows limit (16384)

これはファイルオープン数の上限を上げれば出なくなるらしい。

Samba の起動ユーザーだけ上限を上げたいところだが、あいにく Samba の起動ユーザーは root なので、root の上限を上げることになる。/etc/init.d/smb を編集(ulimit -n)すれば影響を Samba プロセスだけに限定できるが、システムがインストールするファイルを変更するのは気持ちが良くない。なのでひとまず root の上限を上げることにする。

[rhel6]# echo 'root - nofile 16384' >/etc/security/limits.d/50-samba.conf
[rhel6]# reboot

でもこれ、確かに厳密には 16384 必要なのかも知れないが、実際には絶対に必要ないだろ。多分この 16384 は smb.conf 設定でいう max open files の値だが、クライアントが同時に 16384 個もファイルを開くってどんな状況だよ。

なので取りあえずは問題が出るまで、無視することにした :-p。いやだって、RHEL5 Samba は今までデフォルト値(1024)で問題なく動いてるんだもん。


2017-05-03 追記

今まで放置していて申し訳ないが、このエントリには 重大な嘘 が 2 つある。

まず 1 つ目。

daemon系プロセスのファイルディスクリプタ数上限を設定する際、/etc/security/limits.conf は使えません。

うん、ほんとこれ。当時は Samba を再起動して確認しただけで、サーバー再起動後に改めて確認してなかった。当時の自分を叱ってやりたい。

そして 2 つ目。ソースコードをご覧あれ。

	if (rlimit_max < MIN_OPEN_FILES_WINDOWS) {
		DEBUG(2,("rlimit_max: increasing rlimit_max (%d) to "
			"minimum Windows limit (%d)\n",
			rlimit_max,
			MIN_OPEN_FILES_WINDOWS));
		rlimit_max = MIN_OPEN_FILES_WINDOWS;
	}

つまりこのメッセージは rlimit_max を「上げろ」ではなく、「上げるよ」と教えてくれていた訳だ。なのでこのメッセージを受けて、私たちがやるべきことは何もない。

まあ確かに、increase (命令形)ではなく increasing となっているけれども、誤解されやすいメッセージだと思うんだよなあ。せめて increased だったら気付けたのかも。

2012-03-28

RHEL6: runit: /usr/bin/ld: cannot find -lc

RHEL6 で runit-2.1.1 を make するとエラーが発生。

./compile runit.c
./load runit unix.a byte.a -static
/usr/bin/ld: cannot find -lc
collect2: ld returned 1 exit status
make[1]: *** [runit] Error 1
make[1]: Leaving directory `/package/admin/runit-2.1.1/compile'

さてどうしたもんかと思ったら、先人は偉かった。

その patch。

--- src/Makefile.orig	2009-10-05 05:44:02.000000000 +0900
+++ src/Makefile	2011-12-28 17:21:46.956241394 +0900
@@ -6,10 +6,10 @@
 	./check-local $(IT)
 
 runit: load runit.o unix.a byte.a
-	./load runit unix.a byte.a -static
+	./load runit unix.a byte.a
 
 runit-init: load runit-init.o unix.a byte.a
-	./load runit-init unix.a byte.a -static
+	./load runit-init unix.a byte.a
 
 runsv: load runsv.o unix.a byte.a time.a
 	./load runsv unix.a byte.a time.a

2012-03-14

Bash: builtin command `local' returns status

成功したら何も出さないけど、失敗したらエラー内容を出力して終わりたい、ということがある。

test1.sh:

#!/bin/bash

compute() {
    $((3 / 0))
}

echo -n 'computing ... '
out=$(compute 2>&1)
ret=$?
if [ $ret -eq 0 ] ; then
    echo '[OK]'
else
    echo '[NG]'
    echo "$out"
fi
exit $ret

これは意図通りに動く。

$ bash test1.sh
computing ... [NG]
test1.sh: line 4: 3 / 0: division by 0 (error token is "0")

しかし Bash を使っていて local を使わないなんて あり得ない ので、

test2.sh:

#!/bin/bash

compute() {
    $((3 / 0))
}

main() {
    echo -n 'computing ... '
    local out=$(compute 2>&1)
    local ret=$?
    if [ $ret -eq 0 ] ; then
	echo '[OK]'
    else
	echo '[NG]'
	echo "$out"
    fi
    return $ret
}

main
$ bash test2.sh
computing ... [OK]

はい、動かなくなったー。何で!?

長い間この理由が分からなくて、挙句 Bash のバグじゃねーの?と疑ったこともあったが、ずっと後になって謎が解けた。

local が実行結果を返すなんて知らなかったよ・・・。 orz

local は関数内で使われる限り失敗しないので、「local out=$(...)」の結果は常に真になる。なので正しくはこう。

test3.sh:

#!/bin/bash

compute() {
    $((3 / 0))
}

main() {
    echo -n 'computing ... '
    local out
    out=$(compute 2>&1)
    local ret=$?
    if [ $ret -eq 0 ] ; then
	echo '[OK]'
    else
	echo '[NG]'
	echo "$out"
    fi
    return $ret
}

main
$ bash test3.sh
computing ... [NG]
test3.sh: line 4: 3 / 0: division by 0 (error token is "0")

もう Bash さん、疑ったりして本当にすみません。

2012-02-27

Slony-I 2.0.7: EXECUTE SCRIPT is terribly slow

あるとき PostgreSQL が死ぬほど遅くなった。思えば PostgreSQL も Slony-I もアップグレードしたし、 VM の CPU 割り当て個数も変えた。そう言えばアンチウィルスもアップグレードした。もう心当たりがあり過ぎて、何が原因なのか切り分けるのも面倒臭え。

しかし観察していると、PostgreSQL が止まっている間、PostgreSQL が CPU を使い切っている。いったい何を?と pg_stat_activity を覗くと、

testdb=# select * from pg_stat_activity;
 datid  | datname | procpid | usesysid | usename  |                     current_query                      | waiting |          xact_start           |          query_start          |         backend_start         | client_addr | client_port
--------+---------+---------+----------+----------+--------------------------------------------------------+---------+-------------------------------+-------------------------------+-------------------------------+-------------+-------------
 415435 | testdb  |    5255 |    16389 | slony    | <IDLE>                                                 | f       |                               | 2012-02-24 19:48:07.394428+09 | 2012-02-24 19:22:35.865852+09 | 127.0.0.1   |       48965
 415435 | testdb  |    5260 |    16389 | slony    | <IDLE>                                                 | f       |                               | 2012-02-24 19:22:36.307407+09 | 2012-02-24 19:22:36.034956+09 | 127.0.0.1   |       48966
 415435 | testdb  |    5261 |    16389 | slony    | <IDLE>                                                 | f       |                               | 2012-02-24 19:45:36.396714+09 | 2012-02-24 19:22:36.03749+09  | 127.0.0.1   |       48968
 415435 | testdb  |    9887 |    16389 | slony    | select "_slony1".ddlScript_complete(1, $1::text, -1);  | f       | 2012-02-24 19:48:01.33087+09  | 2012-02-24 19:48:01.349439+09 | 2012-02-24 19:48:01.325438+09 | **.***.*.** |       43807
 415435 | testdb  |    5262 |    16389 | slony    | select "_slony1".createEvent('_slony1', 'SYNC', NULL); | t       | 2012-02-24 19:48:08.113728+09 | 2012-02-24 19:48:08.113921+09 | 2012-02-24 19:22:36.044133+09 | 127.0.0.1   |       48969
 415435 | testdb  |    4417 |       10 | postgres | select * from pg_stat_activity;                        | f       | 2012-02-24 19:48:08.321619+09 | 2012-02-24 19:48:08.321619+09 | 2012-02-24 19:17:32.972981+09 |             |          -1
(6 rows)

Slony-I が犯人っぽい。多分、2.0.6 -> 2.0.7 で入ったコレが原因。

この変更によって、EXECUTE SCRIPT の実行に最大 30 秒かかるようになった。連続で EXECUTE SCRIPT を流すと、2 回目以降の EXECUTE SCRIPT は確実に 30 秒かかる。

もちろん Slony-I に悪意はない。EXECUTE SCRIPT で主キーを変更すると同期が壊れる問題の対処として、EXECUTE SCRIPT を実行するタイミングを変更したのだろう。EXECUTE SCRIPT なんて滅多に実行しないはずだから、30 秒かかるくらいは大した問題ではない、と。しかし、

私には大問題だー! :-(

私のシステムは大量の DDL を使う。それも複数の DDL を纏めて流すのではなく、それぞれの DDL を個々に EXECUTE SCRIPT で流す。そうして、いつもなら 5 分で終わる処理が、1 時間かかるようになった。orz

そもそも 2.0.7 に上げたのには理由がある。2.0.6 には、一方のサーバーを再起動するだけで同期が停止する という、非同期レプリケーションとしてあるまじき致命的な不具合がある(slon 再起動で復旧する)。この問題だけは放置できない。しかし EXECUTE SCRIPT の問題も、私にとっては大問題だ。

ひとしきり悩んだ結果、2.0.7 から bug 217 の修正を除去するのは面倒そうなので、2.0.6 に同期復旧の patch だけを当てることにした。

さて、今後のアップグレードはどうしたもんか・・・。


2016-06-07 追記

今更の更新だが、この EXECUTE SCRIPT が遅い問題は、下記の patch で解決する。ddlScript_complete_int 中の 1 行をコメントアウトするだけで良い。

--- slony1-2.0.8/src/backend/slony1_funcs.sql.orig	2014-04-09 08:18:17.000000000 +0900
+++ slony1-2.0.8/src/backend/slony1_funcs.sql	2016-06-07 20:09:32.000000000 +0900
@@ -3800,7 +3800,7 @@
 	v_row				record;
 begin
 	perform @NAMESPACE@.updateRelname(p_set_id, p_only_on_node);
-	perform @NAMESPACE@.repair_log_triggers(true);
+	-- perform @NAMESPACE@.repair_log_triggers(true);
 	return p_set_id;
 end;
 $$ language plpgsql;

2.1.4 で動作することも確認している。2.2.x は未確認だが、たぶん問題ないと思う。

もちろん、これは「EXECUTE SCRIPT で主キーを変更しない」ことが条件となる。仮に主キーの変更が必要になったとしても、そのテーブルを一旦 Slony-I から外して主キーを変更し、Slony-I に再登録すれば良い。テーブルの再構築は発生してしまうが、EXUCUTE SCRIPT に 30 秒かかることに比べれば、私にとっては問題でない。そもそも主キーなんてそうそう変更しないし。

2012-02-22

Slony-I: duplicate key value violates unique constraint "sl_nodelock-pkey", part 1

Slony-I をアップグレードしたら、エラーが発生するようになった。

2012-02-22 12:15:13.37963 FATAL  localListenThread: "select "_slony1".cleanupNodelock(); insert into "_slony1".sl_nodelock values (    1, 0, "pg_catalog".pg_backend_pid()); " - ERROR:  duplicate key value violates unique constraint "sl_nodelock-pkey"

slon 再起動でも復旧しない。公式ドキュメントによると、

This error message is typically a sign that you have started up a second slon process for a given node. The slon asks the obvious question: "Do you already have a slon running against this node?"

Supposing you experience some sort of network outage, the connection between slon and database may fail, and the slon may figure this out long before the PostgreSQL instance it was connected to does. The result is that there will be some number of idle connections left on the database server, which won't be closed out until TCP/IP timeouts complete, which seems to normally take about two hours. For that two hour period, the slon will try to connect, over and over, and will get the above fatal message, over and over.

とのことだが、今回の場合には二重起動もネットワークエラーもあり得ない。サーバーを再起動しても直らないし。エラーになる前にやったことは、

  1. slave: stop slon
  2. slave: upgrade Slony-I 2.0.6 -> 2.0.7
  3. slave: reboot (start slon)
  4. master: stop slon
  5. master: upgrade Slony-I 2.0.6 -> 2.0.7
  6. master: reboot (start slon)

で、master 側がエラーになる。今まで 2 回起こったので、再現性はあるような気がする。いつか調べるハメになるかも知れないが、まずは復旧させないと。

# psql -U postgres testdb
testdb=# select * from _slony1.sl_nodelock;
 nl_nodeid | nl_conncnt | nl_backendpid
-----------+------------+---------------
         2 |        184 |          2608
         2 |        185 |          2593
         2 |        186 |         11678
         1 |          0 |          2583
         2 |        183 |          2606
(5 rows)

testdb=# delete from _slony1.sl_nodelock where nl_nodeid = 1 and nl_conncnt = 0;
DELETE 1
testdb=# \q

sl_nodelock から duplicate しているレコードを削除して、slon を再起動すれば復旧する。


2012-06-27 追記

いつか調べるハメになるかも知れないが、まずは復旧させないと。

調べた。

2012-02-20

runit: svlogd failed

runit をビルドして使っていれば一生のうち何回かは出くわすエラー。かくいう私も初見はかなり焦った。「何も変えてないのに何で!?」って。

・・・と思ったけど、検索しても目ぼしいものが見つからないね。これに悩むのはかなりレアらしい。

make[1]: Entering directory `/package/admin/runit-2.1.1/compile'
./check-local chpst runit runit-init runsv runsvchdir runsvdir sv svlogd utmpset

Checking chpst...
Checking runit...
Checking runit-init...
Checking runsv...
Checking runsvchdir...
Checking runsvdir...
Checking sv...
Checking svlogd...
usage: svlogd [-ttv] [-r c] [-R abc] [-l len] [-b buflen] dir ...

111
$Id: 5e55a90e0a1b35ec47fed3021453c50675ea1117 $
usage: svlogd [-ttv] [-r c] [-R abc] [-l len] [-b buflen] dir ...

111
0
foo
0
foo
bar
baz
0
foo
bar
baz
:oo
:ar
:az
0
foo
bar
baz
:oo
:ar
:az
foo
svlogd failed.
make[1]: *** [check] Error 1
make[1]: Leaving directory `/package/admin/runit-2.1.1/compile'

svlogd のテストに失敗している。これを眺めてても訳分からんし、失敗する環境では何度やっても失敗するので、とっとと諦めてソースを読むのが吉。

先の出力と突き合わせると、エラー場所は次の部分。

runit-2.1.1/src/svlogd.check:

echo t2 >"${ctmp}"/config
( echo foo; sleep 3 ) |svlogd "${ctmp}"
echo $?
cat "${ctmp}"/current

これから想像するに、2 秒でローテートする設定にして 3 秒待ったのに、current ログがローテートして(空になって)いない?

ということは時計? クロック? あ、これ VM 環境だ。

もういい加減に学習しろ、と言われても仕方がない感じだが、色んな所にシステムを入れまくっていると忘れることもあるんだよ。VMware も Server とか Player とか ESX とか色々あってバージョンによっても挙動が変わるし、イメージをコピー・移動とかしてたらそりゃあいつか間違うわ。せめてもの予防策は divider=10 を必ず付けておくくらいか。

2012-02-16

Bash: BASH_SOURCE

Bash で __FILE__ みたいなやつが欲しい。つまり Python でいう、

if __name__ == '__main__':
   main()

とか、Perl でいう、

if ($0 eq __FILE__) {
   main;
}

おっと、Perl ではこっちの方がイケてるんだっけ?

main unless caller;

とかいうのを Bash でやりたい。今まではその度に誤魔化してお茶を濁してきたが、そろそろ我慢できなくなってきた。で、調べると BASH_SOURCE を使え、と。

if [ "$0" = "$BASH_SOURCE" ] ; then
    main "$@"
fi

組み込み配列変数 BASH_SOURCE の 0 番目要素に現在処理中のファイル名が入っている。なので本来は ${BASH_SOURCE[0]} とするのが正確なのだろうが、動くなら簡単な方でいいや。

ただ、配列変数を単純参照したとき 0 番目要素の参照になる、という根拠は manpage や「O'reilly 入門 bash 第3版」などからは見つけきらんかった。

2012-02-12

Java: OmitStackTraceInFastThrow

あるとき開発者からこんなことを聞かれた。

「Java のスタックトレースが出なくなることがあるんだけど、リモートデバッグを始めると出るようになるんだよ。そしてリモートデバッグを止めると、また出なくなるの。そういうの、何か知らない?」

最初、「この人は何を言ってるんだろう? :-o」と思っていたが、念のため調べてみると・・・、

本当にあったー!

OmitStackTraceInFastThrow が原因らしい。こいつはデフォルトで ON になっているので、OFF にするにはオプション -XX:-OmitStackTraceInFastThrow を使う。

昔 Java のオプションを決めるときに散々調べたけど、このオプションは見なかったなー。GC 関連のオプションは飽きるほど見たけど。でもこれはもっと有名になっても良いオプションだと思う。

サーバ VM のコンパイラではすべての「コールド」組み込み例外に対する的確なスタックバックトレースを提供しています。このような例外が数回スローされると、パフォーマンス向上のため、メソッドが再コンパイルされることがあります。再コンパイルの後、コンパイラはスタックトレースを提供しない事前割り当て済みの例外を使用して、より速い方法を選択できます。事前割り当て済みの例外を全く使用しないようにするには新しいフラグ -XX:-OmitStackTraceInFastThrow を使用します。

2012-01-31

Emacs: BDF fonts no longer supported on MS-Windows.

Emacs 23 からフォントは MeiryoKe_Console を使っている。フォントさえ持っていけば Windows XP でも Linux でも使えるし、等幅だし、何もしなくても「\」がバックスラッシュになるし。フォント・設定は SCM からチェックアウトするだけにしてるので楽チン。

しかし、依然としてエディターでアンチエイリアスフォントを使うのを認めたくない気持ちもある。それまで intlfonts を使っていたし、何とか使えんかなーと暇なときに色々やってたのだが・・・、

** BDF fonts no longer supported on MS-Windows.

がーん、知らんかった。orz

じゃあ info に載っけるなー。:-(

でもまあ考えようによっては未練を断ち切ってくれたとも。Linux では動くのかも知らんけど、OS 間で設定を変える手間は掛けたくない。

さてフォントは結論が出たし、今まで手を掛けれなかったところをボチボチやっていくとする。差し当たりは encoding 周り。まあ殆んど↓からのコピペだけど。

とりあえず今の私に必要なのは下記くらい。

  • sjis ではなく cp932 を使う。
  • メールで機種依存文字(丸数字・ローマ数字など)を見る。

後者は今まで izonmoji-mode を使っていたが、少なくとも Emacs 23 + Gnus では動かんかった。

(when (>= emacs-major-version 23)
  (set-coding-system-priority 'utf-8 'cp932))

(when (and (>= emacs-major-version 23)
	   (require 'cp5022x nil t))
  ;; Gnus
  (eval-after-load "mm-util"
    '(when (coding-system-p 'cp50220)
       (add-to-list 'mm-charset-override-alist '(iso-2022-jp . cp50220))))
  ;; SEMI
  (eval-after-load "mcs-20"
    '(when (coding-system-p 'cp50220)
       (add-to-list 'mime-charset-coding-system-alist
		    '(iso-2022-jp . cp50220))))
  )

職場では Gnus、家では Wanderlust を使っているが、そろそろ Gnus に統一する頃合かも知れん。Wanderlust はメンテナが誰だかよく分からない状態だし、インストールも面倒いし。でも Emacs 歴 = Wanderlust 歴の自分にとって、これは結構な決断だったりする。

2012-01-25

Animes in the 4th quarter of 2011

アニメは IT エンジニアの必須科目です。 ということで簡単なレビューを。特に断りが無い限り、原作は読んでいない(書籍) or やっていない(ゲーム)。

初話の衝撃が凄かった。「何てものが始まったんだ!」とワクワクしながら見てたら、回を追うごとにグングンつまらなくなっていった。もはや終盤は意味が分からん。せっかくのぶっ飛んだ設定が勿体ない。しかしそれを差し引いてもネタとして見る価値がある。「きっと何者にもなれないお前たち」「生存戦略」「早くすり潰さないと」など、覚えておくべきものは多い。

エロがどうとかいうより、もう話が徹底的につまらん。ただ OP 曲は意外と好き。それだけ。

決して面白くはないはずなのに、何故か結構楽しんで見てしまった。内容は余りにバカバカしいが、作り手がそれを分かってなおバカに振っているところが良い。真面目に見るとつまらないので、見る側もバカになる必要がある。

何か凡庸な。見ているとそれなりに楽しめるが、見なくても平気。OP 曲のサビに入るまでが好き。

けいおん!よりはよっぽどこっちが好き。実はのりえの存在がこの作品の要だと思う。特にコーたんに萌えている時のハイテンションが素敵。OP/ED 曲は(作曲とボーカルの人が微妙だが)作品と合っていて良いと思う。

てっきりセイバー・遠坂がメインかと思ったら、やつらは囮(おとり)。予想に反して、そのムサい風貌ゆえに気にもかけていなかったライダーが熱かった。その男気に惚れた。愛すべきキャラ。私的にはこれだけで見る価値があった。

相変わらずの男色 only。ここまで徹底されるとむしろ清々しい。私は社会勉強として見ているが、普通の男子は見る必要なし。念のため、私自身は「杏ちゃん全然可愛いじゃん!」っていうヘテロ系(最近覚えた)だからね。

中二病が治らずそのまま高二になった人が書いたような作品。設定凝りすぎ・登場人物多すぎに加え、説明・描写もそこそこに話が進行していくため、予習・復習なしでついていくのは殆んど不可能。普通に見ると分かりかけてきた頃に終わる。話数を倍にしてゆっくりやれば、独特な世界観でファンを増やせたかも知れないのに。

この手のアニメを見るといつも、よくこれだけの人数のキャラ作りができるもんだ、と関心する。しかし話数を考えると、キャラへの思い入れという点で人数が増えると逆に不利か。内容は、成り上がる過程はそこそこで、成り上がってからはウジウジでつまらんかった。まあ思い入れが無いし、ゲームをやってればまた違ったとは思う。

2012-01-22

Perl can invoke every shebang

Perl で Python スクリプトを実行してみる。

$ cat hello.py
#!/usr/bin/python
print 'Hello Python!'

$ perl hello.py
Hello Python!

何これ!? 何で動くの?

$ cat hello2.py
#!/usr/bin/env python
print 'Hello Python!'

$ perl hello2.py
Hello Python!
$ cat hello.sh
#!/bin/bash
echo Hello Bash!

$ perl hello.sh
Hello Bash!

どうやら Perl は shebang 行を理解して良きに計らってくれるらしい。今まで Perl 使ってて全く気付かなかった。何て気の利く子!

ということは、ファイル形式が分からないときは Perl で起動するのが一番確実ということか。となると次は当然「バイナリファイルも!」と期待は膨らむが、残念ながら ELF 形式は起動できなかった。

何でこんなことに気付いたかというと、昔書いた Makefile 中に「perl foo.py」とかいうのを見つけたから。いっそエラーになってくれれば恥ずかしい思いをせずに済んだのに・・・。