|
W/O index |
PAM 認証(Tue Oct 05 2004)
さりとてもちろんクラックはダメです。 mingetty(8) が login(8) を起動するところまでは以前見ましたので、続いて login(8) が PAM (Pluggable Authentication Module) を用いて認証をするところを見ようと思います。PAM の目的は、認証を行うコードの共通化。実際に認証処理を行なう個々のコードをモジュール化(多くは /lib/security/pam_*.so にあるシェアドライブラリ)する。そして、認証ポリシーの異なるアプリケーションごとに、必要とする 0 個以上の認証を /etc/pam.d/* に記述することで、実行時にポリシーを変更することも可能になるわけですな。 以下は util-linux-2.11z の login-utils/login.c。
...
#define PAM_MAX_LOGIN_TRIES 3
...
static char * username;
...
int
main (int argc, char * * argv) {
pam_handle_t *pamh = NULL;
struct pam_conv conv = { misc_conv, NULL };
// オプションを解析したあとで、
while ((ch = getopt(argc, argv, "fh:p")) != -1) {
...
}
argc -= optind;
argv += optind;
// ユーザ名を獲得します。
if (* argv) {
username = strdup(* argv);
}
...
// 第一引数を設定名にして、認証プロセススタート。
pam_start("login", username, & conv, & pamh);
// 各種パラメータを設定して、
pam_set_item(pamh, PAM_RHOST, hostname);
pam_set_item(pamh, PAM_TTY, tty_name);
pam_set_item(pamh, PAM_USER_PROMPT, _("login: "));
...
if (!username)
pam_set_item(pamh, PAM_USER, NULL);
// ハイ、ここで認証。
retcode = pam_authenticate(pamh, 0);
// ダメなら 3 回繰り返し。
while((failcount ++ < PAM_MAX_LOGIN_TRIES) && (
(retcode == PAM_AUTH_ERR) ||
(retcode == PAM_USER_UNKNOWN) ||
(retcode == PAM_CRED_INSUFFICIENT) ||
(retcode == PAM_AUTHINFO_UNAVAIL) )) {
fprintf(stderr,_("Login incorrect\n\n"));
// 一度目のプロンプトは mingetty(8) が出したので、
// 次からは PAM で出します。
pam_set_item(pamh, PAM_USER, NULL);
// で、いま一度認証。
retcode = pam_authenticate(pamh, 0);
}
// それでもダメだったら、
if (retcode != PAM_SUCCESS) {
...
// おしまい、それっきり。
fprintf(stderr,_("\nLogin incorrect\n"));
pam_end(pamh, retcode);
exit(0);
}
// 以下、成功時。
...
// fork(2) して、親は子の終了を待ち続ける。
if (fork() > 0) {
...
wait(NULL);
PAM_END;
exit(0);
}
// 子はシェルへ exec(2) 実行。
...
execvp(childArgv[0], childArgv + 1);
}
ほな、次は PAM が設定を読み込む箇所と、モジュールのコードを呼び出すところ。Linux-PAM-0.77/dynamic.pam.c。
int pam_start(const char *service_name, const char *user,
const struct pam_conv *pam_conversation,
pam_handle_t **pamh)
{
static int (*real_pam_start)(const char *, const char *,
const struct pam_conv *,
pam_handle_t **);
CONFIRM_PAM_FUNCTION(pam_start, int, (const char *, const char *,
const struct pam_conv *,
pam_handle_t **), PAM_ABORT);
return real_pam_start(service_name, user, pam_conversation, pamh);
}
consolehelper(8) は、"-w" オプション + argv[0] をつけた userhelper(8) へ exec(2) するためだけの、薄いラッパー。「/usr/bin/halt -> consolehelper で /sbin/halt を起動する」などのように、リンクを consolehelper(8) にかけておけばいい。userhelper(8) の利用時、console.apps(5) (/etc/security/console.apps/) 内のファイルの存在は pam_console(8) が、内容は userhelper(8) が用いる。console.apps(5) の man によると、pam_console(8) は内容を無視するので、各アプリケーションが自由に使ってよいとある。
LKCD 診断(Mon Sep 27 2004)
いやそこは「ガッ」……。 本来的な意味での "NULL Pointer" フォルトとか、たまーに起きますので、そこで LKCD です。クラッシュダンプの書き出し先は、かならずしも swap でなくとも、任意のブロックデバイスを /dev/vmdump にリンクしておけば、lkcd(8)(あるいは lkcd_config(8)?)がそちらを開いて、ioctl(2) でそちらを使うようにセットするらしい(rc.sysinit が swap にリンクするようですが)。ネットダンプもできるらしいけれど、今回はパスで。 カーネルパニックで、トレースの練習SysRq 機能を使うことでカーネルパニックをひきおこして、LKCD を試してみます。チュートリアル(PDF)より引き写しなんですが、文中「Turbolinux では」とかあるけれど、ドキュメントが古いのか、Turbolinux 10 固有なのか、パス、コマンド名、SysRq のキーが微妙に異なります。
# ls -l /proc/sys/kernel/dump/ # LKCD の機能中なるを確認 total 0 -r--r--r-- 1 root root 0 Sep 27 05:24 addr -rw-r--r-- 1 root root 0 Sep 27 05:24 compress -rw-r--r-- 1 root root 0 Sep 27 05:24 device -rw-r--r-- 1 root root 0 Sep 27 05:24 flags -rw-r--r-- 1 root root 0 Sep 27 05:24 level # echo 1 > /proc/sys/kernel/sysrq # SysRq 機能をオンに # chvt 1 # X からでは SysRq 使えません そして、Alt-SysRq-S (Sync: Emergency Sync), Alt-SysRq-U (Unmount: Emergency Remount R/O), Alt-SysRq-X (paniceXecute: Call Panic Function) を押して、カネパニらせます。ん、swap 小さかったかな?(メモリを gzip して格納できるくらいの swap が必要らしい) しかしヘッダ(とは何だ?)しか書き出してくれない。どうやら、マシンが悪いんだ、こいつ LKCD 使えない。しかたがないので、マシン交換、こんどこそキター。 Dumping to block device (3,1) on CPU 0 / ← くるくるくる そして勝手にリブート。起動後、以下のような手順でログを解析。 # cd /var/log/dump/0 ← 数字は適宜変更のこと # lcrash -n 0 ... >> rd ← なにはともあれレジスタダンプ CPU: 0 EIP: 0060:[<c01fb3d0>] ← ここで死んだのね EFLAGS: 00010246 eax: 00000006 ebx: c03035cc ecx: c034be90 edx: c02c4790 esi: 00000078 edi: c15fd000 ebp: c034bfb0 esp: c034be7c ds: 007b es: 007b ss: 0068 いきなりそこを見に行ってもいいんですが、それではつまらないので、つづいてスタックトレース。 >> bt ============================================================ STACK TRACE FOR TASK: 0xc02f1a80(swapper) 0 __handle_sysrq+92 [0xc01fb59c] ← おケツ 1 kbd_event+37 [0xc01f5585] 2 input_event+193 [0xc023f061] 3 atkbd_report_key+39 [0xc0241fc7] 4 atkbd_interrupt+485 [0xc02421e5] 5 serio_interrupt+97 [0xc0243521] 6 i8042_interrupt+139 [0xc0243bcb] 7 handle_IRQ_event+49 [0xc01081d1] 8 do_IRQ+112 [0xc0108480] 9 common_interrupt+24 [0xc0106be8] 10 default_idle [0xc0104030] 11 default_idle+36 [0xc0104054] 12 cpu_idle+37 [0xc01040c5] 13 start_kernel+366 [0xc034c72e] 14 L6 [0xc010019f] ============================================================"+92" が戻りアドレスなんだから、コケているのはその一つ前ですね。ではその周辺を逆アセンブル。 >> dis __handle_sysrq+87 10 0xc01fb597 <__handle_sysrq+87>: pushl %edi 0xc01fb598 <__handle_sysrq+88>: pushl %ebp 0xc01fb599 <__handle_sysrq+89>: pushl %esi 0xc01fb59a <__handle_sysrq+90>: call *(%ebx) 0xc01fb59c <__handle_sysrq+92>: addl $0x14,%esp 0xc01fb59f <__handle_sysrq+95>: popl %ebx 0xc01fb5a0 <__handle_sysrq+96>: popl %ebx 0xc01fb5a1 <__handle_sysrq+97>: popl %esi 0xc01fb5a2 <__handle_sysrq+98>: popl %edi 0xc01fb5a3 <__handle_sysrq+99>: popl %ebp >> rd CPU: 0 EIP: 0060:[<c01fb3d0>] EFLAGS: 00010246 eax: 00000006 ebx: c03035cc ecx: c034be90 edx: c02c4790 esi: 00000078 edi: c15fd000 ebp: c034bfb0 esp: c034be7c ds: 007b es: 007b ss: 0068 >> dump c03035cc 1 0xc03035cc: c01fb3d0 : .... ← ここへ飛んだらしい EIP が指しているのも、まさにここですね。 >> dis c01fb3d0 5 0xc01fb3d0 <sysrq_handle_panicexec>: movb $0x0,0x0 ← うげ 0xc01fb3d7 <sysrq_handle_panicexec+7>: ret 0xc01fb3d8 <sysrq_handle_panicexec+8>: nop 0xc01fb3d9 <sysrq_handle_panicexec+9>: leal 0x0(%esi,1),%esi 0xc01fb3e0 <sysrq_handle_showregs>: movl 0x8(%esp,1),%eax >> では "sysrq_handle_panicexec" を探しに行く。 $ cd /usr/src/linux/ $ find . -follow -name "*.c" | \ xargs grep -l sysrq_handle_panicexec ./drivers/char/sysrq.c $ ではそちらへ。
static void sysrq_handle_panicexec (int key,
struct pt_regs *pt_regs, struct tty_struct *tty ) {
char *aaa = NULL;
*aaa = 0;
}
そりゃ死ぬわな。 初期化プロセスのしくみvmdump(8) は lkcd(8) へのリンクで、シェルスクリプト。 $ rpm -qf /sbin/vmdump lkcdutils-4.2-2 $ \ls -l /sbin/vmdump lrwxrwxrwx 1 root root 4 Sep 14 14:17 /sbin/vmdump -> lkcd $ file /sbin/lkcd /sbin/lkcd: Bourne shell script text executable $ /etc/rc.sysinit より。
# LKCD ナシの環境では、
if [ ! -x /sbin/vmdump ]
then
# とっとと swapon(8)
swapon -a
fi
...
# アリの環境では、
if [ -x /sbin/vmdump ]
then
if [ -e /dev/vmdump ]
then
# ここで LKCD を発動し、
/sbin/vmdump config
# もしもダンプデータがあるようであれば、保存
/sbin/vmdump save
fi
# ようやく swapon(8)
swapon -a
fi
lkcd_config(8)(man には 1 とあるけど、8 だよな)。 #define DUMP_DEVICE "/dev/dump" ... S.M.A.R.T.(Sun Sep 26 2004)
こわれかけのディスク〜♪(by 徳永) ハードディスクが逝きかけな駆動音は、黒板を爪でひっかく音級にイヤな音なわけでして。smartctl(8), smartmontools ちゃんとしかけておきましょう。smartsuite は、ライセンスがフリーではないようですので、ひとまずパス。 ひとまず man で smartd.conf(5) を確認するのですが、とりあえず "DEVICESCAN" の行を "DEVICESCAN -m root@example.com" のように管理者宛メールアドレスを設定してからサービス上げておけば、何かあると言ってくるのでしょう(いいかげん……)。 ひとまず、どうやったら Linux 上で S.M.A.R.T. 情報が取得できるのかが分からないと落ち着かないので、コードを追ってみる。open(2) してからしかるべき ioctl(2) をかけてやるとドライバが答えてくれるようですね。 smartmontools-5.30。ataprint.c:ataPrintMain(int fd) → atacmds.c:ataReadHDIdentity() → smartcommandhandler()。そして os_linux.c:ata_command_interface() と escalade_command_interface()(3Ware Escalade 用)。 /usr/include/linux-glibc/hdreg.h あります。 #ifdef HAVE_LINUX_HDREG_H #include さて、カーネルは kernel-2.6。drivers/ide/ide.c:generic_ide_ioctl()。
int generic_ide_ioctl(struct file *file, struct block_device *bdev,
unsigned int cmd, unsigned long arg)
...
switch (cmd) {
...
case HDIO_DRIVE_CMD:
return ide_cmd_ioctl(drive, cmd, arg);
...
}
}
そして ide-taskfile.c:ide_cmd_ioctl() → ide-io.c:ide_do_drive_cmd()。 なにげに /proc/ide/ide*/*/smart_thresholds や /proc/ide/ide*/*/smart_values にも何か出ているけれど、smartmontools は使っていないようですね。
PPP は何ではないのか(Thu Sep 23 2004)
# OSI 参照モデル。データリンク層のプロトコルである PPP (RFC1331。データリンク層の LCP と、ネットワーク層の NCP?)。物理層に Ehter を使うプロトコルが PPPoE (RFC2516)。当然 Point-to-Point なのだが。SLIP は IP にしか対応しない。
Line discipline て?()
「ライン・ディシプリン: 回線規約。通信プロトコルの古い呼び名」(某通信用語事典)……いや、そうかも知れませんが、そうじゃなくて、tty に関係するやつ。 tty はキャラクタデバイスです。オブジェクト指向的に言うと、"is a" です。 stty(1), Line discipline: ライン制御とも。疑似端末ならばいくらでもあるのに、そもそも「疑似じゃない」端末とは? 端末制御とかがないとどうなるんだ? 端末デバイスドライバ、ラインディシプリン。カノニカルモード。非カノニカルモード(= raw モード)。 そういえば /dev/pts/ の devpts って誰がマウントするんだっけ? ……って、/etc/fstab に書いてあるじゃん。fs/devpts/, getpt(3)。"pseudo terminal" は、何を以って "pseudo" なのか? じゃ、もともとの "pseudo" じゃない端末ってどれ? じゃあ、xterm(1) とかは、えー……何なんだ? 端末 ← ケーブル → シリアルポート ← シリアルドライバ → アプリケーション xterm(1) ← 疑似 tty → アプリケーション 何らかの tty ← screen(1)・複数の疑似 tty → アプリケーション ICANON (Input Canonicalization)
inittab(5) には以下のようにあり、 1:2345:respawn:/sbin/mingetty vc/1 実体は以下のよう。 $ \ls -lF /dev/vc/1 lrwxrwxrwx 1 root root 7 Sep 14 13:30 1 -> ../tty1 で、mingetty(8) なのじゃが……、
fd = open ("/dev/vc/1", O_RDWR, 0);
ioctl(fd, TIOCSCTTY, (void *) 1);
dup2(fd, 0);
dup2(fd, 1);
dup2(fd, 2);
// 最初のログインプロンプトで、ユーザ名を取得。
logname = get_logname();
// あとは login(1) がやってくれる。
// ログイン失敗以降の "login:" は login(1) が出す。
exec("/bin/login", "/bin/login", "--", logname, NULL);
そうか、fork(2) したプロセスから使いたい tty を開いて、stdin, stdout, stderr を用意してから exec(2) すればいいだけか。以上は、pseudo じゃないターミナルの場合。pseudo terminal は、そのこちら側と向こう側(?)の両方を面倒見るのかな?(→ screen(1) の項) # iSeries(旧 AS/400)につながった、IBM infoWindow II 3486 のグリーンな端末
ATA RAID on 2.6 カーネル Linux(Wed Sep 22 2004)ついに SATA が使えるようになってきましたね(sata_sil.ko だけちょっとあぶなっかしいけど……)。 で、問題の SATA RAID です。ICH5 (ata_piix.ko) 上で isw (Intel Software RAID) の RAID1 を組むのに使用したディスクを用いると、Promise (sata_promise.ko) 上で再度 pdc (Promise FastTrack) の RAID1 を組んでも、dmraid(8)(RC4 を使用)の discovery ルーチンは isw として認識してしまう。正しいのかどうかわからないけれど、isw のメタデータは、以下のようにケツの 10000 セクタほど削ってやったら消えた(Silicon Image が最後まで残る……)。ともかく、ATA RAID のメタ情報は後ろのほうにありますので、RAID1 であれば、前のほうのパーティションはそのままマウントすることもできます。 $ dev=sda $ count=10000 $ seek=$((`cat /sys/block/$dev/size` - $count)) $ dd if=/dev/zero of=/dev/$dev seek=$seek count=$count Linux でしか使わないならば、素直に MD RAID にしたほうがトラブル少ないと思います。 あと、"stride" と「サイズ」の関係がよく分からない。なんぼ使えるの? 以下、ICH5 での結果。 $ dmraid -s -ccc isw_dbfjhbejbc_RAID_Volume1,156300800,128,mirror,operational,2 /dev/sda,isw_dbfjhbejbc_RAID_Volume1,isw,mirror,156300800,0 /dev/sdb,isw_dbfjhbejbc_RAID_Volume1,isw,mirror,156300800,0 $ sfdisk -g /dev/sda /dev/sda: 9964 cylinders, 255 heads, 63 sectors/track $ sfdisk -g /dev/sdb /dev/sdb: 9729 cylinders, 255 heads, 63 sectors/track そして、Promise (PDC 20378 SATA 150)での結果。 $ dmraid -s -ccc pdc_cdfedbhgac,78125000,256,mirror,operational,2 /dev/sda,pdc_cdfedbhgac,pdc,mirror,78125000,0 /dev/sdb,pdc_cdfedbhgac,pdc,mirror,78125000,0 $ sfdisk -g /dev/sda /dev/sda: 9729 cylinders, 255 heads, 63 sectors/track $ sfdisk -g /dev/sdb /dev/sdb: 9964 cylinders, 255 heads, 63 sectors/track 「サイズ」に stride / 256 をかければ KB 単位になるようでもあるが……正しいのかな? ドキュメントにもありまくわしく書かれていないし、ソース読めってかな。device mapper 関連も読まなきゃと思っていたところだし、いい機会か。 Syslog ちゃんと使おう(Tue Sep 21 2004)
……まてません。ちなみに syslog.conf(5) でファイル名に "-" を前置すると fsync(2) しなくなるので、メールサーバやルータなどログの量が多い環境では、少しは楽にしてあげられます(非同期なので、もちろん落ちたときには取りこぼしますが)。ロギングが大変でスローダウンなんて、クラッシュの原因になるノートン・クラッシュガード並に悪です。高負荷時に fsync(2) しない、という設定があればいいのにね。 以前、chroot 環境で動かしたコマンドが、画面にメッセージをぞろぞろーと吐いたので、なるほど syslog(3) はユーザ空間で働くのだな、と気づいた次第。syslogd(8) は、/dev/log の UNIX ドメインソケットからメッセージを読み込む模様。 $ \ls -lF /dev/log srw-rw-rw- 1 root root 0 7月 21日 12:12 /dev/log= しかしなぜ /dev/ にある? /var/ ならともかく。今は libc がユーザ空間でまかなっているけれど、以前はシステムコールだった、とかいう歴史的経緯がある? FHS でも、恒常的なソケットや名前つきパイプは /dev/ 以下にあってもヨシ、ということで /dev/printer, /dev/log, /dev/gpmdata が例外的に挙がってます。VFS 的にはデバイスファイルもパイプも本質的には大差ない、ちゅうことか。 syslog(2), klogd(8), /proc/kmsg。インストーラでは、/proc/kmsg を開いて、/tmp/syslog と /dev/tty4 にリダイレクトするプロセスを fork(2) する。通常環境では、/proc/kmsg を開いて、読み込んだ内容を(優先度レベル書きかえの前処理してから)syslog(3) する klogd(8) がある。 ソースを追って、分かった気になってみるとりあえず読んでおくか。以下参照するのは、Turbolinux パッチドな sysklogd-1.4.1 の syslogd.c ですが、適宜本質的でない部分は省略しつつ引用します。 まずはソケットの定義ですが、
#define MAXFUNIX 20
char *funixn[MAXFUNIX] = { "/dev/log" };
int funix[MAXFUNIX] = { -1, };
int nfunix = 1;
ですって。funixn は名前の、funix はデスクリプタの配列ですね。引数で追加・上書きできます。
case 'a':
if (nfunix < MAXFUNIX)
funixn[nfunix++] = optarg;
else
fprintf(stderr, "Out of descriptors, ignoring %\
break;
case 'p': /* path to regular log socket */
funixn[0] = optarg;
break;
その後で、syslogd.c:init()。ここで UNIX ドメインソケットや、リモートロギング用のインターネットソケットを開きます。
for (i = 0; i < nfunix; i++) {
funix[i] = create_unix_socket(funixn[i]);
}
if (Forwarding || AcceptRemote) {
finet = create_inet_socket();
if (finet >= 0) {
InetInuse = 1;
}
}
あとは select(2) で待ちつつ、ループする。
for (;;) {
...
for (i = 0; i < nfunix; i++) {
if (funix[i] != -1) {
FD_SET(funix[i], &readfds);
if (funix[i]>maxfds) maxfds=funix[i];
}
}
// リモートの分
if ( InetInuse && AcceptRemote ) {
FD_SET(inetm, &readfds);
if (inetm>maxfds) maxfds=inetm;
}
...
nfds = select(maxfds+1, (fd_set *) &readfds, (fd_set *) NULL,
(fd_set *) NULL, (struct timeval *) NULL);
...
}
メッセージが来る度に syslogd.c:printchopped() に渡して、sanity チェックの後、 /etc/syslog.conf, "selector action" であり、selector := facility "." priority である。facility は、openlog(3) で指定できる。 void openlog(const char *ident, int option, int facility); void syslog(int priority, const char *format, ...); void closelog(void); facility := auth | auth-priv | cron | daemon | kern | lpr | mail | news | syslog | user | uucp | local0 〜 local7 低い順に priority := debug | info | notice | warning | err | crit | alert | emerg 。設定されたレベルよりも高いメッセージがロギングされる。 action := ログ用通常ファイル名 | named パイプ | コンソールデバイス | リモートホスト (@ を前置) | ユーザへメール (ユーザ名) | 緊急なので全ユーザに (アスタリスク *) glibc のビルドシステムがよく分かっていないので何ですが、多分 glibc-*/misc/syslog.c がそれでしょう。/usr/include/sys/syslog.h で '#define _PATH_LOG "/dev/log"' ですので、どこでソケットをオープンしているかは簡単に分かります。 うん、これくらい読むと、何となく分かった気分になるよね(笑)。
今さら screen(1), そして疑似端末(Sun Sep 19 2004)
最近、screen(1) を使い始めました。場所をとらずに複数のコンソールを使いたい場合、gnome-terminal(1) や konsole(1) は、GUI のタブもついていてとてもステキなのですが、いかんせん X, Gnome, KDE あたりに依存しているので、クライアント、サーバともにそれなりのモノを要求しますし、クライアントが Windows 上の SSH ターミナルだったりすると、当然のことながら利用できません(VeraTerm なるシェアウェアで、タブ型 ssh クライアントあります)。どうしようかと思ううち、そういえば screen(1) があるではないかと思い至った次第。 まずは、デフォルトのエスケープである "Ctrl-A" の変更から。使いづらすぎます、薬指つるよ。とはいえ、どこに割り当てても Emacs 等のキーアサインとぶつかるのですよ、Alt-* は使えませんし。試行錯誤の結果、"Ctrl-_" (.screenrc 的には、"escape ^__") に割り当て。するとなぜか Ctrl-/ で使えるようになるので具合が良い。ホームポジションもあまりくずれませんし、これはこれでよし、と。 あとは、.bashrc で、screen(1) 環境下にあっ($TERM が "screen" になる)たら、プロンプトを変更するようにしました。ウィンドウ番号をプロンプトに出す手だてはないかな? あった、環境変数 $WINDOW に、初期のウィンドウ番号が入ってきます。 screen セッションのアタッチとデタッチ、"-X" オプション、それと $STY, $WINDOW などを使うと、いろいろおもしろそうです。ウィンドウ番号とウィンドウ用途の対応を固定すれば能率上がるだろうな。それと、会社で開きっぱなしで帰ったコンソール(メーラとか上げっぱなしで帰りがち)を、家から開くことができる! これができないものかと、ずーっと思っていたんですが、意外なところに答えが。 とりあえず 8 と 9 は、以下のように、家用と会社用のメーラで固定です。
$ screen -X screen -t home 8 mutt -f "{home}inbox"
$ screen -X screen -t office 9 mutt -f "{office}inbox"
ダイヤル式の電話といっしょで、キーボードの場合は 0 → 1 → 2 より 1 → 2 → 3 のほうがしっくり来るんだよね。window.c 内 struct NewWindow nwin_default の "StartAt" を "1" にすれば、ウィンドウは 0 からではなく 1 から振られる。あいにく、ランタイムに変更する手はないようだ。 疑似端末の使い方疑似端末を用いる実装ってどうやるの? window.c:OpenDevice() → pty.c:OpenPTY() で、オープンされた疑似端末のディスクリプタが返る。int window.h:(struct win).w_ptyfd に入る。OpenDevice() は、戻り値には PTM (pseudo terminal master, char major 2) のディスクリプタが、最終引数のポインタには、対応する PTS (pseudo terminal slave, char major 3, ptsname(3) で取得) の名前が返る。screen(1) 側では PTM を用いて疑似デバイスを実装し、子プロセス("window" と呼称?)の側では PTS が制御端末になる。
int OpenPTY (char **ttyn) {
static char PtyName[32];
...
#if defined(HAVE_GETPT) && defined(linux)
f = getpt();
#else
f = open("/dev/ptmx", O_RDWR | O_NOCTTY);
#endif
...
strncpy(TtyName, ptsname(f), sizeof(TtyName));
*ttyn = TtyName;
return f;
}
screen-*/window.c:ForkWindow() でシェルを exec(2) してるみたいだな。
switch (pid = fork()) {
case -1:
break;
case 0: {
close(0);
close(1);
close(2);
newfd = open(さっきの PTS);
dup(0);
dup(0);
// 何かいろいろ
...
execve(proc, args, NewEnv);
}
default:
break
}
カーネルの用意している「実端末」のかわりに、アプリケーションのレベルで「疑似端末(pseudo terminal)」のマスター(PTM)を開き、子プロセスは、fork(2) して、対応するスレーブ(PTS)を開いて、dup(2) って、exec(2) すればよいのですね。screen(1) や xterm(1) とその親戚, kon(1) などのたぐいは、みなこのようなことをやっているんでしょう。 /proc/ で見ると、"SCREEN" プロセスが /dev/ptmx を開いていて、子プロセスたちが /dev/pts/* を開いている。
Postfix のソース読み、とりあえずコア編(Sat Sep 18 2004)書いてる中
構造が(巨大だけど)一枚岩だった Sendmail にくらべると、より新しい世代の MTA (+ MDA ?) に属する qmail や Postfix は、セキュリティ的 and ソフトウェア工学的観点からモジュール化が進んでいるので、仕組みを知らないと、問題が起きた時にどこを見ればよいのかがよく分かりません。ナニがアレな qmail はともかく、Postfix はおさえておくべきでしょう。 以下ソースは、Turbolinux パッチドな postfix-2.1.4 です。 まずは README_FILES/OVERVIEW(あるいは web 版)を見るべし。これ見てからソース読んで、また見るとよく分かります。あぅ、遅まきながらいいページ発見―― Postfix Anatomy - Receiving Mail(和訳) 。 Postfix のシステムがすることは、入ってきたメッセージを、ローカルファイルシステム上の複数キュー間で回して、そのうち、しかるべき先へ向けて出す。本質的にはそれだけ。キューは /var/spool/postfix/ にある、ただのディレクトリです。 通常、resident なプロセスは三つだけです――ポートを監視する master(8), ローカルからの maildrop キューを監視する pickup(8), 配信される active キューを監視する qmgr(8)。ポート or キューを監視するプロセス以外は、オンデマンドで master(8) から上がってきます。ポートを開くプロセス以外では root 権限はいらないし、メインのキューを扱う postfix グループと、maildrop 系のキューを扱う postdrop グループとで権限が分かれているのも、いい感じですね。 postfix(1) はサービス起動のメインコマンドです。そのサブコマンド("start" とか "stop" とか)を実際に解釈するのは、postfix(1) から exec(2) される conf/postfix-script というシェルスクリプトで、postfix(1) は、postfix-script の存在するディレクトリの変更や sanity check の類をしているもよう。そのへんの流れは、src/postfix/postfix.c あたりを見ると分かります。 ここからが本番。"postfix-script start" で、ついに本体――master(8) のプロセスが上がります。とはいえ master(8) は、とても一般的な、本質的には単にポートとプロセスを管理するだけのプロセスです。どうりでソース上で、"smtpd" とか "qmgr" とかで grep しても何も出ないわけです、それらはぜんぶ "master.cf" に書かれた設定にすぎないのですから。ポート管理部分は、言うなれば inetd(8) や xinetd(8) のようなスーパーサーバーであって、smtpd(8) などは単一コネクションを扱うサーバとして、要求があったら上がってきます。これで、root で動くのは master(8) だけでいいことになり、セキュリティ的にもリソース的にも好ましいのですね。master.cf のパースルーチンは、master_conf.c あたり。ポートを開いている master/master_listen.c:master_listen_init() にも注目。 "inet_interfaces = all" をするのは master(8) のはずだから、master(8) もも main.cf を見ているはずなんだけど、どこで見てるんだ? 他のサブ・デーモンたちは、master/ 以下の共通 main() から、global/mail_conf.c:mail_conf_suck() で dict_load_file() しているんですが。……マクロと即値が入り乱れて、grep しづらい……あった、master/master_vars.c:master_vars_init() の mail_conf_read() です。 master.cf で、インターネットのホスト:ポート指定でホストが省略されるた場合、オープンされるソケットのホストは、main.cf の $inet_interfaces に従います(上記 master/master_lsten.c:master_listen_init() あたりを見るべし)。以下、一部抜粋。 # ===================================================================== # service type private unpriv chroot wakeup maxproc command + args # (yes) (yes) (yes) (never) (100) # ===================================================================== 127.0.0.1:smtp inet n - n - - smtpd qmgr fifo n - n 300 1 qmgr # 当初私が、ドキュメントも見ずに、全インターフェイスの分だけ master.cf に smtpd のエントリを書いて 25 番を開けていたのは秘密です。main.cf でできるっての、基本的に(マイナーなデーモン上げたいとき以外は)、master.cf はいじらなくていい main.cf は、最低限で以下くらいをいじっておけば、とりあえずは普通に動きます――myhostname(サーバの FQDN), mydomain(省略で、$myhostname からホスト名抜いたもの), myorigin(ローカル配送時に補われる @ 以下、省略で $myhostname), inet_interfaces(もちろん "ALL"), mydestination(受信を許可するドメイン名、"$mydomain" とか "example.com" とか), mynetworks_style, mynetworks, relay_domain(以上三つはリレーの設定)。 さて、src/master/ にある、サブデーモンの共通エントリポイント(三種)がよくわかりません。netstat(8) で見て、大量の UNIX ドメインソケットが開かれているところを見ると、起動後も何らかの IPC してるんでしょうけど。 # QMQP (Quick Mail Querying Protocol), 高レイテンシなコネクションにおいて SMTP より速いメール転送プロトコルらしいけど、本質的でなさそうなので、とりあえずあとまわし。LMTP (Local Mail Transfer Protocol): あとまわし
suEXEC(Tue Aug 24 2004)# ls -l /usr/sbin/suexec -rwsr-x--- 1 root www 13048 4月 6 14:58 /usr/sbin/suexec* # grep -e ^User -e ^Group /etc/httpd/conf/httpd.conf User www Group www # quota の warning(Mon May 17 2004)この警告(↓)は何なのかしら、とソースをあたってみる。 # touch /boot/aquota.user /boot/aquota.group # quotacheck -ug /boot quotacheck: WARNING - Quotafile /boot/aquota.user was probably \ truncated. Can't save quota settings... quotacheck: WARNING - Quotafile /boot/aquota.group was probably \ truncated. Can't save quota settings... 答: touch が不要なのです。(V2 になってから?)ファイルはツールに作らせるのが正しい。上記は「既存のファイル(aquota.user, aquota.group)は読めないので消すぞー」の意味。害はない。 軽量 TL10D(Sat May 01 2004)# 昨年末から PHP, Perl 漬け。他にもいろいろやらねばイカンです うちのサブノート(FMV BIBLO LOOX S9/70)は、小さくて電池がもつのはとてもよいのですが、いかんせん遅いのです。
最近の CPU(Crusoe TM5500 700MHz)のクセに、下手すりゃ PenII に負けそうなくらいです(Transmeta の経営が傾くわけである)。「軽さの Linux」とは言っても、Turbolinux 10 desktop のような最近のディストリビューションのデフォルト(KDE + Mozilla + XEmacs)などになると、永遠に起動中なんじゃないかと思えるほどです(Linux の Crusoe 最適化がなされていないからではないかと思ったけれど、Windows XP でも大差なかった)。そこで、時代遅れのジジィ呼ばわりされつつ、以下のような軽量モノで固めてみます。
おおー速い速い、思考が妨げられません。アムロで言うと(?)マグネットコーティング的です。 # w3m はさすがにアレなので、Firefox ビルドしてみよ(できるか?) 追記(Sun May 02 2004): Fedora からパチってきたら、わりとあっさりビルドできました。firefox-0.8-2(ソース)。充分速いです。体感的には、Celeron 1GHz 上の Mozilla、に匹敵するくらいの速さかな
| |||||||||||