Net::Daemon v0.32 日本語チョー訳

by Hippo2000(2000/12/27)

DBI::ProxyServerの親の親、Net::Daemonなのです。

作者はJochen Wiedmannさんです。それにしても、この方は色々なものを作っています。

Jochen Wiedmannさんにはメールで了解をいただきました。

なお内容等が間違っていたら修正します。ご連絡ください。

 

なおLWPモジュールには以下のサブクラスがあります。

クラス名 説 明
Net::Daemon::Log ログ出力のためのユーティリティ関数
Net::Daemon::Test Net::Daemonサーバーをテストするためのサポート関数

目次


名前

Net::Daemon - 移植可能なデーモンのためのPerl拡張


概要

  # Net::Daemonのサブクラスの作成
  require Net::Daemon;
  package MyDaemon;
  @MyDaemon::ISA = qw(Net::Daemon);
  sub Run ($) {
    # この関数が本当に動きます; これは新しく接続がおこなわれるたびに
    # 呼び出されます
  }

警告

これはALPHA状態のソフトウェアです。 これはインターフェース(API)が最終確定されていないためだけに'Alpha'になっています。Alpha状態はコードの品質や安定性には当てはまりません。


説明

Net::Daemon はとても簡単に移植可能なサーバー・アプリケーションを実装するための抽象クラスです。このモジュールはPerl5.005のために設計されていますが、fork()とPerl5.004で機能します。

Net::Daemon クラスはデーモンに必要なほとんどの一般的なタスクのためのメソッドを提供します:起動、ログ出力、クライアント認証、セキュリティのために自分の環境を制限すること、そして本当の機能をおこなうこと。あなたはあなたの目的に合わないメソッドだけを上書きするだけでよいのです。しかし典型的には継承すると仕事を減らしてくれるでしょう。


コンストラクタ

  $server = Net::Daemon->new($attr, $options);
  $connection = $server->Clone($socket);

2つのコンストラクタが利用できます:new メソッドは起動時と基本的にプログラム全体でのアンカーとして機能するオブジェクトが作られるときに呼ばれます。これはGetopt::Long (3) を通じてコマンド・ラインの解析もサポートします。

new の引数は属性のハッシュ・リファレンスである $attr (下記をご覧ください)とオプションの配列リファレンスである $options で、これは典型的にはコマンド・ライン引数(例えば \@ARGV ) で Getopt::Long::GetOptions に渡されます。

2番目のコンストラクタはClone です。これはクライアントが接続するたびに呼ばれます。これはメインのサーバー・オブジェクトを入力として受け取り、新しいオブジェクトを返します。この新しいオブジェクトは、最終的にクライアントと通信する本来の仕事をおこなうメソッドに渡されます。通信はClone の引数である、$socket ソケットで行われます。

設定できるオブジェクト属性と対応するコマンドラインの引数は以下の通りです:

catchint (--nocatchint)
いくつかのシステム、特にSolarisではaccept()、read()などの関数はシグナルによる中断に対して安全ではありません。例えば、ユーザがUSR1シグナル(典型的には構成設定ファイルの再読に使われます)をおこすと、関数はエラーEINTRを返します。もしcatchint オプションがオンであれば(デフォルト。オフにするには --nocatchint を使ってください)、パッケージはできるかぎりEINTRエラーを無視します。
 
chroot (--chroot=dir)
(UNIXのみ)bind()をした後、chroo()をすることにより指定されたディレクトリにルート・ディレクトリを変更します。これはセキュリティ操作に便利です。しかしこれはプログラマに多くの制約を与えます。例えば典型的には、chroot()する前に外部のPerl拡張をロードしなければなりませんし、あるいはUnixソケットへハードリンクを作成する必要があります。これは典型的には構成設定ファイル(config file)で行われます。--configfile オプションをご覧ください。また --group と --userもご覧ください。
 
chroot()をご存じなければ、ログインした後ににだけあるディレクトリ・ツリーを見ることができるFTPサーバーを考えてみてください。
 
clients
クライアントのリストを持った配列リファレンス。クライアントはハッシュ・リファレンスで、その属性はaccept (0ならば受入ない、1なら許可)、そしてクライアントIP番号またはホスト名のためのPerl正規表現であるmaskです。下記の アクセス制御 をご覧ください。
 
configfile (--configfile=file)
Net::Daemon は構成設定ファイル(config file)の使用をサポートしています。これらのファイルには、newメソッドの引数を上書きする1つのハッシュ・リファレンスが入っていると仮定されます。しかしコマンドライン引数は構成設定ファイルよりも優先されます。構成設定ファイルについての詳細は下記の 構成設定ファイルをご覧ください。
 
debug (--debug)
デバッグ・モードをオンにします。主にこれはレベル"debug"のログ出力メッセージが作成されることを意味しています。
 
facility (--facility=mode)
(UNIXのみ) Sys::Syslog (3) での facility。デフォルトはdaemon
group (--group=gid)
bind()をした後に、実際のそして実効のGIDを与えられたものに変更します。これはサーバーを特権ポートにbinnd(<1024)にバインドしたいときに便利です。しかしrootとしてサーバーを実行したいとは思わないで下さい。--userオプションもご覧ください。

GIDにはグループ名も数値も渡すことができます。

localaddr (--localaddr=ip)
デフォルトではデーモンはマシーンが持っているIP番号のすべてについてlistenします。この属性は与えられたIP番号にサーバーを限定することを可能にします。
 
localpath (--localpath=path)
サーバーをローカル・サービスにだけ限定したければ、もしできるのであればUnixソケットを使うことを好むでしょう。その場合、作成されるUnixソケットのパスを設定するため、このオプションを使うことができます。このオプションは --proto=unix も含みます。
 
localport (--localport=port)
この属性は、デーモンがlistenするポートを設定します。それは何らかの方法で与えられなければなりません。デフォルトはありません。
 
logfile (--logfile=file)
デフォルトではログメッセージはsyslog(Unix)またはイベントログ(Windows NT)に書き込まれます。他のオペレーティング・システムではログ・ファイルを指定する必要があります。特殊な値 "STDERR"は 標準エラー出力(stderr)にログを出力させます。
 
loop-child (--loop-child)
このオプションは新しくループのための子供を作成させます(loop-timeout オプションをご覧ください)。デフォルトでは、ループはシリアル化されます。
 
loop-timeout (--loop-timeout=secs)
いくつかのサーバーはある時から時へ行動する必要があります。例えば、Net::Daemon::Spoolerは5分毎にそのスプーリング・キューを空にしようとします。このオプションが正の値に設定されると(デフォルトでは0)、サーバーは"loop-timeout"秒毎に、そのLoopメソッドを呼び出します。

このインターバルの精度をあまり信用しないで下さい。これはいくつかの要素、特にLoop()メソッドの実行時間に依存しています。ループはselect()関数を使って実装されています。正確なインターバルが必要であれば、alarm()関数とシグナル・ハンドラを使ったほうがよいでしょう。(catchint オプションを見るのを忘れないで下さい!)

loop-timeout.と結び付けるには、loop-child オプションを使うことをお勧めします。

 
mode (--mode=modename)
Net::Daemon サーバーは環境によって、3つの異なるモードで実行することができます。

Perl 5.005を実行し、スレッドが利用できるようにコンパイルしてあれば、サーバーは各接続に対して新しいスレッドを作成します。そのスレッドはサーバーのRun()メソッドを実行し、終了します。このモードがデフォルトで、これは"--mode=threads"で強制することができます。

もしスレッドが使ええなくても、機能するfork()を持っているのであれば、そこでサーバーは同様に、各接続に対して新しいスレッドを作成します。このモードはスレッドがない場合には自動的に、もしくは"--mode=fork"オプションを使うと使われます。

最後にシングルーコネクション・モードがあります。サーバーが接続を受け取ると、それがRun()メソッドに入ります。他の接続はRun()メソッドから戻ってくるまで受け取られません。このオプションはMacintoshのようにスレッドもfork()持っていないときに有効です。デバッグ目的のために、このモードを"--mode=single"を強制することができます。

options
newメソッドを通じてサーバー・オブジェクトに渡されるコマンドライン・オプションの配列リファレンス。
 
parent
Clone でオブジェクトが作成されるとき、元のオブジェクトは新しいオブジェクトの親になります。newで作られるオブジェクトは通常親を持っていません、このためこの属性は設定されません。
 
pidfile (--pidfile=file)
(UNIX のみ) このオプションがあると、指定された場所にPIDファイルが作成されます。
proto (--proto=proto)
使用されるトランスポート・レイヤ。デフォルトでのtcp またはUnixソケットのためのunix 。両方に接続するのはまだできません。
 
socket
Clone メソッドへ $client 引数として渡されるクライアントに接続されたソケット。もしサーバー・オブジェクトがnew で作成されると、この属性はBindメソッドが呼ばれるまでは、undefかもしれません。ソケットはIO::Socketオブジェクトのと仮定されます。
 
user (--user=uid)
bind()をした後に、実際のそして実効のUIDを与えられたものに変更します。これはサーバーを特権ポートにbinnd(<1024)にバインドしたいときに便利です。しかしrootとしてサーバーを実行したいとは思わないで下さい。--groupと--chrootオプションもご覧ください。

UIDにはグループ名も数値も渡すことができます。

 
version (--version)
サーバーの起動をとばし、代わりにバージョン文字列を出力し、プログラムは即座に終了します。
 

これらの属性のほとんど(facility, mode, localaddr, localport, pidfile, version) がm起動のときにのみ意味があることに注意してください。もし後で設定すると、単に無視されます。ほとんどすべての属性は適切なデフォルトを持っています。典型的にはlocalportポートだけを使います。


コマンドラインの解析

  my $optionsAvailable = Net::Daemon->Options();
  print Net::Daemon->Version(), "\n";
  Net::Daemon->Usage();

Options メソッドはありうるコマンド・ライン・オプションのハッシュ・リファレンスを返します。キーはオプション名、値は以下のキーを持ったハッシュ・リファレンスです:

template
Getopt::Long::GetOptions に渡すことができるオプション・テンプレート
 
description
Usage で使われる、このオプションの説明
 

Usage メソッドはすべての可能なオプションと戻りのリストを出力します。それはプログラム名とバージョンを出力するために Version メソッドを使います。


構成設定ファイル(Config File)

もし構成設定ファイル(config file)オプションがコマンドライン・オプションか"new"の引数でで指定されると、以下のメソッド

  $server->ReadConfigFile($file, $options, $args)

が呼び出されます。デフォルトでは構成設定ファイルはオプションのハッシュ・リファレンスを返すPerlソースが入っていることが予定されています。これらのオプションは"new"引数を上書きし、$options ハッシュ・リファレンスで表されたように、コマンドライン・オプションによって上書きされます。

典型的な構成設定ファイルは以下のようになります、例としてDBI::ProxyServerを使います:

    # 外部のモジュールをロードします:これは使わなければ必要ありません
    # chroot() オプション.
    #require DBD::mysql;
    #require DBD::CSV;
    {
        # 'chroot' => '/var/dbiproxy',
        'facility' => 'daemon',
        'pidfile' => '/var/dbiproxy/dbiproxy.pid',
        'user' => 'nobody',
        'group' => 'nobody',
        'localport' => '1003',
        'mode' => 'fork'
        # アクセス制御
        'clients' => [
            # ローカルは受け付ける
            {
                'mask' => '^192\.168\.1\.\d+$',
                'accept' => 1
            },
            # myhost.company.comを受け付ける
            {
                'mask' => '^myhost\.company\.com$',
                'accept' => 1
            }
            # その他は拒絶する
            {
                'mask' => '.*',
                'accept' => 0
            }
        ]
    }

アクセス制御

Net::Daemon パッケージはホスト・ベースのアクセス制御のスキームをサポートしています。デフォルトではすべての人にアクセスが開かれています。しかし 属性 $self->{'clients'} を作れば、典型的には構成設定ファイルになりますが、デフォルトでアクセス制御ができなくなります。すべての接続について、クライアント・リストが処理されます。clients属性はハッシュ・リファレンスのリストへの配列リファレンスです。ハッシュ・リファレンスのいずれにも以下のものを含めた、任意の属性を入れることができます。

mask
クライアント IP番号またはホスト名の対応するPerl正規表現。'mask'属性がマッチしたときにはいつでも、このリストは左から右に処理され、そして関連するハッシュ・リファレンスがクライアントとして選ばれ、クライアント・リストの処理は止まります。
 
accept
これはtrueまたはfalse(この属性を省略したときのデフォルト)に設定することができます。前者はそのクライアントを受け入れることを意味します。
 

イベント・ログ出力

  $server->Log($level, $format, @args);
  $server->Debug($format, @args);
  $server->Error($format, @args);
  $server->Fatal($format, @args);

Log メソッドはSys::Syslog (3) または Win32::EventLog (3) へのインターフェースです。その引数はdebugnotice または err のようなsyslogレベルである$level 、printfの形式でのフォーマット文字列、そして フォーマット文字列引数です。

DebugError メソッドはそれぞれ、debug と err レベルでのLogを呼び出すための短縮です。Fatal メソッドは Error に似ていますが、さらに与えられたメッセージを例外として投げます。

詳細については Net::Daemon::Log(3) をご覧ください。


制御の流れ

  $server->Bind();
  # Bind()のう内側では以下の通り:
  if ($connection->Accept()) {
      $connection->Run();
  } else {
      $connection->Log('err', 'Connection refused');
  }

サーバーが開始するときにBind メソッドがアプリケーションにより呼び出されます。典型的にはサーバー・オブジェクト $server が作られたすぐ後におこなうことができます。エラーの場合を除いて、Bind は通常何も返しません。

クライアントが接続すると、サーバーは、サーバーからconnection オブジェクト$connection を派生されるためにCloneを使います。あなたのクラスのAcceptメソッドを呼び出すためにconnectionオブジェクトを使う新しいスレッドもしくはプロセスが作成されます。このメソッドはホスト認証をするようになっており、FALSE(クライアントの拒絶)あるいはTRUE(クライアントの受入)のどちらかを返します。

もしクライアントが受け入れられると、実際の作業をおこなうRun メソッドが呼び出されます。Runが戻り、対応するスレッドまたはプロセスが終了すると、接続はクローズされます。


エラーの取扱い

すべてのメソッドはエラーの場合にはPerlの例外を投げるようになっています。


マルチスレッドについての考慮

すべてのメソッドはレキスカルなスコープのデータだけで動きます。例外はOpenLogメソッドでこれはスレッドが開始する前に呼び出されます。このため、スレッド間でハンドルを共有しないかぎり安全です。私はあなたのアプリケーション同じように振る舞うことを強く推奨します。


例として、簡単な計算機(calculator)サーバーを書きます。このサーバーに接続した後、1行毎に数式を入力来ることができます。サーバーはその数式を評価し、結果を出力します。(これは例です。実際にはこのようなセキュリティホールを実装することはありません。 :-))

例のため、値として'hex'、'oct'、'dec'を取る、コマンドライン・オプション --base を追加します。サーバー出力はその基数を使います。

  # -*- perl -*-
  #
  # 計算機サーバー(Calculator server)
  #
  require 5.004;
  use strict;
  require Net::Daemon;
  package Calculator;
  use vars qw($VERSION @ISA);
  $VERSION = '0.01';
  @ISA = qw(Net::Daemon); # Net::Daemonから継承するために
  sub Version ($) { 'Calculator Example Server, 0.01'; }
  # コマンドラインのオプション "--base"を追加します
  sub Options ($) {
      my($self) = @_;
      my($options) = $self->SUPER::Options();
      $options->{'base'} = { 'template' => 'base=s',
                             'description' => '--base                  '
                                    . 'dec (default), hex or oct'
                              };
      $options;
  }
  # コマンド行のオプションをコンストラクタで扱います
  sub new ($$;$) {
      my($class, $attr, $args) = @_;
      my($self) = $class->SUPER::new($attr, $args);
      if ($self->{'parent'}) {
          # Called via Clone()
          $self->{'base'} = $self->{'parent'}->{'base'};
      } else {
          # Initial call
          if ($self->{'options'}  &&  $self->{'options'}->{'base'}) {
              $self->{'base'} = $self->{'options'}->{'base'}
          }
      }
      if (!$self->{'base'}) {
          $self->{'base'} = 'dec';
      }
      $self;
  }
  sub Run ($) {
      my($self) = @_;
      my($line, $sock);
      $sock = $self->{'socket'};
      while (1) {
          if (!defined($line = $sock->getline())) {
              if ($sock->error()) {
                  $self->Error("Client connection error %s",
                               $sock->error());
              }
              $sock->close();
              return;
          }
          $line =~ s/\s+$//; # Remove CRLF
          my($result) = eval $line;
          my($rc);
          if ($self->{'base'} eq 'hex') {
              $rc = printf $sock ("%x\n", $result);
          } elsif ($self->{'base'} eq 'oct') {
              $rc = printf $sock ("%o\n", $result);
          } else {
              $rc = printf $sock ("%d\n", $result);
          }
          if (!$rc) {
              $self->Error("Client connection error %s",
                           $sock->error());
              $sock->close();
              return;
          }
      }
  }
  package main;
  my $server = Calculator->new({'pidfile' => 'none',
                                'localport' => 2000}, \@ARGV);
  $server->Bind();

作者と著作権(AUTHOR AND COPYRIGHT)

(原文のまま)

  Net::Daemon is Copyright (C) 1998, Jochen Wiedmann
                                     Am Eisteich 9
                                     72555 Metzingen
                                     Germany
                                     Phone: +49 7123 14887
                                     Email: joe@ispsoft.de
  All rights reserved.
  You may distribute this package under the terms of either the GNU
  General Public License or the Artistic License, as specified in the
  Perl README file.

参考資料

RPC::pServer(3), Netserver::Generic(3), Net::Daemon::Log(3), Net::Daemon::Test(3)

 


ホーム Perlの小技

ご意見、ご質問はこちらの掲示板で受け付けています。
またメールは河馬屋(Nifty)にお願いします。