File Coverage

lib/POEST/Server.pm
Criterion Covered Total %
statement 7 9 77.7
branch n/a
condition n/a
subroutine 3 3 100.0
pod n/a
total 10 12 83.3


line stmt bran cond sub pod time code
1             # $Id: Server.pm,v 1.3 2003/04/08 00:27:30 cwest Exp $
2             package POEST::Server;
3              
4             =pod
5              
6             =head1 NAME
7              
8             POEST::Server - The Poest Master General
9              
10             =head1 SYNOPSIS
11              
12             my $server = POEST::Server->new(
13             Config => 'POEST::Config::Genearl',
14             ConfigFile => '/etc/poest.conf',
15             );
16            
17             $server->start;
18              
19             =head1 ABSTRACT
20              
21             This module controls the server itself.
22              
23             =head1 DESCRIPTION
24              
25             All high-level server interaction happens here.
26              
27             =cut
28              
29 1     1   48923 use strict;
  1         2  
  1         60  
30             $^W = 1;
31             $0 = 'poest';
32              
33 1     1   176 use vars qw[$VERSION];
  1         2  
  1         106  
34             $VERSION = (qw$Revision: 1.3 $)[1];
35              
36 1     1   705 use POE qw[Component::Server::SMTP];
  0            
  0            
37             use Carp;
38             use POSIX ();
39              
40             sub CONFIG () { [ qw[plugin hostname port pidfile] ] }
41              
42             =head2 new()
43              
44             Create a new server instance. This will not make the server run, but
45             it will configure it, load the modules and configure them, and spawn
46             the proper POE sessions. All the parameters passed to new will be
47             passed directly to the configurator of your choice, as defined by the
48             C parameter (L by default).
49              
50             =cut
51              
52             sub new {
53             my ($class, %args) = @_;
54              
55             my $self = bless \%args, $class;
56              
57             $self->{Config} ||= 'POEST::Config';
58              
59             eval qq[use $self->{Config}];
60             croak $@ if $@;
61             croak "$self->{Config} is broken, please fix it or use another"
62             unless $self->{Config}->can( 'new' ) &&
63             $self->{Config}->can( 'config' ) &&
64             $self->{Config}->can( 'set' ) &&
65             $self->{Config}->can( 'get' );
66              
67             $self->{config} = $self->{Config}->new( %args );
68              
69             my %config = %{ $self->{config}->get( @{$self->CONFIG} ) };
70              
71             $self->{conf} = \%config;
72              
73             croak "No plugins configured, server would be useless"
74             unless exists $config{plugin};
75              
76             my (@plugins) = ref $config{plugin} ?
77             @{ $config{plugin} } : $config{plugin};
78              
79             my @pobjects = ();
80             foreach my $plugin ( @plugins ) {
81             eval qq[use $plugin];
82             croak $@ if $@;
83             croak "$plugin is broken, it doesn't conform to POEST::Plugin specs"
84             unless $plugin->can( 'new' ) &&
85             $plugin->can( 'EVENTS' ) &&
86             $plugin->can( 'CONFIG' );
87              
88             my $pobject = $plugin->new(
89             %{ $self->{config}->get( @{ $plugin->CONFIG } ) }
90             );
91             push @pobjects, $pobject, $plugin->EVENTS;
92             }
93            
94             $self->{pobjects} = \@pobjects;
95              
96             return $self;
97             }
98              
99             =head2 run()
100              
101             Make the server run. This will block execution when called directly.
102              
103             =cut
104              
105             sub run {
106             my ($self) = @_;
107              
108             POE::Component::Server::SMTP->spawn(
109             Hostname => $self->{conf}->{hostname},
110             Port => $self->{conf}->{port},
111             ObjectStates => $self->{pobjects},
112             );
113              
114             $poe_kernel->run;
115             exit(0);
116             }
117              
118             =head2 start()
119              
120             Fork and start the server. This method will return the pid of the
121             server. If the C configuration parameter is found in the
122             configuration class, an attempt is made to write that pid file. If
123             that attempt fails, or if the pid file already exists, and exception
124             is thrown and the attempt to start the server is stalled.
125              
126             =cut
127              
128             sub start {
129             my ($self) = shift;
130            
131             my $pid = fork;
132             if ( ! defined $pid ) {
133             croak "Couldn't start poest: $!";
134             } elsif ($pid) {
135             $self->{pid} = $pid;
136             if ( $self->{conf}->{pidfile} ) {
137             croak "PID file already exists, delete it and start again."
138             if -e $self->{conf}->{pidfile};
139              
140             open PID, "> $self->{conf}->{pidfile}"
141             or die "Can't write $self->{conf}->{pidfile}: $!\n";
142             flock PID, 2;
143             print PID "$self->{pid}\n";
144             flock PID, 8;
145             close PID or die "Can't close $self->{conf}->{pidfile}: $!\n";
146             }
147              
148             POSIX::setsid();
149             umask(0);
150              
151             open(STDIN, '
152             open(STDOUT, '>/dev/null');
153             open(STDERR, '>&STDOUT' );
154              
155             exit 0;
156             }
157              
158             $self->run;
159             }
160              
161             =head2 stop()
162              
163             Stop the server. If a pidfile was specified, the pid will be read
164             from it. Otherwise, an attempt to find a process name with the value
165             of C<$0> is tried, by default that is set to 'poest'.
166              
167             B: As of right this minute, the process table magic isn't written
168             as L isn't ported to Darwin.
169              
170             =cut
171              
172             sub stop {
173             my ($self) = @_;
174            
175             if ( $self->{conf}->{pidfile} ) {
176             croak "PID file doesn't exist, manual kill is required."
177             unless -e $self->{conf}->{pidfile};
178             open PID, "< $self->{conf}->{pidfile}"
179             or die "Can't read $self->{conf}->{pidfile}: $!\n";
180             chomp( $self->{pid} = );
181             close PID or die "Can't close $self->{conf}->{pidfile}: $!\n";
182             unlink $self->{conf}->{pidfile}
183             or die "Can't unlink $self->{conf}->{pidfile}: $!\n";
184             } elsif ( $self->{pid} ) {
185             # Cool.
186             } else {
187             croak "PID file was not specified in the configuration."
188             }
189            
190             if ( kill 0, $self->{pid} ) {
191             kill 15, $self->{pid};
192             } else {
193             croak "Server is not running.";
194             }
195             }
196              
197             1;
198              
199             __END__