インターネット・セキュリティ

サーバー構築/運用術
第4回

Perl（後編）

先月号に引き続き，perlを解説します。昨今のディストリビューションは複雑すぎて本質が見えにくくなっています。perlを使って一部を書き直してみることにより，サーバー・システムに対する理解がより深まることでしょう。一例として，syslogの監視とppp接続を行うperlスクリプトを紹介します。
（ケイ・ラボラトリー

仙石 浩明）

先月号に引き続き，昔話をします＊1。

基 礎 を 学 ぶ に は 最 適 の 環 境 でした 。

私が最初に購入したコンピュータは，シROM（SP-1002）はわずかに4Kバイト

ャープのMZ-80K（写真1）シリーズの最だったので，すべて逆アセンブルして解後の機種K2E＊2でした。RAMは32Kバ析することができましたし，ハードウエ

イト，CPUは当時流行だったZilog Z-80

アはCPUなどのLSIを除けばTTLなど

2MHz＊3で，BASICインタプリタをROMのICで大部分が構成されていて，基板ではなく，カセット・テープから読み込むの配線を追って回路図を再構成し，仕方式であるため，BASIC以外の言語へ組みを理解することもできました。そしの対応が容易という，当時としては画期て，CPUクロックの倍速スイッチを付け的＊4なパソコンでした。BASICライクな

写真1 シャープが1978年12月に発売した
MZ-80Kたり，キャラクタしか表示できなかった

アセンブラBASE-80＊5を使って，ゲームディスプレイにグラフィックスを表示できやモニター＊6 を作っていたことを思いるように改造したり，Z-80を使ったコン後継モデルのMZ-80K2Eは，「MZクリーンコンピュータ10万台突破」を記念して発売された。MZ-80KとMZ-80K2Eの外観はほとんど同じだが，ディスプレイのきょう体の色（MZ-80Kは赤，MZ-80K2Eは深緑）やキーボードの色が異なる。

出します。その後，CP/Mを移植するたピュータを手作り＊7していたものです。

めにBIOSを記述する際にも，BASE-80

当時高校生で，論理回路のイロハすを使いました。

ら知らなかった ＊8私にも設計できたのも出ない ＊9ものになってしまったのは残念なことです。

今日の基準からすれば，恐ろしく貧弱は，ひとえに当時のパソコンが単純だっ何を学ぶにしても同じだと思うのでな開発環境で，実際ろくなプログラムはたからです。その後，パソコンは急速にすが，学ぶ対象は単純であればあるほ作れなかったのですが，コンピュータの複雑化・高度化し，初心者には手も足ど望ましいものでしょう。現在のPCの＊1 年をとった証拠ですね。会社の同僚に，先月号に載せたPET2001の写真を見せたら，「コモドール ? 何それ? 1978 年 ? 当時 4 歳でしたよ」などと言われてしまいました。
＊2 定価は14万8000円。キーボードが格子状のレイアウトであり，カセット・デッキが標準装備されている点などが，私が初めて触ったパソコンであるPET 2001と良く似ていたことから引かれたのでしょう。本当はMZ-80Bが欲しかったのですが，定価が27万8000円と高価で，とても手が届きませんでした。
＊3 20MHzの誤植ではありません。
＊4 電源を入れるたびに，1200bpsのカセット・テープからB A S I C インタプリタを読 み 込まなければならず，BASIC全盛の当時は少々無理があり，あまり売れなかったようです。フロッピ・ディスク（当時は5インチ）が普及して，インタプリタ等はディスクから読み込むのが当たり前になるのは数年後のことです。
＊5 キャリー・ラボ製。BASICのような代入文や複文を使うことができたアセンブラです。雑誌に掲載されていたBASE-80の16進ダンプを，自作の16進キーボードを使って（文字通り1バイトずつ）入力しました。あらゆる種類のフリーソフトウエアがインターネットから容易に入手できる今日からは想像もできない時代です。
＊6 メモリーの16進ダンプを行ったり，カセット・テープから任意のメモリー番地へプログラムを読み込んだり，逆にカセット・テープへ出力したり，任意の番地へジャンプさせたりするためのプログラムです。

Nikkei Linux 2001.7

179ハードウエアは，論理回路を学ぶにはを完全に理解しない限り，把握できなくンストールしないよう心掛けてください。

複雑すぎますし，現在のサーバー・シスなってしまいました。サーバー運用管理

そして，何か必要なプログラムがある

テムは，サーバー運用管理を学ぶにはを学ぼうとする管理者にとって，非常にときは，パッケージを入手してrpmコマ複雑すぎます。

不適切な環境と言わざるを得ません。

ンドを使ってインストールするのではなとは言っても，現実に管理すべきサー

く，そのプログラムの開発元が公開してバーがある場合，インストール済みのシいるソース・プログラムを入手し，必ずステムをすべて捨てて単純なディストリ

付属の説明書を熟読した上で適切な設Linuxも，登場当時は単純でした。

ビューションからやり直すわけにはいか定を行ってmakeし，インストールするよLinuxが普及し始めた初期＊10のころのないでしょう。理解できていなくてもうにしてください。/usr/localディレクト

ディストリビューションでは，ブート・ス

GUI管理ツールの助けで一通り設定はリ以下にインストールするようにすれば，クリプト＊11の他は各プログラムごとのできるし，ある程度の管理はできている

既存のパッケージとの競合を最小限に設定ファイルがあるだけで，それぞれのわけですから。

抑えることができるでしょう。

ディストリビューション

プログラムを1つずつ理解していけば，そこでお勧めしたいのが，ディストリあるパッケージをバージョンアップしシステム全体が理解できたものです＊12。

ビューションへの依存を徐々に減らしてようとする場合も，可能であればまずそ

ところが，Linuxの普及にともない，初いく方法です。まず現時点で使用してのパッケージを削除した後に，ソース・心者にも管理できるようにと管理ツールいないパッケージを削除します。Red

プログラムからmakeして/usr/localディのGUI化が進みました。確かに見た目はHat系のディストリビューションであれレクトリ以下にインストールするようにしマウスで操作できるようになり，とっつきば，rpmコマンドを使ってパッケージをてください。多くの場合，パッケージをやすくなったのですが，見えない部分は一覧し（図1），要らなそうなパッケージ削除しようとしてもそのパッケージを必逆に複雑になってしまいました。つまり初の内容を調べ（図2），削除します（図3）。

要とする関連パッケージがあって，削除心者が遭遇するあらゆる事態に対処でもしそのパッケージを必要としているパすることができないのですが，関連パッきるようにするため，そして簡単な操作

ッケージが他にあるのなら（図4），そちケージの内容がすべて把握できて，そで適切な設定 ＊13 ができるようにするたらのパッケージについても必要か否かれらを削除したときの影響が予想できめ，システム全体を統一的に管理する複判断することになります。

るのであれば，思いきって全部削除し，雑な仕掛けが構築されていったのです。

これからLinuxシステムをインストー

各プログラムは互いに密接に連携すルしようとしているのであれば，必要最るようになってしまい，ある設定変更の小限のパッケージのみをインストール

このようにして次第にディストリビュ

影響がどこまで及ぶかは，システム全体し，すぐ使う予定のないパッケージはイーションへの依存度を減らしていけば，＊7 もちろん，今日のPC組み立てとは意味が異なり，ICの足を1本づつワイヤーで半田付けしながら組み立てました。
＊8 大学に入って初めて論理回路設計の基礎を学んだ
とき，設計にかかる時間が，我流の設計手法に比べて1/10以下で済むことを知って，まさに目からうろこ状態でした。
＊9 今では古い規格となってしまった33MHzのPCIバスでさえ，素人が手作りするには動作周波数が高すぎます。

＊10 1993年ごろのことです。Softlanding SoftwareやYggdrasil Computingがディストリビューションを公開していました。
＊11 Linuxシステムがブートするときに実行されるスクリプト。/etc/rc.d/rc.Sなど。詳しくは2001年3月号，pp.110-120の「実戦で学ぶ，一歩進んだサーバー構築術」を参照してください。
＊12 教育用の単純なディストリビューションがあるといいのかも知れません。どなたか作ってみませんか ?

＊13 セキュリティが重視されるようになって以来，あらゆる環境下である程度の安全性を確保するために，ますます複雑になってしまいました。

180

Nikkei Linux 2001.7

必要なものだけソース・プログラムからmakeしてインストールしましょう。

さらに進んだ連載# rpm -qa
setup-2.0.5-2k1
filesystem-1.3.5-1
basesystem-6.0-4
ldconfig-1.9.5-16
glibc-devel-2.1.2-17k5...

図1 パッケージ一覧を表示
このマークで改行# rpm -e mouseconfig

図3 パッケージを削除する

サーバー構築/運用術# rpm -qi mouseconfig
Name: mouseconfig
Relocations: (not relocateable)...
Summary: The Red Hat Linux mouse configuration tool.
Description :Mouseconfig is a text-based mouse configuration tool. Mouseconfig
sets up the files and links needed for configuring and using a mouseon a Red Hat Linux system. The mouseconfig tool can be used to set
the correct mouse type for programs like gpm, and can be used with
Xconfigurator to set up the mouse for the X Window System.

# rpm -e truetype-fonts-ja
error: removing these packages would break dependencies:
truetype-fonts-ja is needed by VFlib-2.24.2-5k4

図 2 パッケージの内容を調べる

図4 このパッケージを必要とする他のパッケージがある場合自分で内容を理解した上でインストールし出してインストールしても良いのですを例にperlスクリプトの書き方を紹介ししたプログラムが大半になるでしょうかが，常に希望通りのプログラムが見つます。実を言えば，syslog監視を行うプ

ら，システム全体の動きも自然と見えてかるとは限りませんから，サーバー管理ログラムとしてはswatch＊14が有名ですくることでしょう。ある設定ファイルを変者を目指す読者の皆さんには，ぜひごし，ppp接続のためのツールとしてRed更したら，意図しなかったプログラムに自身でプログラムを作ってみることをおHat系ディストリビューションにはnetcfgまで影響が及んだ，などということは防勧めします。実際にプログラムを作るこが標準で用意されていますが，出来合ぐことができるようになるはずです。とによりシステムに対する理解がより深いのツールを使うよりは，自分でツールまる，というメリットもあります。

を作る方が勉強になります。トラブルが一般に，プログラムは汎用であればあ起きたときも自作のスクリプトならばするほど複雑になり，逆に特定の環境およぐに修正可能ですし，目的に合わせて自システムをある程度理解できるように

び特定の用途向けのものは単純になり由に機能を追加していくことができるよ

なってくると，システムに対する要求もよます。自分だけのごく限られた範囲の目うになります。

り具体的なものになることでしょう。例

的にのみ使用するプログラムであれば，えば，「セキュリティ対策を行いたい」と簡単なプログラムで済んでしまう場合がいう漠然とした願望は，「syslog監視を多いものです。そして，そういったプロsyslogの出力を監視するツールの場自動化したい」などのはっきりした目的グラムを記述するのに最適なのが，万合，サーバーの運用形態によって注目すになります。

能スクリプト言語であるperlです。

べきログは異なりますから，個々のサーperlスクリプト

目的に応じて，必要なプログラムを探ここでは，syslogの監視と，ppp接続syslogの監視バーの事情に合わせて作り込んだperl＊14 「The Simple WATCHer」（http://www.engr.ucsb.
edu/˜eta/swatch/）を参照してください。

Nikkei Linux 2001.7

181

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32

#!/usr/bin/perl
while(<>) {next if /^\w+\s+\d+\s+\d+:\d+:\d+\s+\w+\s+-- MARK --$/;$log = $_;if (! /^\w+\s+\d+\s+\d+:\d+:\d+\s+(\w+)\s+([^:\[ ]+)(\[\d+\])?:?\s+(.*)\n/) {print "UNKNOWN $log";
next;}$host = $1;$name = $2;$mesg = $4;
if ($name =~ /^syslogd/) {next if $mesg eq "restart.";}elsif ($name eq "sshd") {my($user);if ($mesg =~ /^Accepted rsa for (\w+) from (\d+\.\d+\.\d+\.\d+) port \d+$/) {if ($host eq "asao") {next if $1 eq "sengoku" && $2 eq "192.168.1.2";}}
if ($mesg =~ /^log: Connection from (\d+\.\d+\.\d+\.\d+) port \d+$/ ||$mesg =~ /^log: Closing connection to (\d+\.\d+\.\d+\.\d+)$/) {next if $1 eq "127.0.0.1";
next if $1 eq "192.168.1.2";}
next if $mesg eq "Generating new 768 bit RSA key.";
next if $mesg eq "RSA key generation complete.";}print $log if $mesg ne $prev_mesg;$prev_mesg = $mesg;}

図5 syslogの出力から重要でない行を取り除くスクリプト

May 13 02:54:44 asao -- MARK --図 6 ロ グ が な い とき にsyslogが出力するマーカー

一般的に侵入方法を予測することは困難です。そもそも予測できる侵入方法ならわざわざ穴を放置せず，侵入できないように対策すべきですから，（1）はあまり適切な方法とは言えないでしょう。そこでここでは（2）の方法を考えます。つまり，大量のsyslogの出力の中から，あまり重要でない，すなわち次の条件を満たす行を取り除き，残ったものを管理者に知らせることになります。

・侵入などの危険が少ない・出現頻度が高いスクリプトの例を図5に示します。このスクリプトは標準入力からログを入力し，上記条件を満たす行を取り除き，残りを標準出力へ出力します。実際の運用では標準出力を管理者に知らせる仕掛けを追加する必要があるでしょう。
図5の2行目で，標準入力から1行ずつスクリプトの方が，既存のツールよりも（1）syslogの出力の中に，あらかじめ設読み込み，4行目で変数$logへ代入しま適していると言えるでしょう。また，ツ定しておいた条件を満たす行が見つかす。3行目は，ログが無いときにsyslogがールを作っていく過程でsyslogの出力をれば管理者に知らせる。

自動的に出力する図6のような行を取りじっくり観察することになりますから，シ（2）syslogの出力から，あらかじめ設「\s」「\d」は，そ除きます。ここで「\w」

ステムを理解する上でも有益です。

定しておいた条件を満たす行を削除

れぞれアルファベット1文字，空白1文字，し，残った未知の行を管理者に知らせ数字1文字を表わす正規表現です。perl

る。

で使用可能な，特定の種類の1文字にマsyslogの監視方法としては，次の2通りが考えられます。

182

Nikkei Linux 2001.7

さらに進んだ連載

ッチする正規表現を表1にまとめておきます。
「\w」などに続く「+」は直前の正規表現の1回以上の繰り返しを意味する修飾子です。修飾子の一覧を表2に示します。また，最初の「\w」の前にある「^」は，文字列にマッチさせるとき，この位置が文字列の先頭であることを意味する正規表現です。同様に文字列中の位置を指定する正規表現を，表3に示します。つまり，3行目の正規表現^\w+\s+\d+\s+\d+:\d+:\d+\s+

表1 特定の1文字にマッチする正規表現「α」〜「\NNN」は，文字列中でも使用可能である。
正規表現.
\w\W\s\S\d\D
[…]
α\☆
\n\r
\f\t
\NNN

意味
改行以外の任意の1文字
アルファベット（「̲」を含む）
非アルファベット
空白
非空白
数字
非数字
… のいずれか（…: 文字にマッチする正規表現の並び）αと同一の文字（α:アルファベット1文字）☆と同一の文字（☆:記号1文字）
改行（0x0A）
キャリッジ・リターン（0x0D）
改ページ（0x0C）
タブ（0x09）8進数NNNで文字コードを指定

サーバー構築/運用術

表2 繰り返しを表す修飾子
修飾子＊+{n,m}
{n,}
{n}

意味
0回あるいは1回（「{0,1}」と同等）
0回以上（「{0,}」と同等）
1回以上（「{1,}」と同等）
n回以上 m 回以下
n回以上
n回（「{n,n}」と同等）

表3 文字列中の位置を指定する正規表現
正規表現 意味^
文字列の先頭$文字列の末尾あるいは最後の改行文
字の前
\b
単語の境界
\B
非単語境界

\w+\s+-- MARK --$May 12 10:36:23 asao kernel: Packet log: input DENY eth0 PROTO=6
211.199.73.21:1087 210.145.125.175:111 L=60 S=0x00 I=51126 F=0x4000 T=44 SYN (#21)

は，最初の「\w+」が図6の「May」にマ

図7 syslog が出力する行の例
実際には1行で出力される。

ッチし，以下「\s+」が「 」，「\d+\s+」が「13 」，「\d+:\d+:\d+\s+」が「02:54:44 」，「 \w+\s+-- MARK --$」が「 asao --$1〜$9を用いて参照できます。

なお，正規表現「[^:\[ ]」は，「[ ... ]」で例えば入力が図7のような行であった

くくった，1文字にマッチする正規表現

場合，5行目の正規表現中の「（\w+）」は，のいずれか，にマッチする正規表現で5行目で，ログの各行からホスト名, プ「asao」と，「（[^:\[ ]+）」は「kernel」と，すが，角かっこ内の先頭に否定を意味

ログラム名, メッセージを抽出します。

「（\[\d+\]）?:?\s+」は「:」と，「（. ＊ ）」はする「^」があるため，「:」「[」「 」のいず

この正規表現では「（ ... ）」が使われて「Packet log: input DENY ...」と，それぞれでもない1文字，という意味になります。

いますが，この丸かっこはくくった正規れマッチしますが，このとき$1は「asao」，前述した文字列の先頭を示す「^」とは表現を一まとまりとして扱うためのもの$2は「kernel」，$4は「Packet log: input

意味が異なるので注意してください。

です。つまり「（\[\d+\]）?」は，正規表現DENY ...」を、それぞれ参照することが後は，変数$nameに代入されたプロ「\[\d+\]」の0回または1回の繰り返しをできます。従って，これらをそれぞれ変グラム名ごとに，変数$mesgに代入され意味します。丸かっこを使うと，くくっ数$host，$name，$mesgへ代入します

たメッセージが重要なものか否かを判た正規表現にマッチした文字列を変数（9〜11行目）。

断して ，重 要 で な い 行 を 読 み 飛 ば す

MARK --」とマッチします。

Nikkei Linux 2001.7183

12345678910111213141516171819202122232425
26272829303132
33
34353637383940414243
4445
46
47
48
49
50
51
52

#!/usr/bin/perl$ENV{'PATH'} = "/usr/bin:/usr/sbin:/bin:/sbin";$Dev = "ttyS2";$System = "ddip";$Tel = "0570570011##4";$User = "prin";@PppOption = ("115200", "noipdefault", "defaultroute", "lock", "crtscts","user", "$User", "remotename", "$System");$| = 1;open(MODEM, "+>/dev/$Dev");vec($Rin, fileno(MODEM), 1) = 1;
&receive(0.1);&send("ATD$Tel\r");die "Fail to dial\n" unless &wait(120,"(CONNECT|BUSY|NO CARRIER)","CONNECT");if (!fork) {close(STDIN);
open(STDIN,"<&MODEM");
close(STDOUT);
open(STDOUT,">&MODEM");
exec "pppd", @PppOption;}
exit 0;sub wait {my($sec, $exp, $suc) = @_;my($i);for ($i=0; $i < $sec*10; $i++) {&receive(0.1);
return ($1 eq $suc || ! $suc) if /$exp/i;}
0;}

プトを追加していくだけです。
一例として，$nameが「syslogd」の場合（12〜14行目）と「sshd」の場合（15〜2 9 行目）の スクリプトを 示 しました 。
sshdの場合，アクセス元ホストおよびユーザー名を調べて，特定の条件を満たせば「next」を実行します。
「next」が実行されない場合は，30行目で標準出力へ出力することになります。ただし，同じメッセージが続く場合は最初の1行のみが出力されます。

ppp接続
ppp接続をするためのperlスクリプトを図8に示します。このスクリプトは，モデムにATコマンドを送ってプロバイダ

sub receive {my($timeout) = @_;my($rout);
while ((select($rout=$Rin, undef, undef, $timeout))[0]) {if (vec($rout, fileno(MODEM), 1)) {exit 1 if sysread(MODEM, $_, 1024) <= 0;
tr/\r\000\012\021\023\032/\n/d;$Buffer .= $_;}}$_ = $Buffer;}のアクセス・ポイントへダイアルし，接続完了後にpppdを実行してppp接続を行います。
perlにはデバッグのためのオプション「-d」があるので，効率的な開発が可能です。図8のスクリプトをppp.plというファイル名で保存した場合は，図9に示す

sub send {undef $Buffer;
while ($_ = shift) {syswrite(MODEM, $_, length);}}

図8 ppp接続を行うスクリプト

184（つまり，next命令を実行する）スクリ

Nikkei Linux 2001.7

ように1行ずつスクリプトを表示しながら実行して動作を確認していくと良いでしょう。コマンドの一覧を表示するには，「h 」を入力します。

さらに進んだ
連載

スクリプトの4〜6行目は，プロバイダのアクセス方法の設定です。アクセス・ポイントの電話番号およびユーザーIDの設定を行っています。パスワードの設サーバー構築/運用術

asao:/home/sengoku % perl -d ppp.pl
Default die handler restored.
Loading DB routines from perl5db.pl version 1.07
Editor support available.
Enter h or `h h' for help, or `man perldebug' for more help.

定は，pppdの設定ファイルpap-secretsで行います（図10）。ここでは，DDIポケットのPHSを使ったインターネット接続サービス「PRIN」＊15の場合の設定を示します。設定情報を表4に示す。
7〜8行目は，pppdのオプションです。

main::(ppp.pl:2):
DB<1> smain::(ppp.pl:3):
DB<1>
main::(ppp.pl:4):
DB<1>
main::(ppp.pl:5):
DB<1>$ENV{'PATH'} = "/usr/bin:/usr/sbin:/bin:/sbin";$Dev = "ttyS2";$System = "ddip";$Tel = "0570570011##4";

図9 perlスクリプトのデバッグ

詳しくはpppdのマニュアルを参照してください。9〜14行目でアクセス・ポイント

表4 PRIN設定情報
prinddipprin-に対して電話をかけます。
「MODEM」

図10 pppdの設定ファイル
pap-secrets

がモデム（例ではPHSカード）と入出力

1 番目の「 p r i n 」が ユーザー I D ，3 番目の「prin」がパスワードである。

電話番号末尾の##4は，64k PIAFS（2.1版）でアクセスするための設定。2.1版はベスト・エフォート（best effort）型，2.0版はギャランティ（guarantee）型で，互換性が無いので注意が必要である。前者はDDIポケット，後者はNTTドコモのPHSカードで使われている。

を行うためのファイル・ハンドルです。

電話番号ユーザID
パスワード

0570570011##4
prinprin

無事アクセス・ポイントへ電話がつながる（モデムから「CONNECT」を受信第2引数の「（ ... ）」でくくったパターンに要に応じて機能を追加してください＊16。

する）と，16〜19行目でファイル・ハンドマッチする文字列が第3引数に一致する

例えば接続先プロバイダが複数ある場

ル M O D E M を 標 準 入 出 力（ S T D I N ，

場合のみ，1を返します。

合，あるいはモデム等の通信手段が複34〜45行目は，モデムから文字列を数ある場合は，それらを簡単なオプショ受信するサブルーチンです。引数に与ンで切り替えられる方が便利でしょう。

24〜32行目は，モデムから特定の文えた秒数の間，何も受信しなければそまた，シリアル・ポートを他のプログラム字列を受信するまで待つサブルーチン

れまでに受信した文字列（変数$Bufferからも使用する場合は，ロック・ファイルです。
第1引数がタイムアウトまでの秒数，の内容）を返します。の制御が不可欠になります。

STDOUT）へリダイレクトして，pppdコマンドを実行します（20行目）。

第2引数が期待する受信文字列パターンです。タイムアウトの場合0を返し，期待47〜52行目は，モデムに対して文字列を送信するサブルーチンです。

ここではスクリプトを機能拡張していく一例として，ロック・ファイル制御機能した文字列を受信したとき1を返しまさて図8のスクリプトは，必要最小限を追加する方法を説明します。まず，図す。ただし，第3引数が指定された場合，の機能に絞って簡略化してあります。必11に示すサブルーチンを追加します。

＊15 詳しくは，http://www.ddipocket.co.jp/data/prin.htmlを参照してください。手続き無しで，15円/分の全国均一の通話料金だけで64kbpsで接続できます。私は，接続先を3カ所に限定した「Two LINK DATA」PCカードを使っています。このPCカードは，あらかじめ登録した電話番号しかダイアルできないので，PRINなどの，電話番号が全国共通の接続サービス向きと言えます。
＊16 私が実際に使っている同種のスクリプトは400行以上あります。

Nikkei Linux 2001.7

185

12345678910111213141516171819202122

sub lock_check {if (-f "$LockPrefix$Dev") {open(LOCK,"$LockPrefix$Dev") || die "$!";
if (read(LOCK,$pid,20) > 4) {$pid = $pid + 0;
} else {$pid = unpack("L",$pid);}close(LOCK);
if ($pid != $$ && -d "/proc/$pid") {die "Lock file exist !\n";}}}
sub lock_make {my $umask = umask(0);open(LOCK,">$LockPrefix$Dev") || die "$!";
printf(LOCK "%10d\n",$$);
close(LOCK);
umask($umask);}

図11 ロック・ファイル制御用サブルーチン
図8のスクリプトに追加する。

$LockPrefix = "/var/lock/LCK..";

図12 ロック・ファイル・プレフ
ィックスの設定
図8のスクリプトの2行目に挿入する。

&lock_check;&lock_make;use FileHandle;*MODEM = new FileHandle "/dev/$Dev", O_RDWR|O_NONBLOCK;
vec($Rin, fileno(MODEM), 1) = 1;
&receive(0.1);&lock_check;

図13 ロック・ファイル制御用サブルーチンの呼び出し
図8のスクリプトの10〜12行目をこの7行で置き換える。

&lock_make;

図 1 4 子 プ ロ セス の I D をロック・ファイルへ書き込む
図8のスクリプトの16行目に挿入する。

サブルーチンのlock̲checkは，ロック・ス名のプレフィックスです。ロック・ファイ

ルを再度調べて正しくロック・ファイルがファイルを調べ（2〜13行目），他のプロルのパス名が「/var/lock/LCK..デバイス作成されているか確認します。なぜなら

セスが作ったロック・ファイルがあれば，名」という形式であれば，$LockPrefixに運悪く同時に他のプロセスがロック・ファ終了します（11行目）。すなわち，ロッ「/var/lock/LCK..」という値を設定してイルを作成したかも知れないからです。

ク・ファイルにはそれを作ったプロセスおきます。図8のスクリプトの2行目辺り

もし他のプロセスのIDがロック・ファイルのIDが書き込まれていますから＊17，書に，図12に示す行を挿入しておくと良いに書き込まれていた場合は，ファイル・ハき込まれたIDを読み取って変数$pidへでしょう。

ンドルMODEMに対して送信してはいけ代入し（4〜8行目），そのIDが自身のプ次に，図8のスクリプトで，シリアル・ポません。図8のスクリプトでは，15行目でロセスIDでなく，かつそのプロセスがートをオープンしている10〜12行目を，図子プロセスを生成し，子プロセスとして存在するか＊18調べます（10行目）。

13に示す7行で置き換えます。すなわち，pppdを実行します。すなわちロック・ファ

サブルーチンlock̲makeは，ロック・フ

ロック・ファイルを調べ（lock̲check），作成イルに書き込むプロセスIDを，この子プァイルに自身のプロセスID（変数$$）をした（lock̲make）後にシリアル・ポートをロセスのIDに変更する必要があります。

書き込みます（図11の19行目）。なお，＊19
非ブロック・モード（O̲NONBLOCK）

そこで，図8のスクリプトの16行目に，図14

変数$LockPrefixはロック・ファイルのパでオープンします。そして，ロック・ファイに示す行を挿入します。

＊17 プロセスIDをバイナリで書き込む方法と，文字列として書き込む方法があります。最近のLinuxでは後者が標準的なのではないかと思います。図11に示したサブルーチンは，バイナリが書き込まれている場合にも対応できます（7行目）。
＊18 Linuxでは，/procファイル・システムに，プロセスIDごとのディレクトリが現れます。従ってあるIDのプロセスが存在するか否かは，対応するディレクトリが存在するか調べる（図11の10行目）だけで済みます。このようなファイル・システムがサポートされていないOSでは，psコマンド等を実行してプロセスが存在するか調べる必要があるでしょう。
＊19 シリアル・ポートを複数のプログラムから利用する場合，非ブロック・モードでオープンする必要があります。詳しくは2001年3月号，pp.110-120の「実戦で学ぶ，一歩進んだサーバー構築術」を参照してください。

186

Nikkei Linux 2001.7