root/lang/perl/tiarra/trunk/tiarra @ 11043

Revision 11043, 10.9 kB (checked in by topia, 5 years ago)

* drop cvsversion scanner.
* minor behavior fixes.

  • Property svn:mime-type set to text/x-perl; charset=EUC-JP
  • Property svn:eol-style set to LF
  • Property svn:executable set to *
  • Property svn:keywords set to Id URL Date Rev Author
Line 
1#!/usr/bin/perl
2# -----------------------------------------------------------------------------
3# - T i a r r a - :::bootstrap:::
4# Copyright (c) 2008 Tiarra Development Team. All rights reserved.
5# This is free software; you can redistribute it and/or modify it
6#   under the same terms as Perl itself.
7# -----------------------------------------------------------------------------
8# $Id$
9# -----------------------------------------------------------------------------
10require 5.006;
11use strict;
12use warnings;
13use File::Basename;
14use File::Spec;
15use Carp;
16
17sub follow_link {
18    my ($file, $max_count, $die_on_max) = @_;
19
20    $max_count = 40 unless defined $max_count;
21    my ($count, @path) = 0, ();
22    push(@path, $file);
23    while (-l $file) {
24        $file = File::Spec->rel2abs(readlink($file), dirname($file));
25        push(@path, $file);
26        if (++$count >= $max_count) {
27            if ($die_on_max) {
28                carp 'Too many levels of symbolic links';
29            } else {
30                last;
31            }
32        }
33    }
34    return @path;
35}
36
37BEGIN {
38    # untaint
39    $0 =~ /^(.+)$/;
40    my $self = $1;
41    my @add_inc;
42
43    foreach my $path (map dirname($_), reverse follow_link($1)) {
44        unshift(@INC,
45                map{ File::Spec->catdir($path, $_); } qw(main module));
46        unshift(@add_inc, File::Spec->catdir($path, 'bundle'));
47    }
48    push(@INC, @add_inc);
49}
50
51# optional modules
52use Tiarra::OptionalModules;
53# 外部から呼べるオプションモジュールの存在チェック。
54# (過去互換)
55sub ipv6_enabled { Tiarra::OptionalModules->ipv6; }
56
57use Tiarra::Resolver; # early initialization
58use Tiarra::Encoding;
59use Configuration;
60use RunLoop;
61use ModuleManager;
62use ReloadTrigger;
63use IO::Handle;
64our $terminated = 0;
65
66# version はバージョン番号
67our $version = '0.1';
68
69# based はベースにしている Tiarra のバージョン(パッケージまたは fork 時用)
70our $based_version = '';
71
72# short は短いバージョン番号。(CTCP-Version の返答に使われる)
73our $short_version = '';
74
75
76# オリジナル(based_version が未定義)ならば svnversion をチェックする。
77&check_svnversion unless $based_version;
78# short_version が未定義なら version の値を使う。
79$short_version ||= $version;
80
81&install_signal_handlers;
82
83sub check_svnversion {
84    use IO::File;
85    my $svnversion = '.svnversion';
86    my $svnrevision;
87
88    foreach my $file (follow_link($0)) {
89        my $dir = dirname($file);
90        my $path = File::Spec->catfile($dir, $svnversion);
91        my $fh = IO::File->new($path, 'r');
92        if (defined $fh) {
93            $svnrevision = <$fh>;
94            chomp $svnrevision;
95        } elsif (-e File::Spec->catdir($dir, '.svn')) {
96            (my $svndir = $dir) =~ s/'/'\''/;
97            do {
98                # special cleanup for taint check
99                $ENV{PATH} =~ /^(.*)$/;
100                local $ENV{PATH} = $1;
101                $svndir =~ /^(.*)$/;
102                $svnrevision = (`svnversion --no-newline --committed $1` =~ /(\d+[MS]{0,2})$/)[0];
103            };
104        }
105        $version .= '+svn-' . $svnrevision if defined $svnrevision && length $svnrevision;
106        return;
107    }
108}
109
110sub help {
111    print "\n";
112    print "Usage: tiarra [--config=config-file] [options]\n";
113    print "\n";
114    print "options:\n";
115    print "  --help           print this message\n";
116    print "  --version        print version information\n";
117    print "  --dumpversion    print version\n";
118    print "  --config=<file>  tiarra configuration file; default is 'tiarra.conf'\n";
119    print "  --quiet          don't output any messages to stdout and stderr\n";
120    print "  --no-fork        don't move to background when started in quiet mode\n";
121    print "  --debug          show debug information\n";
122    print "  --make-password  prompt you a password to encrypt.\n";
123    print "                   *Tiarra doesn't do its normal work with this option*\n";
124    print "   -D<symbol>[=<string>]\n";
125    print "                   treat as `\@define <symbol> <string>' is in the conf\n";
126    print "\n";
127    print "If you specify --config=- parameter,\n";
128    print "Tiarra will read configuration from stdin(pipe).\n";
129    print "  example:\n";
130    print "      cat tiarra.conf | sed -e 's/Tiarra/arraiT/g' | ./tiarra --config=- --quiet\n";
131    print "      gunzip -c tiarra.conf.gz | ./tiarra --config=-\n";
132    print "\n";
133}
134
135sub make_password {
136    eval 'use Crypt;';
137    print "Tiarra encrypts your raw password to use it for config file.\n";
138    print "\n";
139
140    my $password = &find_option('make-password');
141    if ($password eq "1") {
142        eval 'use Term::ReadLine;';
143        my $term = Term::ReadLine->new('tiarra');
144        $password = $term->readline("Please enter raw password: ");
145        print "\n";
146    }
147    print Crypt::encrypt($password)." is your encoded password.\n";
148    print "Use this for the general/tiarra-password entry.\n";
149}
150
151sub find_option {
152    my $option = shift;
153    foreach my $arg (@ARGV) {
154        if ($arg eq "--$option") {
155            return 1;
156        } elsif ($arg =~ m/^--$option=(.+)$/) {
157            return $1;
158        }
159    }
160    undef;
161}
162
163sub find_options {
164    # $opt_regex: オプション名の正規表現。後方参照を一つだけ作る事。
165    # 戻り値: ([$1, 値], ...)
166    my $opt_regex = shift;
167    grep {
168        defined;
169    } map {
170        if (m/^--?$opt_regex=(.+)/) {
171            [$1, $2];
172        }
173        elsif (m/^--?$opt_regex$/) {
174            [$1, 1];
175        }
176        else {
177            undef;
178        }
179    } @ARGV;
180}
181
182sub main {
183    if (&find_option('debug')) {
184        eval q(sub debug_printmsg{printmsg('debug: '.shift)});
185        eval q(sub debug_mode{1;});
186        $SIG{__WARN__} = sub {
187            ::printmsg(Carp::longmess(@_));
188        };
189        $SIG{__DIE__} = sub {
190            die @_ if $_[0] =~ /^[Cc]ouldn't connect/;
191            die @_ if $_[0] =~ /^network\/.+:\s*Server replied/;
192            die(Carp::longmess(@_));
193        }
194    } else {
195        eval q(sub debug_printmsg{});
196        eval q(sub debug_mode{0;});
197    }
198
199    if (&find_option('help')) {
200        &help;
201        return 0;
202    } elsif (&find_option('version')) {
203        map { print "$_\n" } get_credit();
204        return 0;
205    } elsif (&find_option('dumpversion')) {
206        print $version . "\n";
207        return 0;
208    } elsif (&find_option('show-env')) {
209        map { print "$_\n" } get_env();
210        return 0;
211    } elsif (&find_option('make-password')) {
212        &make_password;
213        return 0;
214    }
215
216    foreach my $pp_define (&find_options(qr/D(.+?)/)) {
217        &Configuration::Preprocessor::initial_define(@$pp_define);
218    }
219
220    my $conf_file = &find_option('config');
221    if (!defined $conf_file) {
222        if (-f 'tiarra.conf') {
223            $conf_file = 'tiarra.conf';
224        } else {
225            &help;
226            return 2;
227        }
228    }
229
230    my $quiet = &find_option('quiet');
231    my $no_fork = &find_option('no-fork');
232
233    my $boot = sub  {
234        eval {
235            RunLoop->shared_loop->run;
236        }; if ($@) {
237            die "Tiarra aborted: $@\n";
238        } else {
239            print "Tiarra successfully finished.\n";
240        }
241    };
242
243    my $print = $quiet ? sub {} : sub { print @_ };
244
245    if (!$quiet) {
246        foreach my $line (get_credit()) {
247            $print->($line,"\n");
248        }
249        $print->("\n");
250    }
251
252    do {
253        local($|) = 1;
254
255        if ($conf_file ne '-') {
256            $print->("Reading configuration from ${conf_file}... ");
257        } else {
258            $conf_file = IO::Handle->new->fdopen(fileno(STDIN),'r');
259            $print->("Reading configuration from stdin... ");
260        }
261
262        if (!$quiet) {
263            Configuration->shared_conf->load($conf_file);
264        } else {
265            eval {
266                Configuration->shared_conf->load($conf_file);
267            }; if ($@) {
268                die "an error occoured on config read: $@";
269            }
270        }
271        $print->("ok\n");
272    };
273
274    # quietモードならSTDIN, STDOUT, STDERRを閉じる。
275    if ($quiet) {
276        close STDIN;
277        close STDOUT;
278        close STDERR;
279    }
280
281    # quietモードであり、且つno-forkオプションが指定されなかったらfork。
282    if ($quiet && !$no_fork) {
283        my $child_pid = fork;
284        if ($child_pid == 0) {
285            # 子プロセス
286            $boot->();
287        } elsif (!defined $child_pid) {
288            print "Tiarra: fork() failed.\n";
289        }
290    } else {
291        $boot->();
292    }
293    return 0;
294}
295
296sub printmsg {
297    # 文字コードはUTF-8でなければならない。
298    my $msg = shift;
299    local($|) = 1;
300    if (!defined $msg) {
301        $msg = '';
302    }
303    $msg =~ s/\n*$//s;
304
305    # Configurationが読み込まれていない時に文字コード変換するとdie。
306    eval {
307        local $SIG{__DIE__} = 'IGNORE';
308        local $SIG{__WARN__} = 'IGNORE';
309        $msg = Tiarra::Encoding->new($msg,'utf8')->conv(
310            Configuration->shared_conf->get('general')->stdout_encoding);
311    };
312
313    my ($sec,$min,$hour,$day,$mon,$year) = localtime(time);
314    $mon++;
315    $year += 1900;
316
317    #printf("[%02d/%02d/%04d %02d:%02d:%02d] %s\n",$mon,$day,$year,$hour,$min,$sec,$msg);
318    #printf("[%02d/%02d %02d:%02d:%02d] %s\n",$mon,$day,$hour,$min,$sec,$msg);
319    printf("[pid:$$ %04d/%02d/%02d %02d:%02d:%02d] %s\n",$year,$mon,$day,$hour,$min,$sec,$msg);
320}
321
322sub version {
323    $short_version;
324}
325
326sub get_credit {
327    return (
328        (!$based_version ?
329             "- T i a r r a - :::version #${version}:::" :
330                 ("- T i a r r a - :::version ${version}:::",
331                  "                    based #${based_version}")
332            ),
333            "Copyright (c) 2008 Tiarra Development Team. All rights reserved.",
334            "This is free software; you can redistribute it and/or modify it",
335            "  under the same terms as Perl itself.");
336}
337
338sub get_env {
339    use Config;
340    my @lines;
341    if (!$based_version) {
342        push @lines, "- T i a r r a - :::version #${version}:::";
343    } else {
344        push @lines, "- T i a r r a - :::version ${version}:::";
345        push @lines, "                    based #${based_version}";
346    }
347    push @lines, "Environment Information:";
348    push @lines, "  - Perl $Config{version} built for $Config{archname}";
349    push @lines, "Optional Modules:";
350    push @lines, map "  $_", Tiarra::OptionalModules->repr_modules;
351    push @lines, "Bundle Modules:";
352    foreach my $mod (qw(Unicode::Japanese IO::Socket::INET6 enum)) {
353        my $modfile = $mod . '.pm';
354        $modfile =~ s|::|/|g;
355        eval "require $mod;";
356        push @lines, "  - $mod " .
357            ($INC{$modfile} ?
358                 ($mod->VERSION . " (" .
359                      ($INC{$modfile} =~ m|bundle/| ? "bundle" : "system") . ")") :
360                          "(unknown; not loaded)");
361    }
362    push @lines, "Default Encoding Driver:   " . ref(Tiarra::Encoding->new);
363    @lines;
364}
365
366sub install_signal_handlers {
367    local $SIG{__WARN__} = sub {};
368    foreach (qw(INT QUIT ABRT TERM)) {
369        $SIG{$_} = \&handle_exit;
370    }
371    $SIG{HUP} = \&handle_reload;
372    $SIG{USR1} = \&handle_conf_reload;
373}
374
375sub handle_exit {
376    my $signame = shift;
377    printmsg("SIG$signame received.");
378    &shutdown('Tiarra '.::version.": SIG$signame received; exit");
379}
380
381sub handle_reload {
382    my $signame = shift;
383    printmsg("SIG$signame received.");
384    ReloadTrigger->reload_conf_if_updated;
385    ReloadTrigger->reload_mods_if_updated;
386}
387
388sub handle_conf_reload {
389    my $signame = shift;
390    printmsg("SIG$signame received.");
391    ReloadTrigger->reload_conf_if_updated;
392}
393
394sub shutdown {
395    my $msg = shift;
396    $msg = 'Tiarra '.::version.': shutting down...' if !defined $msg;
397    ++$terminated;
398    if ($terminated == 1) {
399        printmsg("Shutting down... [$msg]");
400        RunLoop->shared_loop->terminate($msg);
401    } elsif ($terminated == 2) {
402        printmsg("Second Terminate Request; Force Exit! [$msg]");
403        # force
404        print "cleanup ModuleManager...";
405        ModuleManager->shared_manager->terminate;
406        print "done.\n";
407        print "cleanup TerminateManager...";
408        Tiarra::TerminateManager->terminate('main');
409        print "done.\n";
410        exit;
411    } else {
412        printmsg("Third Terminate Request; Fatal Exit! [$msg]");
413        # fatal
414        exit;
415    }
416}
417
418my $exitval = main;
419&debug_mode && print "cleanup TerminateManager...";
420Tiarra::TerminateManager->terminate('main');
421&debug_mode && print "done.\n";
422exit $exitval;
Note: See TracBrowser for help on using the browser.