Chat (Lingr.com)
Informaiton
Daily
Column
- MySQL日本語の旅(5/1)
- アクセス向上秘伝(5/9)
- 一風変ったHaskellλ門(6/13)
- SICP Answer Book (5/31) 問題3.26追加
Zope Solution
Extra
アーカイブ
OSS案内所
Site Info
関連リンク
- 2004: 01 02 03 04 05 06 07 08 09 10 11 12
- 2005: 01 02 03 04 05 06 07 08 09 10 11 12
- 2006: 01 02 03 04 05 06 07 08 09 10 11 12
- 2006-05-31 [Haskell] I/O も Lazy ゆえに ...
- 2006-05-30 [Tips] ログローテーション(2)
- 2006-05-29 [Tips] ログローテーション(1)
- 2006-05-26 [HTML] HTMLでツールチップを表示する
- 2006-05-25 [MacBook Pro] MacBook Pro 15インチで、2本指による右クリックを実現する
- 2006-05-24 [Tips] 安全にファイルを更新する
- 2006-05-23 [GCC] 有効なマクロ定義をダンプする
- 2006-05-22 [Gauche] Gauche で簡易 split を書く
- 2006-05-19 [Tips] launchdでデーモンを起動する
- 2006-05-18 [IMAP] IMAPで、Message-Idが同じメールを削除する
- 2006-05-17 [IMAP] POPで受信したMH形式のメールをIMAPサーバーにアップロードする
- 2006-05-16 [Tips] Mac OS X で svscan を自動起動する
- 2006-05-15 [Tips] NetBSD で svscan を自動起動する
- 2006-05-12 [Tips] daemontools を使って Kahua を運用する
- 2006-05-11 [Haskell] 簡易版 paste
- 2006-05-10 [JavaScript] 漢字と英数の混在文字列から入力フィールドの桁数を得たい
- 2006-05-09 [quiz] 漢字と英数の混在文字列から入力フィールドの桁数を得たい
- 2006-05-08 [DNS] ルートサーバを調べる
- 2006-05-02 [Gauche] tr もどきをGaucheで書いてみた
- 2006-05-01 [Haskell] ポイントフリースタイル
2006-05-31 [Haskell] I/O も Lazy ゆえに ...
Haskell では遅延評価「必要にならないと評価されない」が基本で, 入出力も例外ではない.
humuhumu.txt を読み出し,それを(関数 foo で)加工して元の humuhumu.txt ファイル に書きもどすプログラムを以下のように書いたとする.
import Data.Char
import System.IO
main = do { r <- openFile "humuhumu.txt" ReadMode
; s <- hGetContents r
; w <- openFile "humuhumu.txt" WriteMode
; hPutStr w (foo s)
; hClose w
}
foo = map toUpper
humuhumu.txt の内容が
humuhumu is a fish.
だったとすると,実行後は
HUMUHUMU IS A FISH.
になるはずである. 実行してみると
% runhaskell foo.hs *** Exception: humuhumu: openFile: resource busy (file is locked)
クローズされていないファイルをWriteModeでオープンしようとして 例外が発生したらしい.hGetContents は EOFを読むとファイルハンドルを クローズしてくれるはずなのだが...
明示的にクローズしてみよう.
import Data.Char
import System.IO
main = do { r <- openFile "humuhumu.txt" ReadMode
; s <- hGetContents r
; hClose r
; w <- openFile "humuhumu.txt" WriteMode
; hPutStr w (foo s)
; hClose w
}
foo = map toUpper
実行してみると,
% runhaskell foo.hs %
エラーはでないが結果は悲惨なことになっている.
% ls -l humuhumu.txt -rw-r--r-- 1 nobsun kahua 0 2006-05-31 16:29 humuhumu.txt
がーーーん.からっぽになっちゃったぁ orz
クローズする前に s は必要とされていなかったので,何も入力されていない. つまり,s は空文字だった.それを上書きしてしまった.
クローズするまえに s を評価する方法はないだろうか. 実はある.seq という特別な関数を使う.
import Data.Char
import System.IO
main = do { r <- openFile "humuhumu" ReadMode
; s <- hGetContents r
; s `seq` hClose r
; w <- openFile "humuhumu" WriteMode
; hPutStr w (foo s)
; hClose w
}
foo = map toUpper
seq x y を評価すると,x を評価してから y を評価してその値を返す.
もういちど,humuhumu.txt の内容を
humuhumu is a fish.
にもどして,実行してみよう.
% runhaskell foo.hs % cat humuhumu.txt HUMUHUMU IS A FISH.
やった!!
とよろこぶのはまだ早い... (to be continued)
--nobsun
2006-05-30 [Tips] ログローテーション(2)
前回は他のプログラムが出力したログを ローテーションするプログラムを紹介した。 今回は、ログ出力そのものを担いつつログローテーションを行うプログラムを紹介する。
rotatelogs
Apache HTTP serverに同梱されているログプログラム。 パイプ経由で受けた情報をそのままログに吐き出す。つまり、タイムスタンプは付加しない。 以下は、Apache HTTP serverの設定ファイル内でrotatelogsを使ってログ出力を行う際の 設定例だ:
CustomLog "|bin/rotatelogs /var/logs/access_log 5M" common
この時、実際のログファイル名は access_log.1149202800 のように、 指定した名前に、最初のログエントリが書き出された時刻のエポックからの秒数が 付加されたものとなる。なお、ログファイル名に%が含まれている場合、 それを strftime(3) で処理したものが実際のファイル名として使用される。
ErrorLog "|bin/rotatelogs -l /var/logs/error_log.%Y-%m-%d-%H:%M:%S 86400"
ログファイル名は error_log.2006-05-30-01:10:15 のようになる。 第2引数はローテーションの条件を表し、数値にMがついている場合はログファイルのサイズが この値を超えた場合(単位はMB)に、単なる数値の場合は最初のログ出力からこの秒数を超えた場合に、 その時点で出力対象としていたログファイルを閉じ、次のファイルへの出力を開始する。 -lとあるのは、時刻をUTCではなく、ローカルタイムで判断するためのオプションだ このオプションは、httpd 2.0.51以降に付属のものでのみ使用できる。 また、第3引数にUTCからのオフセットを分で指定することもできる。 古いログファイルの圧縮機能や削除機能は含まれていないので、 必要なら別途crond+shスクリプトなどで対応する必要があるだろう。
cronolog
rotatelogsを基に、さらに高機能化したログプログラム。 基本的な動作はrotatelogsと同じだが、ローテーションサイクルを 明示しなくても、 ログファイル名のフォーマットから適切にローテーションを行ってくれる。 例:
CustomLog "|/usr/sbin/cronolog /www/logs/apache/access.log.%Y-%m-%d" common
この場合、例えば2006年05月29日から30日に日付が変わった瞬間に access.log.2006-05-29は閉じられ、 以降最初の出力時にaccess.log.2006-05-30が作成されてログ出力される。
また、必要なディレクトリを自動で作成したり、
CustomLog "|/usr/sbin/cronolog /www/logs/apache/%Y/%m/%d/access.log" common
最新のログファイルへのハードリンクやシンボリックリンクを作成したり といった便利な機能を備えている。 特にawstatsなどのログアナライザで日毎の解析を行いたいような場合は、 このプログラムを使った方がよいだろう。 ログ出力にタイムスタンプは付加せず、古いログの圧縮機能や削除機能が含まれていないのは rotatelogsと同様である。
multilog
daemontoolsに含まれるログプログラム。 標準入力から読み込んだデータをログとして吐くという動作は上記の2つの プログラムと同様だが、以下のような機能と特徴を備える。
- ログの書き出し先をファイル名ではなくディレクトリ名で指定する (ファイル名は決まっている)。
- tai64n形式によるタイムスタンプ付加機能
- パターンマッチによる入力行の選択
- 単一ファイルへの上書き更新
- 複数ログへの出力(の振り分け)
- コマンドの呼び出し
- ローテーションサイクルはサイズでのみ指定
通常はsuperviseやsvscanと組み合わせて使うが、単独で使うこともできる。 Apache HTTP serverのログ出力にmultilogを使うには、
CustomLog "|/command/multilog s16777215 n20 /www/logs/access" common
こうすると、/www/logs/access ディレクトリの下に一連のログファイルを作成する。 現在出力中のログファイル名はcurrentであり、16MB(s16777215)を超えるタイミングで、 その時刻をtai64n形式で表した文字列に".s"を付加したファイル名にリネームし、 新たにcurrentファイルを作成してログ出力を続ける。ログファイルはcurrentを 含めて20個まで(n20)で、それを超えると古いログファイルを削除する。 ディスクフルなどでログファイルの出力ができない場合は 入力をバッファリングしつつ出力を遅延する。 このため入力が失われることはない代わりに、 出力側のプログラム(この場合はApache HTTP server)をブロックする可能性がある。
ログの管理は、サーバ運用の重要な要素であるだけに、 他にも様々なプログラムがあると思う。この記事を書いている最中にも、 LogSplitterというパッケージを見つけた。 「LogSplitter is a log handler for Apache which combines the features of rotatelogs, splitlog and (in part) cronolog.」 だそうだ。
--び
There is no comment.
2006-05-29 [Tips] ログローテーション(1)
サーバを管理していると必ずと言っていいほど必要になるのが 「ログローテーション」。 今時のたいていのOSで標準的な機能となっているため、 案外意識せずに使っている場合が多いのではないか。
ということで、 ログローテーションを実現している様々なプログラムを紹介してみる。
savelog
Debian GNU/Linux で syslogd、klogd が吐き出すログのローテーションに使われている。 debianutils パッケージに含まれているところを見ると、Debian固有のコマンドらしい。 コマンドラインオプションで全ての挙動を制御する単純なプログラムで、主にcrondが 呼び出す。機能としては
- 通番付加によるログローテーション
- ローテーションサイクルの指定(デフォルトで7世代)
- 古いログの圧縮(ローテーション第1世代は圧縮しない)
- ローテーションサイクルを超えたファイルの削除
- オーナー、グループ、パーミッションの指定
/etc/cron.daily/sysklogd からの抜粋:
cd /var/log
for LOG in `syslogd-listfiles`
do
if [ -s $LOG ]; then
savelog -g adm -m 640 -u root -c 7 $LOG >/dev/null
fi
done
for LOG in `syslogd-listfiles --auth`
do
if [ -f $LOG ]; then
chown root:adm $LOG
chmod o-rwx $LOG
fi
done
/etc/init.d/sysklogd reload-or-restart > /dev/null
ログを吐いているプロセスにシグナルを投げる機能はないので、 別途スクリプトの中で行っているのがわかる。
機能的にはほぼ標準的なものは揃っているが、おそらくDebianでしか 使えない(他のLinuxディストリビューションについては不明)。
logrotate
たぶん一番有名なログローテーションプログラム。 一次配布元はredhatのようだが、 たいていのLinuxディストリビューションには当たり前のように入っている。 動作の制御は設定ファイルから行い、savelogの機能に加えて、
- ローテートを行う閾値となるログのサイズを指定
- ログ内容をメールで送信
- ローテーションの前後に起動するコマンドを指定 といった機能を備えている。manからの設定ファイルの抜粋:
"/var/log/httpd/access.log" /var/log/httpd/error.log {
rotate 5
mail www@my.org
size 100k
sharedscripts
postrotate
/usr/bin/killall -HUP httpd
endscript
}
この設定ファイルをコマンドラインに指定してcrondが呼び出す。
0 4 * * * /usr/sbin/logrotate /etc/logrotate.conf
Linuxディストリビューションで意識してログローテーションを行う際には これを使うのが普通のやり方になると思う。
newsyslog
*BSDで標準的に使われているログローテーションプログラム。 機能的にはsavelogとlogrotateの中間くらいか。 メールの送信機能やコマンドの呼び出し機能はないが、 シグナルの送出機能はある。動作の制御は設定ファイルで行い、 crondが呼び出す。設定ファイル /etc/newsyslog.conf の例:
/var/log/aculog uucp:dialer 640 7 * 24 Z /var/log/authlog 600 5 100 * Z /var/log/cron root:wheel 600 3 100 * Z /var/log/kerberos.log 640 7 * 24 ZN /var/log/lpd-errs 640 7 100 * Z /var/log/maillog 600 7 * 24 Z /var/log/messages 644 10 250 * Z /var/log/wtmp root:utmp 664 7 * 168 ZBN /var/log/wtmpx root:utmp 664 7 * 168 ZBN /var/log/xferlog 640 7 250 * Z /var/log/httpd/access_log 644 20 4096 * Z /var/run/httpd.pid /var/log/httpd/error_log 644 20 1024 * Z /var/run/httpd.pid
いかにも昔ながらのUNIXという風情の設定ファイルだ。 NetBSDの場合、標準的には毎正時に呼び出している。
0 * * * * /usr/bin/newsyslog
*BSDユーザは当たり前のように使っていると思うが(筆者もNetBSDなサーバではこれを使っている)、 Linuxユーザはおそらく目にする機会もあまりないのではないか。 昔 Slackware に newsyslog が含まれていたようなかすかな 記憶があるのだが、勘違いかもしれない。
他にもきっとあると思うのだが、筆者が実際に使ったことがあるのはこのくらいだ。 面白いもの(有益なもの)があればぜひ教えて欲しい。
次回はログ出力そのものの面倒を見つつログローテーションを実現するプログラムを紹介する。
--び
There is no comment.
2006-05-26 [HTML] HTMLでツールチップを表示する
JavaScriptを使う方法もあるが、最もシンプルなのはtitle属性を使う方法だ。
<img src="humuhumunukunukuapuaa.gif" alt="モンガラカワハギ" title="モンガラカワハギ">
任意の範囲に表示したければspanを使えば良い。
<p>任意の<span title="ここの部分だけにツールチップが表示されます">範囲</span>に表示したければspanを使えば良い。</p>
--yasuyuki
There is no comment.
2006-05-25 [MacBook Pro] MacBook Pro 15インチで、2本指による右クリックを実現する
MacBook 13.3インチから可能になった2本指による右クリック。MacBook Pro 15インチでもこれを可能にする方法がある。ただし自己責任にてお願いします。
http://forum.osx86project.org/index.php?showtopic=17685&pid=118022&st=20&
上記からMachineSettings.framework.zipをダウンロードして展開するとMachineSettings.frameworkフォルダが得られる。
これを/System/Library/PrivateFrameworks/MachineSettings.frameworkフォルダにすべて上書きして再起動すれば良い。
注意: /System/Library/PrivateFrameworks/MachineSettings.frameworkをどこかにバックアップしておかないと元にもどせなくなる。
--yasuyuki
Mac OS X 10.4.7から、MacBook Proでもこの機能が使えるようになったらしいです。 自分のはProじゃないので確認はしていませんが。
2006-05-24 [Tips] 安全にファイルを更新する
いつ参照されるかわからないファイルを安全に(つまり、inconsistentな状態を見せることなく)更新したい。 ついでにバックアップも取っておきたい。そんな時の定番。更新したいファイルがhogeだとすると、
% ln hoge hoge.old && mv hoge.new hoge
とするのが正しい。気にしないでけっこう直接書き換えちゃったりするものだが、 タイミングによっては痛い目にあう。他のデーモンから呼ばれるコマンドファイルは特に危険。
--び
There is no comment.
2006-05-23 [GCC] 有効なマクロ定義をダンプする
ややこしいマクロ定義や #if 〜 #endif がネストしまくっているようなCコードを追っていると、 いったいどのマクロ定義が有効なのか、その値はいくつなのか追い切れなくなる時がある。 そんな時は gcc に -c の代わりに -E -dM をわたしてみるとよい。
% gcc -E -dM hoge.c
#define __used __attribute__((__used__))
#define __DBL_MIN_EXP__ (-1021)
#define __weak_extern(sym) __asm__(".weak " _C_LABEL_STRING(#sym));
#define __CUSERID_DECLARED
[中略]
#define __FLT_MANT_DIG__ 24
#define __VERSION__ "3.3.3 (NetBSD nb3 20040520)"
#define ___RENAME(x) __asm__(___STRING(_C_LABEL(x)))
#define _I386_INT_TYPES_H_
特定のコンパイル条件下ではなく、単に「システムではどんなマクロが定義されているのか」 を知りたいだけなら、これでよい。
% echo|gcc -E -dM - #define __DBL_MIN_EXP__ (-1021) #define __FLT_MIN__ 1.17549435e-38F #define __CHAR_BIT__ 8 #define __WCHAR_MAX__ 2147483647 #define __DBL_DENORM_MIN__ 4.9406564584124654e-324 #define __FLT_EVAL_METHOD__ 2 #define __i386__ 1 #define __SIZE_TYPE__ unsigned int #define __ELF__ 1 #define __DBL_MIN_10_EXP__ (-307) #define __FINITE_MATH_ONLY__ 0 #define __GNUC_PATCHLEVEL__ 3 #define __FLT_RADIX__ 2 #define __LDBL_EPSILON__ 1.08420217248550443401e-19L #define __SHRT_MAX__ 32767 #define __LDBL_MAX__ 1.18973149535723176502e+4932L #define __LDBL_MAX_EXP__ 16384 #define __LONG_MAX__ 2147483647L #define __SCHAR_MAX__ 127 #define __DBL_DIG__ 15 #define __USER_LABEL_PREFIX__ #define __STDC_HOSTED__ 1 #define __LDBL_MANT_DIG__ 64 #define __FLT_EPSILON__ 1.19209290e-7F #define __NetBSD__ 1 #define __LDBL_MIN__ 3.36210314311209350626e-4932L #define __WCHAR_TYPE__ int #define __FLT_DIG__ 6 #define __FLT_MAX_10_EXP__ 38 #define __INT_MAX__ 2147483647 #define __FLT_MAX_EXP__ 128 #define __DECIMAL_DIG__ 21 #define __DBL_MANT_DIG__ 53 #define __WINT_TYPE__ int #define __GNUC__ 3 #define __LDBL_MIN_EXP__ (-16381) #define __LDBL_MAX_10_EXP__ 4932 #define __DBL_EPSILON__ 2.2204460492503131e-16 #define __DBL_MAX__ 1.7976931348623157e+308 #define __DBL_MAX_EXP__ 1024 #define __FLT_DENORM_MIN__ 1.40129846e-45F #define __LONG_LONG_MAX__ 9223372036854775807LL #define __FLT_MAX__ 3.40282347e+38F #define __GXX_ABI_VERSION 102 #define __FLT_MIN_10_EXP__ (-37) #define __FLT_MIN_EXP__ (-125) #define i386 1 #define __GNUC_MINOR__ 3 #define __DBL_MAX_10_EXP__ 308 #define __LDBL_DENORM_MIN__ 3.64519953188247460253e-4951L #define __DBL_MIN__ 2.2250738585072014e-308 #define __PTRDIFF_TYPE__ int #define __tune_i386__ 1 #define __LDBL_MIN_10_EXP__ (-4931) #define __REGISTER_PREFIX__ #define __LDBL_DIG__ 18 #define __NO_INLINE__ 1 #define __i386 1 #define __FLT_MANT_DIG__ 24 #define __VERSION__ "3.3.3 (NetBSD nb3 20040520)"
OSやアーキテクチャを識別するためのマクロや、 数値型の最大値/最小値などを調べるのに便利である。
この元ネタ、どこかで教わったのだと思うのだが、 どこでだったかどうしても思い出せない。 gccのマニュアルを見ても
-dM -fdump-rtl-mach Dump after performing the machine dependent reorganization pass, to file.35.mach.
などと書いてあって、とうてい自分で見つけたとは思えないのだけど。
--び
There is no comment.
2006-05-22 [Gauche] Gauche で簡易 split を書く
Mac OS Xには標準的にUNIX系コマンドがひとそろい含まれているのだが、 思わぬ落とし穴にハマることがある。あるとき、 10GB超の単一ファイルを4GBごとに分割する必要に迫られた。 UNIX使いなら普通splitを使うだろう。ところが...
% split -b 4096m somelarge.file somelarge. split: too many files.
調べてみるとなぜか250KB前後のファイルに分割されてしまう。 そりゃtoo manyになりますよ。
急いでいたこともあって、仕方なく以下のスクリプトをでっち上げた。
#!/usr/bin/env gosh
;;; -*- mode: scheme; coding: utf-8-unix -*-
(use gauche.uvector)
(use util.match)
(define (copy-port-partially in out size . args)
(let* ((buffer (get-optional args (make-u8vector 65536)))
(bufsize (u8vector-length buffer)))
(let loop ((total 0)
(rsize (read-block! buffer in 0 (min size bufsize))))
(cond ((= total size) total)
((> total size) (errorf "size ~s required but got total %~s" size total))
((eof-object? rsize) (if (> total 0) total rsize))
(else
(write-block buffer out 0 rsize)
(loop (+ total rsize)
(read-block! buffer in 0 (min (- size total rsize) bufsize))))))))
(define (split-in-bytes size in base . args)
(let loop ((num 0))
(let* ((fname (format "~a.~v,'0d" base 6 num))
(ret (call-with-output-file fname
(lambda (out)
(with-port-locking out
(lambda ()
(apply copy-port-partially in out size args)))))))
(if (eof-object? ret)
(sys-unlink fname)
(loop (+ num 1))))))
(define (main args)
(let1 buf (make-u8vector 131072)
(match args
((_ size infile base)
(call-with-input-file infile
(lambda (in)
(with-port-locking in
(lambda ()
(split-in-bytes (string->number size) in base buf))))))))
0)
1個余分に0byteのファイルが出来てしまうため、 最後にそれを消しているあたりがどうにも香ばしい。
さて、ここからは余談。 とりあえずこのスクリプトでその場は凌げたのだが、どうも納得がいかない。 幸い、ソースコードは公開されているので(ADCの登録が必要)、調べてみた。 splitコマンドのコードはtext_cmds-47プロジェクトに含まれている。
long bytecnt; /* Byte count to split on. */
ちなみに /usr/include/ppc/limits.h ではLONG_MAXは
#define __LONG_MAX__ 2147483647L
となっている。 つまり、Mac OS X標準のsplitは2GBを超えるファイルサイズに分割することができない。
この制限、64bitファイルシステムを持つOSとしてはあんまりだと思うので、 NetBSDから/usr/src/usr.bin/split/split.c を持ってきてコンパイルし、 /usr/local/bin に入れてしまった。上記のスクリプトでもよかったのだが、 やっぱり遅いし、真っ当なsplitコマンドを入れておきたかったのだ。 ちなみに NetBSD版split ではbytecntはoff_tで、符号付き64bit整数だから、 64bitファイルシステムで許容される最大サイズまで正常に扱える(はず)。
--び
There is no comment.
2006-05-19 [Tips] launchdでデーモンを起動する
先日の記事の最後で、 launchdでsvscanを起動してみる、と書いたのだが、その後いろいろ調べていくと、 どうやらsvscanを起動する手段として launchd はあまり的確ではない。 manに含まれる関連マニュアルやAppleのドキュメントを読む限り、launchd は init+cron+inetd といったプログラムのようだから、svscan の下で動かすデーモン類を起動することを考えた方が適切だと思われる。
ということではなはだ言い訳がましいが、急遽
- 非特権ユーザ "kahua" の権限で
- Kahuaをデーモンとして起動する
ことを試してみる。
launchd はシステムの起動から終了まで動作する通常のデーモンプログラムの他に、 ユーザがログインする時に起動し、ログアウトする時に終了するデーモンプログラムも 扱うことができる。今回は前者なので、設定ファイルを /Library/LaunchDaemons もしくは ~/Library/LaunchDaemons に置くことになる。設定ファイルは Mac OS X ではおなじみの XML 形式のプロパティリストファイルである。 システム付属の Property List Editor を使って作成してもよいし、 この程度なら使い慣れたエディタの方が楽かもしれない。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>UserName</key>
<string>kahua</string>
<key>GroupName</key>
<string>kahua</string>
<key>Label</key>
<string>org.kahua.Kahua</string>
<key>OnDemand</key>
<false/>
<key>Program</key>
<string>/usr/local/kahua/bin/kahua-spvr</string>
<key>ProgramArguments</key>
<array>
<string>/usr/local/kahua/bin/kahua-spvr</string>
</array>
<key>RunAtLoad</key>
<true/>
<key>ServiceDescription</key>
<string>Kahua Supervisor Daemon</string>
<key>StandardOutPath</key>
<string>/usr/local/kahua/var/kahua/logs/kahua-stdout.log</string>
<key>StandardErrorPath</key>
<string>/usr/local/kahua/var/kahua/logs/kahua-stderr.log</string>
</dict>
</plist>
これを /Library/LaunchDaemons フォルダに org.kahua.Kahua.plist というファイル名で置き、
% sudo launchctl load /Library/Launch/Daemons/org.kahua.Kahua.plist
とするとKahuaが起動してくる。
% sudo launchctl list com.apple.KernelEventAgent com.apple.mDNSResponder com.apple.nibindd com.apple.periodic-daily com.apple.periodic-monthly com.apple.periodic-weekly com.apple.portmap com.apple.syslogd com.vix.cron org.postfix.master org.xinetd.xinetd org.kahua.Kahua
org.kahua.Kahua というラベル名で起動しているのがわかる。 これで、OS起動時にKahuaも同時に起動することができるようになる。 rc系の起動スクリプトに慣れている人間にとっては何だか回りくどい方法だが、 Mac OS Xでは今後これが主流になるようなので、慣れておくのも一興だろう。
--び
There is no comment.
2006-05-18 [IMAP] IMAPで、Message-Idが同じメールを削除する
前提: IMAPサーバーの同じディレクトリーに、同じMessage-Idのメールが複数存在する。重複したぶんだけ削除したい。
解法: Mozilla ThunderbirdのRemove Duplicate Messagesプラグインを使う。
実はこの用途のためだけにMozilla Thunderbirdをインストールしていたりして...
--yasuyuki
There is no comment.
2006-05-17 [IMAP] POPで受信したMH形式のメールをIMAPサーバーにアップロードする
前提: POPで受信した大量のメールが、ローカルディスクにMH形式で保存されている。これを新たなIMAPサーバーにアップロードして保存したい。
解法: MHとIMAP両方に対応しているMUAを使う。ex. Sylpheed
Ubuntu Linux 5.1に付属のSylpheed 2.2.0beta1だとなぜかMHからIMAPへのコピーができなかったので、 Windows版Sylpheed 2.2.4を使った。
(筆者の約8年分のメールの総容量は1GBほどであった)
--yasuyuki
There is no comment.
2006-05-16 [Tips] Mac OS X で svscan を自動起動する
Mac OS X のrcの仕組みは他の UNIXen に比べてかなり独特で、 デーモンを自動起動するためには Startup Item Bundle なるものを作成することになる。
まずは /Library/StartupItems フォルダ(なければ作る)に、Svscan フォルダを掘る。
% sudo mkdir -p /Library/StartupItems/Svscan
ここに以下のようなshスクリプトをフォルダと同じ名前で置く。もちろん、実行ビットを立てておく。
#!/bin/sh
##
# Svscan (from daemontools).
##
PKG_BIN=/command
SVSCAN_CMD=${PKG_BIN}/svscan
SVC_CMD=${PKG_BIN}/svc
SERVICE_DIR=/service
. /etc/rc.common
StartService ()
{
if ! pid=$(GetPID svscan); then
ConsoleMessage "Starting svscan"
env PATH=${PKG_BIN}:${PATH} ${SVSCAN_CMD} ${SERVICE_DIR} \
& echo $! >/var/run/svscan.pid
fi
}
StopService ()
{
if pid=$(GetPID svscan); then
ConsoleMessage "Stopping svscan"
kill -TERM "${pid}" && rm -f /var/run/svscan.pid
${SVC_CMD} -dx ${SERVICE_DIR}/* ${SERVICE_DIR}/*/log 2>/dev/null
else
echo "svscan is not running."
fi
}
RestartService ()
{
StopService
StartService
}
RunService "$1"
やっていることは昨日のNetBSD版のrcファイルと同じだ。
もうひとつ、同じフォルダに以下のようなファイルを StartupParameters.plist という名前で配置する
{
Description = "Svscan daemon manager";
Provides = ("Svscan");
Uses = ("Network");
}
このあたりのファイル構成については、Appleの 「Creating a Startup Item」 にざっと説明されている。また、システム関係の Startup Item Bundle が /System/Library/StartupItems にあるので、 これをいろいろ眺めてみるのも参考になるだろう。
これで自動起動/終了の準備は完了だ。コマンドラインから確かめてみる。 Startup Item Bundleを手動で起動するには、SystemStarterコマンドを使う。
% sudo SystemStarter start Svscan % sudo SystemStarter stop Svscan
なお、2番目の引数として渡すのは、StartupParameters.plist の Provides に渡している 値のどれかである(上記の場合はSvscan)。ここにStartup Bundleフォルダと違う値を入れ、 SystemStarterの第2引数にStartup Item Bundleのフォルダ名を渡して「なぜ起動しない」と悩まないように。
さて、実はMac OS X 10.4からは、デーモンの起動終了を管理するために、 別の新しい仕組みがある... らしい。 筆者は上記のStartup Item Bundleを10.2の頃から使い回しているので、 10.4からの新しい仕組み "launchd" についてはよく知らないのだ。 せっかくなので、次回はlaunchdを使ってsvscanを起動してみよう(このネタまだ引っ張るのか...)。
--び
There is no comment.
2006-05-15 [Tips] NetBSD で svscan を自動起動する
daemontools は慣れるとけっこう便利なのだが、 svscan は自分ではバックエンドに行ってくれないし、 プロセスIDを /var/run の下に書き出すような親切もしてくれないため、 OS起動時に svscan を自動的に起動するにはそれなりの準備がいる。 例えば、筆者は運用しているNetBSDマシンで、以下のようなrcファイルを書いている。
#!/bin/sh
#
#
#
# PROVIDE: svscan
# REQUIRE: NETWORKING
# BEFORE: ntpdate
#
. /etc/rc.subr
name="svscan"
rcvar="$name"
start_cmd="${name}_start"
stop_cmd="${name}_stop"
PATH=/command:${PATH}
svscan_start()
{
echo Starting "${name}."
sh -c '/command/svscan /service & echo $!>/var/run/svscan.pid'
}
svscan_stop()
{
if [ -f /var/run/svscan.pid ]; then
echo Stopping "${name}."
kill -TERM `cat /var/run/svscan.pid` \
&& rm -f /var/run/svscan.pid \
&& /command/svc -dx /service/* /service/*/log 2>/dev/null
/usr/bin/true
fi
}
load_rc_config $name
run_rc_command "$1"
これを /etc/rc.d/svscan に置き(別の場所からこの名前でシンボリックリンクを張ってもよい)、 /etc/rc.conf に
svscan=YES
とすればNetBSD起動時に自動的にsvscanが起動する。手動で起動、終了したい時は、
# /etc/rc.d/svscan start Starting svscan. # /etc/rc.d/svscan stop Stopping svscan.
とすればよい。
なぜREQUIRE: NETWORKINGで、BEFORE: ntpdateかと言えば、 このホストではsvscan配下でdnscacheやtinydnsなどを動かし、 ntpdateでプロバイダのNTPサーバと同期をとっているからだ。
なお、どういう順番でrcスクリプトが起動されるかを確認するには、
% rcorder -s nostart /etc/rc.d/*
とすればよい(実際 /etc/rc ではそうやって起動順序を決めている)。
--び
There is no comment.
2006-05-12 [Tips] daemontools を使って Kahua を運用する
筆者はdjbのソフトウェアの愛好者なので、当然個人サイトの Kahua の運用には daemontools を使っている。 /usr/local/kahua の下に Kahua が、 /package の流儀に従って daemontools がインストールされていて、 kahua ユーザと savelog ユーザが存在することを前提として、 こんな感じになる。 なお、/usr/local/service/kahua は別の場所でもかまわない。
# mkdir -p /usr/local/service/kahua # cat >/usr/local/service/kahua/run <<EOF #!/bin/sh exec 2>&1 exec env - PATH=/usr/local/kahua/bin/kahua:/command \ setuidgid kahua kahua-spvr EOF # chmod +x /usr/local/service/kahua/run # mkdir -p /usr/local/service/kahua/log # cat >/usr/local/service/kahua/log/run <<EOF #!/bin/sh exec /command/setuidgid savelog /command/multilog t ./main EOF # chmod +x /usr/local/service/kahua/log/run # mkdir -p /usr/local/service/kahua/log/main # chown savelog /usr/local/service/kahua/log/main
準備完了。 svscanが監視しているディレクトリに /usr/local/service/kahua へのシンボリックリンクを張ってやれば、 kahua-spvr と、その標準出力、標準エラー出力を受ける multilog とが 起動する。
# ln -s /usr/local/service/kahua /service
すぐ起動してくるはずなので、すかさず確認。
# /command/svstat /service/kahua /service/kahua/log /service/kahua: up (pid 8563) 13 seconds /service/kahua/log: up (pid 8564) 13 seconds
この方法の素敵なところは、
- kahua-spvr.scm や kahua-server.scm が何かの理由で死んだ時、 運が良ければ標準エラー出力に gosh が吐いた悲鳴を、タイムスタンプ付きで確実にログに記録できる。
- multilogの機能により、ログは自動的にローテートされる。
- kahua-spvrが死んでも、自動的に再起動してくれる。
- すでに daemontools を使っているサイトなら、 システムごとに微妙に流儀の違う /etc/rc に悩まされなくて済む。
上記の例ではやっていないが、ログの内容を判別してメールで知らせるような仕組みも multilog の機能でできるはず。
--び
There is no comment.
2006-05-11 [Haskell] 簡易版 paste
Lisper である sakae さんからメールをいただいた.
Haskell本が出たというので、この連休にちょっと練習してみました。
とのこと.\(^o^)/
Haskellで書いた unix コマンド paste の簡易版のコードを送っていただいた. OSS-WEB で公開してもよいとのことだったので,literate comment 形式に 変更して載せます(プログラムコードそのものには変更を加えていません).
#!/usr/bin/env runhaskell usage: paste.lhs file1 file2 .. > module Main where > import System > import System.IO > > main = do args <- getArgs > hl <- mapM (\f -> openFile f ReadMode) args > paste hl > mapM_ hClose hl > > paste :: [Handle] -> IO () > paste hl = do > s <- mapM fetchLine hl > if all (=="") s > then return () > else do > putStrLn $ join "\t" s > paste hl > > fetchLine :: Handle -> IO [Char] > fetchLine h = do > eof <- hIsEOF h > if eof > then return "" > else do > x <- (hGetLine h) > return x > > join :: String -> [String] -> String > join t [] = "" > join t (s:[]) = s > join t (s:sc) = s ++ t ++ join t sc
で,私も書いてみた.
#!/usr/bin/env runhaskell
usage: paste2.lhs file1 file2 ...
\begin{code}
module Main where
import Data.List
import System
main :: IO ()
main = getArgs
>>= mapM readFile
>>= putStr . unlines . map (concat . intersperse "\t") . zips "" . map lines
zips :: a -> [[a]] -> [[a]]
zips d xs | all null xs = []
| otherwise = case unzip $ map (hdtl d) xs of
(ys,zs) -> ys : zips d zs
hdtl :: a -> [a] -> (a,[a])
hdtl d [] = (d,[])
hdtl _ (x:xs) = (x,xs)
\end{code}
(2006-05-15):コード修正 (thanks [1..100]>>=pen さん)
--nobsun
zips を短くしてみました。 http://hpcgi2.nifty.com/1to100pen/wiki/wiki.cgi?p=%CB%E8%C6%FCHaskell の 2006-05-11 ところで 「map (hdtl d) xs of」のところが「map (f d) xs of」になってます。
2006-05-10 [JavaScript] 漢字と英数の混在文字列から入力フィールドの桁数を得たい
HTMLフォームを動的に生成するときに、元の文字列がぴったり収まるように入力フィールド(<INPUT type="text" ...>)のサイズを決めたい。
条件: ループ禁止
JavaScriptによるアレゲな回答。
function getMBLength(str) {
var rep = encodeURI(str.replace(/[\x20\x22\x25\x3c\x3e\x5b\x5c\x5d\x5e\x60\x7b\x7c\x7d]/g, '.'));
return rep.replace(/%(\d|[A-Z0-9]){2}%(\d|[A-Z0-9]){2}%(\d|[A-Z0-9]){2}/g, "..").length;
}
1行目で1バイト文字のうちエスケープされる文字を'.'に置換しencodeURIでエスケープしておく。 encodeURIは漢字部分をUTF-8にエスケープする。
2行目でエスケープされた部分を'..'に置換して長さを調べる。
(サイズを調べてもプロポーショナルフォントだと意味ないorz)
--yasuyuki
There is no comment.
2006-05-09 [quiz] 漢字と英数の混在文字列から入力フィールドの桁数を得たい
HTMLフォームを動的に生成するときに、元の文字列がぴったり収まるように入力フィールド(<INPUT type="text" ...>)のサイズを決めたい。
条件: ループ禁止
JavaScriptによるアレゲな回答は明日。
--yasuyuki
There is no comment.
2006-05-08 [DNS] ルートサーバを調べる
ネットワークの管理をしていると、 時々DNSのルートサーバのアドレス一覧が欲しくなることがある。 正しくはwww.root-servers.orgを見るのだろうが、 djbdnsに含まれるコマンドを使うと、こんな風に手軽に一覧を得られる。
% dnsqr ns . 2 .: 436 bytes, 1+13+0+13 records, response, noerror query: 2 . answer: . 99841 NS j.root-servers.net answer: . 99841 NS k.root-servers.net answer: . 99841 NS l.root-servers.net answer: . 99841 NS m.root-servers.net answer: . 99841 NS a.root-servers.net answer: . 99841 NS b.root-servers.net answer: . 99841 NS c.root-servers.net answer: . 99841 NS d.root-servers.net answer: . 99841 NS e.root-servers.net answer: . 99841 NS f.root-servers.net answer: . 99841 NS g.root-servers.net answer: . 99841 NS h.root-servers.net answer: . 99841 NS i.root-servers.net
参照しているDNSキャッシュサーバによっては、 additionalで各サーバのAレコードがいっしょに 得られることもある。 また、BINDに含まれるdigでも同じことができる。
% dig . ns ; <<>> DiG 9.2.2 <<>> . ns ;; global options: printcmd ;; Got answer: ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 18657 ;; flags: qr rd ra; QUERY: 1, ANSWER: 13, AUTHORITY: 0, ADDITIONAL: 0 ;; QUESTION SECTION: ;. IN NS ;; ANSWER SECTION: . 518400 IN NS A.ROOT-SERVERS.NET. . 518400 IN NS H.ROOT-SERVERS.NET. . 518400 IN NS C.ROOT-SERVERS.NET. . 518400 IN NS G.ROOT-SERVERS.NET. . 518400 IN NS F.ROOT-SERVERS.NET. . 518400 IN NS B.ROOT-SERVERS.NET. . 518400 IN NS J.ROOT-SERVERS.NET. . 518400 IN NS K.ROOT-SERVERS.NET. . 518400 IN NS L.ROOT-SERVERS.NET. . 518400 IN NS M.ROOT-SERVERS.NET. . 518400 IN NS I.ROOT-SERVERS.NET. . 518400 IN NS E.ROOT-SERVERS.NET. . 518400 IN NS D.ROOT-SERVERS.NET. ;; Query time: 204 msec ;; SERVER: 192.168.0.5#53(192.168.0.5) ;; WHEN: Thu May 4 13:39:01 2006 ;; MSG SIZE rcvd: 228
で、ここからが本題。 djbdnsに含まれるdnscacheが参照するルートサーバ一覧は $(ROOT)/servers/@ というファイルに記述されているのだが、 標準の tarball からインストールした場合、 古いアドレスが含まれたものが使われてしまう。 これを手軽に更新したい。
% dnsqr ns . |awk '$1~/^answer:/{print $5}'|xargs dnsip|tr -d ' '
128.63.2.53
192.36.148.17
192.58.128.30
193.0.14.129
198.32.64.12
202.12.27.33
198.41.0.4
192.228.79.201
192.33.4.12
128.8.10.90
192.203.230.10
192.5.5.241
192.112.36.4
こんなワンライナーでイケる。 ちなみにBINDで使うルートゾーンファイルは オフィシャルなもの が存在するのでそれをとってくればよい。
DNS関係の診断ツールとしては、digの方が標準的だとは思うが、 個人的にはdjbdnsに含まれるツール類の方が単純明快で好きだ。 dnscacheやtinydnsを使うのだけがdjbdnsを導入する理由ではない。
--び
There is no comment.
2006-05-02 [Gauche] tr もどきをGaucheで書いてみた
2006-04-28 の tr もどきをGaucheで書いてみた. 短いですねぇ...
(use gauche.collection) (define ((add-entry k v s) q) (if (char=? k q) v (s q))) (define (tr set1 set2 str) (coerce-to <string> (map (fold add-entry identity set1 set2) str)))
あっ.add-entry の定義の記法は,Gauche-0.8.7 では
- トップレベルでは使えるけど,内部定義には使えない
- なにより,正式にサポートされている機能ではなく,将来にわたって使えることは保証されていない.
ので注意.
--nobsun
(use text.tr) …では、趣旨に外れますかな。
2006-05-01 [Haskell] ポイントフリースタイル
関数を定義するとき,仮引数を使わずに既存の関数を組合せるだけで 関数を定義するスタイルのことをポイントフリースタイルといいます.
自明な例は
foo x = bar x
なら
foo = bar
です.また,
hoge x = huga (hage x)
なら
hoge x = (huga . hage) x
なので,
hoge = huga. hage
と書けます.
2006-04-28の tr ポイントフリースタイルに変換してみよう.
tr set1 set2 = map trans
↓ trans を定義で展開する
tr set1 set2 = map (foldl (flip $ uncurry add) id (zip set1 set2))
↓ まず set2 をはじきだす.
tr set1 set2 = (map . (foldl (flip $ uncurry add) id)) ((zip set1) set2)
↓ もう一息
tr set1 set2 = ((map . (foldl (flip $ uncurry add) id)) . (zip set1)) set2
↓ set2 を消す
tr set1 = (map . foldl (flip $ uncurry add) id) . (zip set1)
↓ つぎに set1 をはじきだす.関数合成演算子をセクションにして前置
tr set1 = (.) (map . foldl (flip $ uncurry add) id) (zip set1)
↓ もう一息
tr set1 = ((.) (map . foldl (flip $ uncurry add) id) . zip) set1
↓ set1 を消す
tr = (.) (map . foldl (flip $ uncurry add) id) . zip
ポイントフリースタイルになった定義をぐっとにらんで何をやっているか すぐに理解できればあなたはもう上級者(何の!?).
--nobsun
すっきり書けますねぇ。やってみました。 http://d.hatena.ne.jp/takatoh/20060509
set1 をはじき出すところで関数合成演算子をセクションにして
前置していますが,この変換は括弧のネストが深くならないように
するためです.括弧のネストを気にしないのなら,
x + y ==> (+) x y という変換のかわりに
x + y ==> (x +) y という部分適用セクションを使った変換ができます.
それを適用すると
tr set1 = (map . foldl (flip $ uncurry add) id) . (zip set1)
↓ セクション
tr set1 = ((map . foldl (flip $ uncurry add) id) .) (zip set1)
↓
tr set1 = (((map . foldl (flip $ uncurry add) id) .) . zip) set1
↓ set1 を消す
tr = ((map . foldl (flip $ uncurry add) id) .) . zip
There is no comment.