|  line  | 
 stmt  | 
 bran  | 
 cond  | 
 sub  | 
 pod  | 
 time  | 
 code  | 
| 
1
 | 
  
 
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 package Toadfarm;  | 
| 
2
 | 
19
 | 
 
 | 
 
 | 
  
19
  
 | 
 
 | 
5507038
 | 
 use Mojo::Base 'Mojolicious';  | 
| 
 
 | 
19
 | 
 
 | 
 
 | 
 
 | 
 
 | 
169
 | 
    | 
| 
 
 | 
19
 | 
 
 | 
 
 | 
 
 | 
 
 | 
130
 | 
    | 
| 
3
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
4
 | 
19
 | 
 
 | 
 
 | 
  
19
  
 | 
 
 | 
2768811
 | 
 use Cwd 'abs_path';  | 
| 
 
 | 
19
 | 
 
 | 
 
 | 
 
 | 
 
 | 
47
 | 
    | 
| 
 
 | 
19
 | 
 
 | 
 
 | 
 
 | 
 
 | 
857
 | 
    | 
| 
5
 | 
19
 | 
 
 | 
 
 | 
  
19
  
 | 
 
 | 
104
 | 
 use Data::Dumper ();  | 
| 
 
 | 
19
 | 
 
 | 
 
 | 
 
 | 
 
 | 
38
 | 
    | 
| 
 
 | 
19
 | 
 
 | 
 
 | 
 
 | 
 
 | 
341
 | 
    | 
| 
6
 | 
19
 | 
 
 | 
 
 | 
  
19
  
 | 
 
 | 
97
 | 
 use File::Basename qw(basename dirname);  | 
| 
 
 | 
19
 | 
 
 | 
 
 | 
 
 | 
 
 | 
42
 | 
    | 
| 
 
 | 
19
 | 
 
 | 
 
 | 
 
 | 
 
 | 
846
 | 
    | 
| 
7
 | 
19
 | 
 
 | 
 
 | 
  
19
  
 | 
 
 | 
113
 | 
 use File::Spec;  | 
| 
 
 | 
19
 | 
 
 | 
 
 | 
 
 | 
 
 | 
33
 | 
    | 
| 
 
 | 
19
 | 
 
 | 
 
 | 
 
 | 
 
 | 
451
 | 
    | 
| 
8
 | 
19
 | 
 
 | 
 
 | 
  
19
  
 | 
 
 | 
8222
 | 
 use File::Which;  | 
| 
 
 | 
19
 | 
 
 | 
 
 | 
 
 | 
 
 | 
16652
 | 
    | 
| 
 
 | 
19
 | 
 
 | 
 
 | 
 
 | 
 
 | 
839
 | 
    | 
| 
9
 | 
19
 | 
 
 | 
 
 | 
  
19
  
 | 
 
 | 
120
 | 
 use Mojo::File;  | 
| 
 
 | 
19
 | 
 
 | 
 
 | 
 
 | 
 
 | 
34
 | 
    | 
| 
 
 | 
19
 | 
 
 | 
 
 | 
 
 | 
 
 | 
603
 | 
    | 
| 
10
 | 
19
 | 
 
 | 
 
 | 
  
19
  
 | 
 
 | 
87
 | 
 use Mojo::Util qw(class_to_path monkey_patch);  | 
| 
 
 | 
19
 | 
 
 | 
 
 | 
 
 | 
 
 | 
36
 | 
    | 
| 
 
 | 
19
 | 
 
 | 
 
 | 
 
 | 
 
 | 
1141
 | 
    | 
| 
11
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
12
 | 
19
 | 
  
 50
  
 | 
 
 | 
  
19
  
 | 
 
 | 
103
 | 
 use constant DEBUG => $ENV{TOADFARM_DEBUG} ? 1 : 0;  | 
| 
 
 | 
19
 | 
 
 | 
 
 | 
 
 | 
 
 | 
38
 | 
    | 
| 
 
 | 
19
 | 
 
 | 
 
 | 
 
 | 
 
 | 
2782
 | 
    | 
| 
13
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
14
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 our $VERSION = '0.84';  | 
| 
15
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
16
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 BEGIN {  | 
| 
17
 | 
19
 | 
  
 50
  
 | 
  
 33
  
 | 
  
19
  
 | 
 
 | 
324
 | 
   $ENV{TOADFARM_ACTION} //= (@ARGV and $ARGV[0] =~ /^(reload|start|stop)$/) ? $1 : 'load';  | 
| 
 
 | 
 
 | 
 
 | 
  
 66
  
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
18
 | 
19
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
59221
 | 
   $ENV{MOJO_CONFIG} = $ENV{TOADFARM_CONFIG} if $ENV{TOADFARM_CONFIG};  | 
| 
19
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
20
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
21
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub import {  | 
| 
22
 | 
16
 | 
  
100
  
 | 
 
 | 
  
16
  
 | 
 
 | 
149
 | 
   return unless grep {/^(-dsl|-init|-test)/} @_;  | 
| 
 
 | 
31
 | 
 
 | 
 
 | 
 
 | 
 
 | 
206
 | 
    | 
| 
23
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
24
 | 
15
 | 
 
 | 
 
 | 
 
 | 
 
 | 
35
 | 
   my $class  = shift;  | 
| 
25
 | 
15
 | 
 
 | 
 
 | 
 
 | 
 
 | 
32
 | 
   my $caller = caller;  | 
| 
26
 | 
15
 | 
 
 | 
 
 | 
 
 | 
 
 | 
124
 | 
   my $app    = Toadfarm->new;  | 
| 
27
 | 
15
 | 
 
 | 
  
 50
  
 | 
 
 | 
 
 | 
4257
 | 
   my $tf     = $app->config->{tf} ||= {};    # internal  | 
| 
28
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
29
 | 
15
 | 
 
 | 
 
 | 
 
 | 
 
 | 
476
 | 
   $_->import for qw(strict warnings utf8);  | 
| 
30
 | 
15
 | 
 
 | 
 
 | 
 
 | 
 
 | 
355
 | 
   feature->import(':5.10');  | 
| 
31
 | 
15
 | 
 
 | 
 
 | 
 
 | 
 
 | 
27
 | 
   unshift @{$app->commands->namespaces}, 'Toadfarm::Command';  | 
| 
 
 | 
15
 | 
 
 | 
 
 | 
 
 | 
 
 | 
102
 | 
    | 
| 
32
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
33
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   monkey_patch $caller, (  | 
| 
34
 | 
6
 | 
 
 | 
 
 | 
  
6
  
 | 
 
 | 
2493
 | 
     app         => sub {$app},  | 
| 
 
 | 
 
 | 
 
 | 
 
 | 
  
6
  
 | 
 
 | 
 
 | 
    | 
| 
35
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     change_root => \&_change_root,  | 
| 
36
 | 
  
0
  
 | 
 
 | 
 
 | 
  
0
  
 | 
 
 | 
0
 | 
     logging     => sub { $tf->{logging}++; $app->_setup_log(@_) },  | 
| 
 
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    | 
| 
37
 | 
  
0
  
 | 
  
  0
  
 | 
 
 | 
  
0
  
 | 
 
 | 
0
 | 
     mount       => sub { push @{$app->config->{apps}}, @_ == 2 ? @_ : ($_[0], {}); $app },  | 
| 
 
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    | 
| 
 
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    | 
| 
38
 | 
1
 | 
  
 50
  
 | 
 
 | 
  
1
  
 | 
 
 | 
730
 | 
     plugin      => sub { push @{$app->config->{tf_plugins}}, @_ == 2 ? @_ : ($_[0], {}); $app },  | 
| 
 
 | 
1
 | 
 
 | 
 
 | 
 
 | 
 
 | 
4
 | 
    | 
| 
 
 | 
1
 | 
 
 | 
 
 | 
 
 | 
 
 | 
13
 | 
    | 
| 
39
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     run_as      => \&_run_as,  | 
| 
40
 | 
  
0
  
 | 
 
 | 
 
 | 
  
1
  
 | 
 
 | 
0
 | 
     secrets     => sub { $tf->{secrets}++; $app->secrets([@_]) },  | 
| 
 
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    | 
| 
41
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     start       => sub {  | 
| 
42
 | 
4
 | 
  
 50
  
 | 
 
 | 
  
4
  
 | 
 
 | 
1341
 | 
       if (@_) {  | 
| 
43
 | 
4
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
15
 | 
         my $listen = ref $_[0] eq 'ARRAY' ? shift : undef;  | 
| 
44
 | 
4
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
25
 | 
         $app->config->{hypnotoad}         = @_ > 1 ? {@_} : {%{$_[0]}} if @_;  | 
| 
 
 | 
  
0
  
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    | 
| 
45
 | 
4
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
51
 | 
         $app->config->{hypnotoad}{listen} = $listen if $listen;  | 
| 
46
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
       }  | 
| 
47
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
48
 | 
4
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
32
 | 
       $app->moniker($class->_moniker) if $app->moniker eq 'toadfarm';  | 
| 
49
 | 
4
 | 
 
 | 
  
 33
  
 | 
 
 | 
 
 | 
23
 | 
       $app->config->{hypnotoad}{pid_file} ||= $class->_pid_file($app);  | 
| 
50
 | 
4
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
18
 | 
       $app = $class->_setup_app($app) if $ENV{TOADFARM_ACTION};  | 
| 
51
 | 
4
 | 
 
 | 
 
 | 
 
 | 
 
 | 
20
 | 
       Mojo::UserAgent::Server->app($app);  | 
| 
52
 | 
4
 | 
 
 | 
 
 | 
 
 | 
 
 | 
24
 | 
       warn '$config=' . Mojo::Util::dumper($app->config) if DEBUG;  | 
| 
53
 | 
4
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
15
 | 
       $class->_die_on_insecure($app) unless $ENV{TOADFARM_INSECURE};  | 
| 
54
 | 
1
 | 
 
 | 
 
 | 
 
 | 
 
 | 
6
 | 
       $app->start;  | 
| 
55
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     },  | 
| 
56
 | 
15
 | 
 
 | 
 
 | 
 
 | 
 
 | 
1246
 | 
   );  | 
| 
57
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
58
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
59
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub startup {  | 
| 
60
 | 
19
 | 
 
 | 
 
 | 
  
23
  
 | 
  
1
  
 | 
374172
 | 
   my $self   = shift;  | 
| 
61
 | 
19
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
86
 | 
   my $config = $ENV{MOJO_CONFIG} ? $self->plugin('Config') : {};  | 
| 
62
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
63
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   # remember the config when hot reloading the app  | 
| 
64
 | 
19
 | 
 
 | 
 
 | 
 
 | 
 
 | 
8400
 | 
   $ENV{TOADFARM_CONFIG} = delete $ENV{MOJO_CONFIG};  | 
| 
65
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
66
 | 
19
 | 
 
 | 
 
 | 
 
 | 
 
 | 
63
 | 
   $self->{mounted} = 0;  | 
| 
67
 | 
19
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
425
 | 
   $self->_setup_log($config->{log})                if $config->{log}{file};  | 
| 
68
 | 
19
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
82
 | 
   $self->_paths($config->{paths})                  if $config->{paths};  | 
| 
69
 | 
19
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
89
 | 
   $self->secrets([$config->{secret}])              if $config->{secret};  | 
| 
70
 | 
19
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
63
 | 
   $self->secrets($config->{secrets})               if $config->{secrets};  | 
| 
71
 | 
19
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
55
 | 
   $self->_mount_apps(@{$config->{apps}})           if $config->{apps};  | 
| 
 
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
10
 | 
    | 
| 
72
 | 
19
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
71
 | 
   $self->_load_plugins(@{$config->{tf_plugins}})   if $config->{tf_plugins};  | 
| 
 
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
10
 | 
    | 
| 
73
 | 
19
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
1566
 | 
   $self->_mount_root_app(delete $self->{root_app}) if $self->{root_app};  | 
| 
74
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
75
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
76
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub _change_root {  | 
| 
77
 | 
  
0
  
 | 
 
 | 
 
 | 
  
0
  
 | 
 
 | 
0
 | 
   my @cmd  = @_;  | 
| 
78
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
   my $exit = -2;  | 
| 
79
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
80
 | 
  
0
  
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
0
 | 
   return 1 if $<;    # not root  | 
| 
81
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
82
 | 
  
0
  
 | 
 
 | 
  
  0
  
 | 
 
 | 
 
 | 
0
 | 
   unshift @cmd, $ENV{TOADFARM_CHROOT_BIN} || 'chroot';  | 
| 
83
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
   push @cmd, $^X;  | 
| 
84
 | 
  
0
  
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
0
 | 
   push @cmd, -I => $INC[0] if $ENV{TOADFARM_ACTION} eq 'test';  | 
| 
85
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
   push @cmd, File::Spec->rel2abs($0), @ARGV;  | 
| 
86
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
87
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
   warn "[Toadfarm] system @cmd\n" if DEBUG;  | 
| 
88
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
   system @cmd;  | 
| 
89
 | 
  
0
  
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
0
 | 
   die "Could not run '@cmd' exit=$exit\n" if $exit = $? >> 8;  | 
| 
90
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
   exit $?;  | 
| 
91
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
92
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
93
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub _die_on_insecure {  | 
| 
94
 | 
4
 | 
 
 | 
 
 | 
  
4
  
 | 
 
 | 
7
 | 
   my ($class, $app) = @_;  | 
| 
95
 | 
4
 | 
 
 | 
 
 | 
 
 | 
 
 | 
8
 | 
   my $config  = $app->config;  | 
| 
96
 | 
4
 | 
 
 | 
  
100
  
 | 
 
 | 
 
 | 
32
 | 
   my $plugins = $config->{tf_plugins} || [];  | 
| 
97
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
98
 | 
4
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
17
 | 
   die "Cannot change user without TOADFARM_INSECURE=1"  if $config->{hypnotoad}{user};  | 
| 
99
 | 
3
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
13
 | 
   die "Cannot change group without TOADFARM_INSECURE=1" if $config->{hypnotoad}{group};  | 
| 
100
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   die "Cannot run as 'root' without TOADFARM_INSECURE=1"  | 
| 
101
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     if +($> == 0 or $< == 0)  | 
| 
102
 | 
2
 | 
  
100
  
 | 
  
 33
  
 | 
 
 | 
 
 | 
30
 | 
     and !grep {/\bSetUserGroup$/} @$plugins;  | 
| 
 
 | 
2
 | 
 
 | 
  
 66
  
 | 
 
 | 
 
 | 
13
 | 
    | 
| 
103
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
104
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
105
 | 
  
0
  
 | 
  
  0
  
 | 
 
 | 
  
0
  
 | 
 
 | 
0
 | 
 sub _exit { say shift and exit 0 }  | 
| 
106
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
107
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub _load_plugins {  | 
| 
108
 | 
3
 | 
 
 | 
 
 | 
  
3
  
 | 
 
 | 
5
 | 
   my $self = shift;  | 
| 
109
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
110
 | 
3
 | 
 
 | 
 
 | 
 
 | 
 
 | 
4
 | 
   unshift @{$self->plugins->namespaces}, 'Toadfarm::Plugin';  | 
| 
 
 | 
3
 | 
 
 | 
 
 | 
 
 | 
 
 | 
12
 | 
    | 
| 
111
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
112
 | 
3
 | 
 
 | 
 
 | 
 
 | 
 
 | 
39
 | 
   while (@_) {  | 
| 
113
 | 
3
 | 
 
 | 
 
 | 
 
 | 
 
 | 
9
 | 
     my ($plugin, $config) = (shift @_, shift @_);  | 
| 
114
 | 
3
 | 
 
 | 
 
 | 
 
 | 
 
 | 
11
 | 
     $self->log->info("Loading plugin $plugin");  | 
| 
115
 | 
3
 | 
 
 | 
 
 | 
 
 | 
 
 | 
771
 | 
     $self->plugin($plugin, $config);  | 
| 
116
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   }  | 
| 
117
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
118
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
119
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub _moniker {  | 
| 
120
 | 
1
 | 
 
 | 
 
 | 
  
1
  
 | 
 
 | 
91
 | 
   my $moniker = basename $0;  | 
| 
121
 | 
1
 | 
 
 | 
 
 | 
 
 | 
 
 | 
6
 | 
   $moniker =~ s!\W!_!g;  | 
| 
122
 | 
1
 | 
 
 | 
 
 | 
 
 | 
 
 | 
5
 | 
   $moniker;  | 
| 
123
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
124
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
125
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub _mount_apps {  | 
| 
126
 | 
2
 | 
 
 | 
 
 | 
  
2
  
 | 
 
 | 
4
 | 
   my $self   = shift;  | 
| 
127
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
9
 | 
   my $routes = $self->routes;  | 
| 
128
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
14
 | 
   my $config = $self->config;  | 
| 
129
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
130
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
17
 | 
   while (@_) {  | 
| 
131
 | 
3
 | 
 
 | 
 
 | 
 
 | 
 
 | 
296
 | 
     my ($app, $rules) = (shift @_, shift @_);  | 
| 
132
 | 
3
 | 
 
 | 
 
 | 
 
 | 
 
 | 
18
 | 
     my $server      = Mojo::Server->new;  | 
| 
133
 | 
3
 | 
 
 | 
 
 | 
 
 | 
 
 | 
57
 | 
     my $mount_point = delete $rules->{mount_point};  | 
| 
134
 | 
3
 | 
 
 | 
 
 | 
 
 | 
 
 | 
5
 | 
     my ($request_base, $tmp, @over);  | 
| 
135
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
136
 | 
3
 | 
 
 | 
 
 | 
 
 | 
 
 | 
14
 | 
     local $ENV{MOJO_CONFIG} = $ENV{MOJO_CONFIG};  | 
| 
137
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
138
 | 
3
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
11
 | 
     if (ref $rules->{config} eq 'HASH') {  | 
| 
139
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
10
 | 
       require File::Temp;  | 
| 
140
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
2
 | 
       my %config = (%{$self->config}, %{$rules->{config}});  | 
| 
 
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
5
 | 
    | 
| 
 
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
23
 | 
    | 
| 
141
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
14
 | 
       $tmp = File::Temp->new;  | 
| 
142
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
       Mojo::File->new($tmp->filename)->spurt(  | 
| 
143
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
1017
 | 
         do {  | 
| 
144
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
34
 | 
           local $Data::Dumper::Terse    = 1;  | 
| 
145
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
3
 | 
           local $Data::Dumper::Deepcopy = 1;  | 
| 
146
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
8
 | 
           Data::Dumper::Dumper(\%config);  | 
| 
147
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         }  | 
| 
148
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
       );  | 
| 
149
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
649
 | 
       $ENV{MOJO_CONFIG} = $tmp->filename;  | 
| 
150
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
151
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
152
 | 
3
 | 
  
 50
  
 | 
  
 33
  
 | 
 
 | 
 
 | 
34
 | 
     unless (ref $app and UNIVERSAL::isa($app, 'Mojolicious')) {  | 
| 
153
 | 
3
 | 
 
 | 
 
 | 
 
 | 
 
 | 
8
 | 
       my ($class, $path, @error) = ($app, $app);  | 
| 
154
 | 
3
 | 
  
 50
  
 | 
  
 33
  
 | 
 
 | 
 
 | 
91
 | 
       $path = File::Which::which($path) || class_to_path($path) unless -r $path;  | 
| 
155
 | 
3
 | 
  
 50
  
 | 
  
 33
  
 | 
 
 | 
 
 | 
903
 | 
       $app  = eval { $server->build_app($class) } or push @error, $@ if $class =~ /^[\w:]+$/;  | 
| 
 
 | 
3
 | 
 
 | 
 
 | 
 
 | 
 
 | 
18
 | 
    | 
| 
156
 | 
3
 | 
  
 50
  
 | 
  
  0
  
 | 
 
 | 
 
 | 
16563
 | 
       $app  = eval { $server->load_app($path) }   or push @error, $@ unless ref $app;  | 
| 
 
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    | 
| 
157
 | 
3
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
10
 | 
       die join "\n", @error unless $app;  | 
| 
158
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
159
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
160
 | 
3
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
11
 | 
     $app->log($self->log)         if $config->{log}{combined};  | 
| 
161
 | 
3
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
9
 | 
     $app->secrets($self->secrets) if $config->{tf}{secrets};  | 
| 
162
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
163
 | 
3
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
19
 | 
     if (ref $rules->{config} eq 'HASH') {  | 
| 
164
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
3
 | 
       my $local = delete $rules->{config};  | 
| 
165
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
12
 | 
       $app->config->{$_} = $local->{$_} for keys %$local;  | 
| 
166
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
167
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
168
 | 
3
 | 
 
 | 
  
 66
  
 | 
 
 | 
 
 | 
38
 | 
     $app->config->{$_} ||= $config->{$_} for keys %$config;  | 
| 
169
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
170
 | 
3
 | 
 
 | 
 
 | 
 
 | 
 
 | 
112
 | 
     for my $k (qw(local_port remote_address remote_port)) {  | 
| 
171
 | 
9
 | 
 
 | 
 
 | 
 
 | 
 
 | 
20
 | 
       push @over, $self->_skip_if(tx => $k, delete $rules->{$k});  | 
| 
172
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
173
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
174
 | 
3
 | 
 
 | 
 
 | 
 
 | 
 
 | 
10
 | 
     for my $name (sort keys %$rules) {  | 
| 
175
 | 
3
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
19
 | 
       $request_base = $rules->{$name} if $name eq 'X-Request-Base';  | 
| 
176
 | 
3
 | 
 
 | 
 
 | 
 
 | 
 
 | 
11
 | 
       push @over, $self->_skip_if(header => $name, $rules->{$name});  | 
| 
177
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
178
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
179
 | 
3
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
23
 | 
     if (@over) {  | 
| 
 
 | 
 
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
180
 | 
3
 | 
 
 | 
 
 | 
 
 | 
 
 | 
20
 | 
       $self->log->info("Mounting @{[$app->moniker]} with conditions");  | 
| 
 
 | 
3
 | 
 
 | 
 
 | 
 
 | 
 
 | 
126
 | 
    | 
| 
181
 | 
3
 | 
 
 | 
 
 | 
 
 | 
 
 | 
59
 | 
       unshift @over, "sub { my \$h = \$_[1]->req->headers;\nlocal \$1;";  | 
| 
182
 | 
3
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
14
 | 
       push @over, "\$_[1]->req->url->base(Mojo::URL->new(\$1 || '$request_base'));" if $request_base;  | 
| 
183
 | 
3
 | 
 
 | 
 
 | 
 
 | 
 
 | 
7
 | 
       push @over, "return 1; }";  | 
| 
184
 | 
3
 | 
 
 | 
  
 50
  
 | 
 
 | 
 
 | 
445
 | 
       $routes->add_condition("toadfarm_condition_$self->{mounted}", => eval "@over" || die "@over: $@");  | 
| 
185
 | 
3
 | 
 
 | 
  
 50
  
 | 
 
 | 
 
 | 
51
 | 
       $routes->any($mount_point || '/')->requires("toadfarm_condition_$self->{mounted}")->partial(1)->to(app => $app);  | 
| 
186
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
187
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     elsif ($mount_point) {  | 
| 
188
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
       $routes->any($mount_point)->partial(1)->to(app => $app);  | 
| 
189
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
190
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     else {  | 
| 
191
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
       $self->{root_app} = $app;  | 
| 
192
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
193
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
194
 | 
3
 | 
 
 | 
 
 | 
 
 | 
 
 | 
764
 | 
     $self->{mounted}++;  | 
| 
195
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   }  | 
| 
196
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
197
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
233
 | 
   $self;  | 
| 
198
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
199
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
200
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub _mount_root_app {  | 
| 
201
 | 
  
0
  
 | 
 
 | 
 
 | 
  
0
  
 | 
 
 | 
0
 | 
   my ($self, $app) = @_;  | 
| 
202
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
   $self->log->info("Mounting @{[$app->moniker]} without conditions.");  | 
| 
 
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    | 
| 
203
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
   $self->routes->any('/')->partial(1)->to(app => $app);  | 
| 
204
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
205
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
206
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub _paths {  | 
| 
207
 | 
1
 | 
 
 | 
 
 | 
  
1
  
 | 
 
 | 
15
 | 
   my ($self, $config) = @_;  | 
| 
208
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
209
 | 
1
 | 
 
 | 
 
 | 
 
 | 
 
 | 
4
 | 
   for my $type (qw(renderer static)) {  | 
| 
210
 | 
2
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
16
 | 
     my $paths = $config->{$type} or next;  | 
| 
211
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
9
 | 
     $self->$type->paths($paths);  | 
| 
212
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   }  | 
| 
213
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
214
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
215
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub _pid_file {  | 
| 
216
 | 
4
 | 
 
 | 
 
 | 
  
4
  
 | 
 
 | 
49
 | 
   my ($class, $app) = @_;  | 
| 
217
 | 
4
 | 
 
 | 
 
 | 
 
 | 
 
 | 
88
 | 
   my $name = basename $0;  | 
| 
218
 | 
4
 | 
 
 | 
 
 | 
 
 | 
 
 | 
196
 | 
   my $dir  = dirname abs_path $0;  | 
| 
219
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
220
 | 
4
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
113
 | 
   return File::Spec->catfile($dir,               "$name.pid") if -w $dir;  | 
| 
221
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
   return File::Spec->catfile(File::Spec->tmpdir, "toadfarm-$name.pid");  | 
| 
222
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
223
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
224
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub _run_as {  | 
| 
225
 | 
  
0
  
 | 
 
 | 
  
  0
  
 | 
  
0
  
 | 
 
 | 
0
 | 
   my $user = shift || die "Usage: run_as('username')";  | 
| 
226
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
   my ($exit, $uid, @sudo);  | 
| 
227
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
228
 | 
  
0
  
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
0
 | 
   $uid = $user =~ m!^\d+$! ? $user : scalar getpwnam $user;  | 
| 
229
 | 
  
0
  
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
0
 | 
   die "Could not find uid for user $user\n" unless $uid;  | 
| 
230
 | 
  
0
  
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
0
 | 
   return 1 if $uid == $>;  | 
| 
231
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
232
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
   for my $p (File::Spec->path) {  | 
| 
233
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
     $sudo[0] = File::Spec->catfile($p, 'sudo');  | 
| 
234
 | 
  
0
  
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
0
 | 
     next unless -x $sudo[0];  | 
| 
235
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
     push @sudo, qw(-i -n -u), "#$uid";  | 
| 
236
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
     last;  | 
| 
237
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   }  | 
| 
238
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
239
 | 
  
0
  
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
0
 | 
   die "Cannot change to uid=$uid: 'sudo' was not found.\n" unless @sudo > 1;  | 
| 
240
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
   push @sudo, $^X;  | 
| 
241
 | 
  
0
  
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
0
 | 
   push @sudo, -I => $INC[0] if $ENV{TOADFARM_ACTION} eq 'test';  | 
| 
242
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
   push @sudo, File::Spec->rel2abs($0), @ARGV;  | 
| 
243
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
   warn "[Toadfarm] system @sudo\n" if DEBUG;  | 
| 
244
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
   system @sudo;  | 
| 
245
 | 
  
0
  
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
0
 | 
   die "Could not run '@sudo' exit=$exit\n" if $exit = $? >> 8;  | 
| 
246
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
   exit $?;  | 
| 
247
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
248
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
249
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub _setup_app {  | 
| 
250
 | 
4
 | 
 
 | 
 
 | 
  
4
  
 | 
 
 | 
9
 | 
   my ($class, $app) = @_;  | 
| 
251
 | 
4
 | 
 
 | 
 
 | 
 
 | 
 
 | 
11
 | 
   my $config = $app->config;  | 
| 
252
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
253
 | 
4
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
80
 | 
   $app->secrets([Mojo::Util::md5_sum($$ . $0 . time . rand)]) unless $config->{tf}{secrets};  | 
| 
254
 | 
4
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
25
 | 
   $app->_mount_apps(@{$config->{apps}})         if $config->{apps};  | 
| 
 
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    | 
| 
255
 | 
4
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
10
 | 
   $app->_load_plugins(@{$config->{tf_plugins}}) if $config->{tf_plugins};  | 
| 
 
 | 
1
 | 
 
 | 
 
 | 
 
 | 
 
 | 
5
 | 
    | 
| 
256
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
257
 | 
4
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
405
 | 
   if (my $root_app = delete $app->{root_app}) {  | 
| 
258
 | 
  
0
  
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
0
 | 
     if (@{$config->{apps} || []} == 2) {  | 
| 
 
 | 
  
0
  
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    | 
| 
259
 | 
  
0
  
 | 
 
 | 
  
  0
  
 | 
 
 | 
 
 | 
0
 | 
       my $plugins = $config->{tf_plugins} || [];  | 
| 
260
 | 
  
0
  
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
0
 | 
       $root_app->config(hypnotoad => $config->{hypnotoad}) if $config->{hypnotoad};  | 
| 
261
 | 
  
0
  
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
0
 | 
       $root_app->log($app->log)                            if $config->{tf}{logging};  | 
| 
262
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
       $root_app->plugin(shift(@$plugins), shift(@$plugins)) for @$plugins;  | 
| 
263
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
       $root_app->secrets($app->secrets);  | 
| 
264
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
       push @{$root_app->commands->namespaces}, 'Toadfarm::Command';  | 
| 
 
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    | 
| 
265
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
       return $root_app;  | 
| 
266
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
267
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     else {  | 
| 
268
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
       $app->_mount_root_app($root_app);  | 
| 
269
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
270
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   }  | 
| 
271
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
272
 | 
4
 | 
 
 | 
 
 | 
 
 | 
 
 | 
8
 | 
   return $app;  | 
| 
273
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
274
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
275
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub _setup_log {  | 
| 
276
 | 
1
 | 
 
 | 
 
 | 
  
1
  
 | 
 
 | 
3
 | 
   my ($self, $config) = @_;  | 
| 
277
 | 
1
 | 
 
 | 
 
 | 
 
 | 
 
 | 
17
 | 
   my $log = Mojo::Log->new;  | 
| 
278
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
279
 | 
1
 | 
 
 | 
 
 | 
 
 | 
 
 | 
26
 | 
   $self->config(log => $config);  | 
| 
280
 | 
1
 | 
  
 50
  
 | 
  
 33
  
 | 
 
 | 
 
 | 
22
 | 
   $log->path($config->{path}) if $config->{path} ||= delete $config->{file};  | 
| 
281
 | 
1
 | 
 
 | 
  
 50
  
 | 
 
 | 
 
 | 
10
 | 
   $log->level($config->{level} || 'info');  | 
| 
282
 | 
1
 | 
 
 | 
 
 | 
 
 | 
 
 | 
10
 | 
   $self->log($log);  | 
| 
283
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
284
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
285
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub _skip_if {  | 
| 
286
 | 
12
 | 
 
 | 
 
 | 
  
12
  
 | 
 
 | 
18
 | 
   my ($self, $type, $k, $value) = @_;  | 
| 
287
 | 
12
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
28
 | 
   my $format = $type eq 'tx' ? '$_[1]->tx->%s' : $type eq 'header' ? q[$h->header('%s')] : q[INVALID(%s)];  | 
| 
 
 | 
 
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
288
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
289
 | 
12
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
25
 | 
   if (!defined $value) {  | 
| 
 
 | 
 
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
290
 | 
9
 | 
 
 | 
 
 | 
 
 | 
 
 | 
16
 | 
     return;  | 
| 
291
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   }  | 
| 
292
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   elsif (ref $value eq 'Regexp') {  | 
| 
293
 | 
1
 | 
 
 | 
 
 | 
 
 | 
 
 | 
3
 | 
     $value =~ s,(?
 | 
| 
294
 | 
1
 | 
 
 | 
 
 | 
 
 | 
 
 | 
8
 | 
     return sprintf "return 0 unless +($format || '') =~ /(%s)/;", $k, $value;  | 
| 
295
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   }  | 
| 
296
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   else {  | 
| 
297
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
13
 | 
     return sprintf "return 0 unless +($format || '') eq '%s';", $k, $value;  | 
| 
298
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   }  | 
| 
299
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
300
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
301
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 1;  | 
| 
302
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
303
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =encoding utf8  | 
| 
304
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
305
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =head1 NAME  | 
| 
306
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
307
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 Toadfarm - One Mojolicious app to rule them all  | 
| 
308
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
309
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =head1 VERSION  | 
| 
310
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
311
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 0.84  | 
| 
312
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
313
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =head1 DESCRIPTION  | 
| 
314
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
315
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 Toadfarm is a module for configuring and starting your L  | 
| 
316
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 applications. You can either combine multiple applications in one script,  | 
| 
317
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 or just use it as a init script.  | 
| 
318
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
319
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 Core features:  | 
| 
320
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
321
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =over 4  | 
| 
322
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
323
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =item *  | 
| 
324
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
325
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 Wrapper around L that makes your  | 
| 
326
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 application L  | 
| 
327
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 compatible.  | 
| 
328
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
329
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =item *  | 
| 
330
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
331
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 Advanced routing and virtual host configuration. Also support routing  | 
| 
332
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 from behind another web server, such as L.  | 
| 
333
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 This feature is very much like L on steroids.  | 
| 
334
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
335
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =item *  | 
| 
336
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
337
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 Hijacking log messages to a common log file. There's also plugin,  | 
| 
338
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 L, that allows you to log the requests sent  | 
| 
339
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 to your server.  | 
| 
340
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
341
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =back  | 
| 
342
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
343
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =head1 SYNOPSIS  | 
| 
344
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
345
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =head2 Script  | 
| 
346
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
347
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 Here is an example script that sets up logging and mounts some applications  | 
| 
348
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 under different domains, as well as loading in some custom plugins.  | 
| 
349
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
350
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 See L for more information about the different functions.  | 
| 
351
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
352
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   #!/usr/bin/perl  | 
| 
353
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   use Toadfarm -init;  | 
| 
354
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
355
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   logging {  | 
| 
356
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     combined => 1,  | 
| 
357
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     file     => "/var/log/toadfarm/app.log",  | 
| 
358
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     level    => "info",  | 
| 
359
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   };  | 
| 
360
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
361
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   mount "MyApp"  => {  | 
| 
362
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     Host   => "myapp.example.com",  | 
| 
363
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     config => {  | 
| 
364
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
       config_parameter_for_myapp => "foo"  | 
| 
365
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     },  | 
| 
366
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   };  | 
| 
367
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
368
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   mount "/path/to/app" => {  | 
| 
369
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     Host        => "example.com",  | 
| 
370
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     mount_point => "/other",  | 
| 
371
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   };  | 
| 
372
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
373
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   mount "Catch::All::App";  | 
| 
374
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
375
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   plugin "Toadfarm::Plugin::AccessLog";  | 
| 
376
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
377
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   start; # needs to be at the last line  | 
| 
378
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
379
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =head2 Usage  | 
| 
380
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
381
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 You don't have to put L in init.d, but it will work with standard  | 
| 
382
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 start/stop actions.  | 
| 
383
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
384
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   $ /etc/init.d/your-script reload  | 
| 
385
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   $ /etc/init.d/your-script start  | 
| 
386
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   $ /etc/init.d/your-script stop  | 
| 
387
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
388
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 See also L for more details.  | 
| 
389
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
390
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 You can also start the application with normal L commands:  | 
| 
391
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
392
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   $ morbo /etc/init.d/your-script  | 
| 
393
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   $ /etc/init.d/your-script daemon  | 
| 
394
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
395
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =head1 DOCUMENTATION INDEX  | 
| 
396
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
397
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =over 4  | 
| 
398
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
399
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =item * L - Introduction.  | 
| 
400
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
401
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =item * L - Domain specific language for Toadfarm.  | 
| 
402
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
403
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =item * L - Config file format.  | 
| 
404
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
405
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =item * L - Command line options.  | 
| 
406
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
407
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =item * L - Toadfarm behind nginx.  | 
| 
408
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
409
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =item * L - Virtual host setup.  | 
| 
410
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
411
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =back  | 
| 
412
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
413
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =head1 PLUGINS  | 
| 
414
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
415
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =over 4  | 
| 
416
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
417
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =item * L  | 
| 
418
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
419
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 Log each request that hit your application.  | 
| 
420
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
421
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =item * L  | 
| 
422
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
423
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 Kill Hypnotoad workers if they grow too large.  | 
| 
424
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
425
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =item * L  | 
| 
426
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
427
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 Start as root, run workers as less user. See also  | 
| 
428
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 L.  | 
| 
429
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
430
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =back  | 
| 
431
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
432
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =head1 PREVIOUS VERSIONS  | 
| 
433
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
434
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 L prior to version 0.49 used to be a configuration file loaded in  | 
| 
435
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 by the C script. This resulted in all the executables to be named  | 
| 
436
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 C instead of something descriptive. It also felt a bit awkward to  | 
| 
437
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 take over C and use all the crazy hacks to start C.  | 
| 
438
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
439
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 It also didn't work well as an init script, so there still had to be a  | 
| 
440
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 seperate solution for that.  | 
| 
441
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
442
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 The new L DSL aim to solve all of these issues. This means that  | 
| 
443
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 if you decide to still use any C, it should be for the  | 
| 
444
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 applications loaded from inside C and not the startup script.  | 
| 
445
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
446
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 Note that the old solution still works, but a warning tells you to change  | 
| 
447
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 to the new L based API.  | 
| 
448
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
449
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =head1 COPYRIGHT AND LICENSE  | 
| 
450
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
451
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 Copyright (C) 2014, Jan Henning Thorsen  | 
| 
452
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
453
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 This program is free software, you can redistribute it and/or modify it  | 
| 
454
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 under the terms of the Artistic License version 2.0.  | 
| 
455
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
456
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =head1 AUTHOR  | 
| 
457
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
458
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 Jan Henning Thorsen - C  | 
| 
459
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
460
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =cut  |