File Coverage

blib/lib/Lighttpd/Control.pm
Criterion Covered Total %
statement 1 3 33.3
branch n/a
condition n/a
subroutine 1 1 100.0
pod n/a
total 2 4 50.0


line stmt bran cond sub pod time code
1             package Lighttpd::Control;
2 4     4   852231 use Moose;
  0            
  0            
3             use MooseX::Types::Path::Class;
4             use Path::Class;
5              
6             our $VERSION = '0.03';
7             our $AUTHORITY = 'cpan:STEVAN';
8              
9             has 'config_file' => (
10             is => 'rw',
11             isa => 'Path::Class::File',
12             coerce => 1,
13             trigger => sub {
14             my $self = shift;
15             $self->_clear_pid_file;
16             $self->_clear_server_pid;
17             }
18             );
19              
20             has 'binary_path' => (
21             is => 'rw',
22             isa => 'Path::Class::File',
23             coerce => 1,
24             lazy => 1,
25             builder => '_find_binary_path',
26             clearer => '_clear_binary_path',
27             );
28              
29             has 'pid_file' => (
30             is => 'rw',
31             isa => 'Path::Class::File',
32             coerce => 1,
33             lazy => 1,
34             builder => '_find_pid_file',
35             clearer => '_clear_pid_file',
36             predicate => 'has_pid_file',
37             trigger => sub {
38             my $self = shift;
39             $self->_clear_server_pid;
40             }
41             );
42              
43             has 'server_pid' => (
44             init_arg => undef,
45             is => 'ro',
46             isa => 'Int',
47             lazy => 1,
48             builder => '_find_server_pid',
49             clearer => '_clear_server_pid',
50             predicate => 'has_server_pid',
51             );
52              
53             has 'binary_path_prefixes_to_search' => (
54             is => 'ro',
55             isa => 'ArrayRef',
56             default => sub { [ qw(/usr /usr/local /opt/local /sw /usr/pkg) ] },
57             );
58              
59             sub log { shift; warn @_, "\n" }
60              
61             ## ---------------------------------
62             ## events
63              
64             sub pre_startup {
65             my $self = shift;
66             $self->_clear_server_pid;
67             inner();
68             }
69             sub post_startup {
70             my $self = shift;
71             inner();
72             until ($self->is_server_running) {
73             $self->log("... waiting for server to start");
74             }
75             }
76              
77             sub pre_shutdown { inner() }
78              
79             sub post_shutdown {
80             my $self = shift;
81             inner();
82             $self->_clear_server_pid;
83             }
84              
85             ## ---------------------------------
86              
87             sub _find_server_pid {
88             my $self = shift;
89             my $pid = $self->pid_file->slurp(chomp => 1);
90             ($pid)
91             || confess "No PID found in pid_file (" . $self->pid_file . ")";
92             $pid;
93             }
94              
95             sub _find_pid_file {
96             my $self = shift;
97              
98             my $config_file = $self->config_file;
99              
100             (-f $config_file)
101             || confess "Could not find pid_file because could not find config file ($config_file)";
102              
103             # the two possible approaches to
104             # find the pid file (that I know of)
105             my @approaches = (
106             sub { $config_file->slurp(chomp => 1) },
107             sub {
108             # NOTE:
109             # if we couldn't get the full path
110             # from the config file itself, then
111             # we use the -p option on the lighttpd
112             # binary to give us the parsed config
113             # which will have the full path in it.
114             # - SL
115             my $cli = join " " => $self->_construct_command_line('-p');
116             `$cli`;
117             }
118             );
119              
120             foreach my $approach (@approaches) {
121             my @config = $approach->();
122             foreach my $line (@config) {
123             if ($line =~ /server\.pid\-file\s*\=\s*(.*)/) {
124             my $pid_file = $1;
125             # NOTE:
126             # pid file from the config must
127             # be a valid path, which means
128             # it must start and end with quotes
129             # - SL
130             if ($pid_file =~ /^\"(.*)\"$/) {
131             return Path::Class::File->new($1);
132             }
133             }
134             }
135             }
136              
137             confess "Could not locate the pid-file information, please supply it manually";
138             }
139              
140             sub _find_binary_path {
141             my $self = shift;
142              
143             my $lighttpd = do {
144             my $bin = `which lighttpd`;
145             chomp($bin);
146             Path::Class::File->new($bin)
147             };
148              
149             return $lighttpd if -x $lighttpd;
150              
151             for my $prefix ( @{ $self->binary_path_prefixes_to_search } ) {
152             for my $bindir (qw(bin sbin)) {
153             my $lighttpd = Path::Class::File->new($prefix, $bindir, 'lighttpd');
154             return $lighttpd if -x $lighttpd;
155             }
156             }
157              
158             confess "can't find lighttpd anywhere tried => (" . ($lighttpd || 'nothing') . ")";
159             }
160              
161             sub _construct_command_line {
162             my $self = shift;
163             my @opts = @_;
164             my $conf = $self->config_file;
165              
166             (-f $conf)
167             || confess "Could not locate configuration file ($conf)";
168              
169             ($self->binary_path, @opts, '-f', $conf->stringify);
170             }
171              
172             ## ---------------------------------
173              
174             sub is_server_running {
175             my $self = shift;
176             # no pid file, no server running ...
177             return 0 unless -s $self->pid_file;
178             # has pid file, then check it ...
179             kill(0, $self->server_pid) ? 1 : 0;
180             }
181              
182             sub start {
183             my $self = shift;
184              
185             $self->log("Starting lighttpd ...");
186             $self->pre_startup;
187              
188             # NOTE:
189             # do this after startup so that it
190             # would be possible to write the
191             # config file in the pre_startup
192             # hook if we wanted to.
193             # - SL
194             my @cli = $self->_construct_command_line;
195              
196             unless (system(@cli) == 0) {
197             $self->log("Could not start lighttpd (@cli) exited with status $?");
198             return;
199             }
200              
201             $self->post_startup;
202             $self->log("Lighttpd started.");
203             }
204              
205             sub stop {
206             my $self = shift;
207             my $pid_file = $self->pid_file;
208              
209             if (-f $pid_file) {
210              
211             if (!$self->is_server_running) {
212             $self->log("Found pid_file($pid_file), but process does not seem to be running.");
213             return;
214             }
215              
216             $self->log("Stoping lighttpd ...");
217             $self->pre_shutdown;
218              
219             kill 2, $self->server_pid;
220              
221             $self->post_shutdown;
222             $self->log("Lighttpd stopped.");
223              
224             return;
225             }
226              
227             $self->log("... pid_file($pid_file) not found.");
228             }
229              
230             no Moose; 1;
231              
232             __END__
233              
234             =pod
235              
236             =head1 NAME
237              
238             Lighttpd::Control - Simple class to manage a Lighttpd server
239              
240             =head1 SYNOPSIS
241              
242             #!perl
243              
244             use strict;
245             use warnings;
246              
247             use Lighttpd::Control;
248              
249             my ($command) = @ARGV;
250              
251             my $ctl = Lighttpd::Control->new(
252             config_file => [qw[ conf lighttpd.conf ]],
253             # PID file can also be discovered automatically
254             # from the conf, or if you prefer you can specify
255             pid_file => 'lighttpd.control.pid',
256             );
257              
258             $ctl->start if lc($command) eq 'start';
259             $ctl->stop if lc($command) eq 'stop';
260              
261             =head1 DESCRIPTION
262              
263             This is a packaging and cleaning up of a script we have been using
264             for a while now to manage our Lighttpd servers. This is an early
265             release with only the bare bones functionality we needed, future
266             releases will surely include more functionality. Suggestions and
267             crazy ideas welcomed, especially in the form of patches with tests.
268              
269             Also note the recently uploaded L<Nginx::Control> and L<Sphinx::Control>
270             both of which are based on this module but for different servers.
271              
272             =head1 ATTRIBUTES
273              
274             =over 4
275              
276             =item I<config_file>
277              
278             This is a L<Path::Class::File> instance for the configuration file.
279              
280             =item I<binary_path>
281              
282             This is a L<Path::Class::File> instance pointing to the Lighttpd
283             binary. This can be autodiscovered or you can specify it via the
284             constructor.
285              
286             =item I<pid_file>
287              
288             This is a L<Path::Class::File> instance pointing to the Lighttpd
289             pid file. This can be autodiscovered from the config file or you
290             can specify it via the constructor.
291              
292             =item I<server_pid>
293              
294             This is the PID of the live server.
295              
296             =item I<binary_path_prefixes_to_search>
297              
298             This is a list of path prefixes in which we will attempt to find
299             your lighttpd install in. The default list provided is; /usr,
300             /usr/local, /opt/local, /sw, and /usr/pkg, which should cover most
301             cases, but you can override in the constructor if not.
302              
303             =back
304              
305             =head1 METHODS
306              
307             =over 4
308              
309             =item B<start>
310              
311             Starts the Lighttpd server that is currently being controlled by this
312             instance. It will also run the pre_startup and post_startup hooks.
313              
314             =item B<stop>
315              
316             Stops the Lighttpd server that is currently being controlled by this
317             instance. It will also run the pre_shutdown and post_shutdown hooks.
318              
319             =item B<is_server_running>
320              
321             Checks to see if the Lighttpd server that is currently being controlled
322             by this instance is running or not (based on the state of the PID file).
323              
324             =item B<log>
325              
326             Simple logger that you can use, it just sends the output to STDERR via
327             the C<warn> function.
328              
329             =back
330              
331             =head1 AUGMENTABLE METHODS
332              
333             These methods can be augmented in a subclass to add extra functionality
334             to your control script. Here is an example of how they might be used
335             to integrate with L<FCGI::Engine::Manager> (For a complete, working
336             version of this, take a look at the file F<003_basic_with_fcgi_engine.t>
337             in the test suite).
338              
339             package My::Lighttpd::Control;
340             use Moose;
341              
342             extends 'Lighttpd::Control';
343              
344             has 'fcgi_manager' => (
345             is => 'ro',
346             isa => 'FCGI::Engine::Manager',
347             default => sub {
348             FCGI::Engine::Manager->new(
349             conf => 'conf/fcgi.engine.yml'
350             )
351             },
352             );
353              
354             augment post_startup => sub {
355             my $self = shift;
356             $self->log('Starting the FCGI Engine Manager ...');
357             $self->fcgi_manager->start;
358             };
359              
360             augment post_shutdown => sub {
361             my $self = shift;
362             $self->log('Stopping the FCGI Engine Manager ...');
363             $self->fcgi_manager->stop;
364             };
365              
366             =over 4
367              
368             =item B<pre_startup>
369              
370             This will clear the I<server_pid> attribute before doing anything
371             else so that it can start with a clean slate.
372              
373             =item B<post_startup>
374              
375             This will initialize the L<server_pid> attribute and block while
376             the server itself is starting up (and print a nice log message too).
377              
378             =item B<pre_shutdown>
379              
380             =item B<post_shutdown>
381              
382             This will clear the I<server_pid> attribute as the last thing it does
383             so that there is not stale data in the instance.
384              
385             =back
386              
387             =head1 BUGS
388              
389             All complex software has bugs lurking in it, and this module is no
390             exception. If you find a bug please either email me, or add the bug
391             to cpan-RT.
392              
393             =head1 AUTHOR
394              
395             Stevan Little E<lt>stevan.little@iinteractive.comE<gt>
396              
397             Based on code originally developed by Chris Prather.
398              
399             =head1 COPYRIGHT AND LICENSE
400              
401             Copyright 2008-2009 Infinity Interactive, Inc.
402              
403             L<http://www.iinteractive.com>
404              
405             This library is free software; you can redistribute it and/or modify
406             it under the same terms as Perl itself.
407              
408             =cut