allow for custom open() args for stdout_file and stderr_file

This commit is contained in:
Zoffix Znet 2015-08-01 16:48:40 -04:00
parent 79840ca607
commit 7e7ed7fe62
5 changed files with 207 additions and 19 deletions

View file

@ -1,3 +1,6 @@
* allow for custom open() args for stdout_file and stderr_file
0.001007 2015-07-27 SymKat <symkat@symkat.com>
* Module name POD format fixed (RT 93280) * Module name POD format fixed (RT 93280)
* Add "forground" to --help (by marcusramberg) * Add "forground" to --help (by marcusramberg)
* Add with_plugins to support a simple plugin system * Add with_plugins to support a simple plugin system

View file

@ -1,5 +1,5 @@
use inc::Module::Install; use inc::Module::Install;
# Define metadata # Define metadata
name 'Daemon-Control'; name 'Daemon-Control';
all_from 'lib/Daemon/Control.pm'; all_from 'lib/Daemon/Control.pm';
@ -15,5 +15,6 @@ requires 'Cwd' => '0';
requires 'File::Path' => '2.08'; requires 'File::Path' => '2.08';
test_requires 'Test::More' => '0.88'; test_requires 'Test::More' => '0.88';
test_requires 'File::Temp' => '0.14';
WriteAll; WriteAll;

View file

@ -136,7 +136,7 @@ sub with_plugins {
my @plugins = map { my @plugins = map {
substr( $_, 0, 1 ) eq '+' substr( $_, 0, 1 ) eq '+'
? substr( $_, 1 ) ? substr( $_, 1 )
: "Daemon::Control::Plugin::$_" : "Daemon::Control::Plugin::$_"
} ref $in[0] eq 'ARRAY' ? @{ $in[0] } : @in; } ref $in[0] eq 'ARRAY' ? @{ $in[0] } : @in;
@ -185,17 +185,36 @@ sub redirect_filehandles {
if ( $self->stdout_file ) { if ( $self->stdout_file ) {
my $file = $self->stdout_file; my $file = $self->stdout_file;
$file = $file eq '/dev/null' ? File::Spec->devnull : $file; $file = $file eq '/dev/null' ? File::Spec->devnull : $file;
open STDOUT, ">>", $file
or die "Failed to open STDOUT to $file: $!";
$self->trace( "STDOUT redirected to $file" );
if ( ref $file eq 'ARRAY' ) {
my $mode = shift @$file;
open STDOUT, $mode, @$file ? @$file : ()
or die "Failed to open STDOUT with args $mode @$file: $!";
$self->trace("STDOUT redirected to open(STDOUT $mode @$file)");
}
else {
open STDOUT, ">>", $file
or die "Failed to open STDOUT to $file: $!";
$self->trace( "STDOUT redirected to $file" );
}
} }
if ( $self->stderr_file ) { if ( $self->stderr_file ) {
my $file = $self->stderr_file; my $file = $self->stderr_file;
$file = $file eq '/dev/null' ? File::Spec->devnull : $file; $file = $file eq '/dev/null' ? File::Spec->devnull : $file;
open STDERR, ">>", $file
or die "Failed to open STDERR to $file: $!"; if ( ref $file eq 'ARRAY' ) {
$self->trace( "STDERR redirected to $file" ); my $mode = shift @$file;
open STDERR, $mode, @$file ? @$file : ()
or die "Failed to open STDERR with args $mode @$file: $!";
$self->trace("STDERR redirected to open(STDERR $mode @$file)");
}
else {
open STDERR, ">>", $file
or die "Failed to open STDERR to $file: $!";
$self->trace("STDERR redirected to $file");
}
} }
} }
@ -294,7 +313,7 @@ sub _double_fork {
return $self; return $self;
} }
sub _foreground { shift->_launch_program } sub _foreground { shift->_launch_program }
sub _fork { sub _fork {
my ( $self ) = @_; my ( $self ) = @_;
@ -644,7 +663,7 @@ sub run_template {
sub run_command { sub run_command {
my ( $self, $arg ) = @_; my ( $self, $arg ) = @_;
# Error Checking. # Error Checking.
if ( ! $self->program ) { if ( ! $self->program ) {
die "Error: program must be defined."; die "Error: program must be defined.";
@ -914,6 +933,11 @@ in double fork mode.
$daemon->stdout_file( "/tmp/mydaemon.stdout" ); $daemon->stdout_file( "/tmp/mydaemon.stdout" );
Alternatively, you can specify an arrayref of arguments to C<open()>:
$daemon->stdout_file( [ '>', '/tmp/overwrite-every-run' ] );
$daemon->stdout_file( [ '|-', 'my_pipe_program', '-a foo' ] );
=head2 stderr_file =head2 stderr_file
If provided stderr will be redirected to the given file. This is only supported If provided stderr will be redirected to the given file. This is only supported
@ -921,6 +945,11 @@ in double fork mode.
$daemon->stderr_file( "/tmp/mydaemon.stderr" ); $daemon->stderr_file( "/tmp/mydaemon.stderr" );
Alternatively, you can specify an arrayref of arguments to C<open()>:
$daemon->stderr_file( [ '>', '/tmp/overwrite-every-run' ] );
$daemon->stderr_file( [ '|-', 'my_pipe_program', '-a foo' ] );
=head2 pid_file =head2 pid_file
The location of the PID file to use. Warning: if using single-fork mode, it is The location of the PID file to use. Warning: if using single-fork mode, it is
@ -972,7 +1001,7 @@ as that the daemon started. A shortcut to turn status off and go into foregroun
mode is C<foreground> being set to 1, or C<DC_FOREGROUND> being set as an mode is C<foreground> being set to 1, or C<DC_FOREGROUND> being set as an
environment variable. Additionally, calling C<foreground> instead of C<start> will environment variable. Additionally, calling C<foreground> instead of C<start> will
override the forking mode at run-time. override the forking mode at run-time.
$daemon->fork( 0 ); $daemon->fork( 0 );
$daemon->fork( 1 ); $daemon->fork( 1 );
@ -1072,13 +1101,13 @@ Your plugin should use the name Daemon::Control::Plugin::YourModuleName and
YourModuleName should reasonably match the effect your plugin has on YourModuleName should reasonably match the effect your plugin has on
Daemon::Control. Daemon::Control.
You can replace Daemon::Control methods by writing your own and using You can replace Daemon::Control methods by writing your own and using
Role::Tiny within your class to allow it to be composed into Daemon::Control. Role::Tiny within your class to allow it to be composed into Daemon::Control.
The default Daemon::Control ships with no dependancies and supports Perl The default Daemon::Control ships with no dependancies and supports Perl
5.8.1+, to use the plugin system your module MUST declare dependency on 5.8.1+, to use the plugin system your module MUST declare dependency on
L<Role::Tiny> and if you wish to use the C<around>, C<before> and C<after> L<Role::Tiny> and if you wish to use the C<around>, C<before> and C<after>
your module MUST declare dependance on L<Class::Method::Modifiers> in your your module MUST declare dependance on L<Class::Method::Modifiers> in your
package. package.
=head1 METHODS =head1 METHODS
@ -1086,7 +1115,7 @@ package.
=head2 run_command =head2 run_command
This function will process an action on the Daemon::Control instance. This function will process an action on the Daemon::Control instance.
Valid arguments are those which a C<do_> method exists for, such as Valid arguments are those which a C<do_> method exists for, such as
B<start>, B<stop>, B<restart>. Returns the LSB exit code for the B<start>, B<stop>, B<restart>. Returns the LSB exit code for the
action processed. action processed.
@ -1095,7 +1124,7 @@ action processed.
This will make your program act as an init file, accepting input from This will make your program act as an init file, accepting input from
the command line. Run will exit with 0 for success and uses LSB exit the command line. Run will exit with 0 for success and uses LSB exit
codes. As such no code should be used after ->run is called. Any code codes. As such no code should be used after ->run is called. Any code
in your file should be before this. This is a shortcut for in your file should be before this. This is a shortcut for
exit Daemon::Control->new(...)->run_command( @ARGV ); exit Daemon::Control->new(...)->run_command( @ARGV );
@ -1108,7 +1137,7 @@ exits. Called by:
=head2 do_foreground =head2 do_foreground
Is called when B<foreground> is given as an argument. Starts the Is called when B<foreground> is given as an argument. Starts the
program or code reference and stays in the foreground -- no forking program or code reference and stays in the foreground -- no forking
is done, regardless of the compile-time arguments. Additionally, is done, regardless of the compile-time arguments. Additionally,
turns C<quiet> on to avoid showing L<Daemon::Control> output. turns C<quiet> on to avoid showing L<Daemon::Control> output.
@ -1198,6 +1227,8 @@ Kaitlyn Parkhurst (SymKat) I<E<lt>symkat@symkat.comE<gt>> ( Blog: L<http://symka
=item * Mark Curtis I<E<lt>mark.curtis@affinitylive.com<gt>> =item * Mark Curtis I<E<lt>mark.curtis@affinitylive.com<gt>>
=item * Zoffix Znet I<E<lt>zoffix@cpan.org<gt>>
=back =back
=head2 SPONSORS =head2 SPONSORS

123
t/06_stderr_stdout.t Normal file
View file

@ -0,0 +1,123 @@
#!/usr/bin/perl
use warnings;
use strict;
use Test::More;
use File::Temp;
my ( $file, $ilib );
# Let's make it so people can test in t/ or in the dist directory.
my $daemon = '05_stderr_stdout.pl';
if ( -f "t/bin/$daemon" ) { # Dist Directory.
$file = "t/bin/$daemon";
$ilib = "lib";
} elsif ( -f "bin/$daemon" ) {
$file = "bin/$daemon";
$ilib = "../lib";
} else {
die "Tests should be run in the dist directory or t/";
}
sub get_command_output {
my ( @command ) = @_;
open my $lf, "-|", @command
or die "Couldn't get pipe to '@command': $!";
my $content = do { local $/; <$lf> };
close $lf;
return $content;
}
{
diag 'Test STDOUT and STDERR when we use plain strings as arguments';
my $out;
my $stdout = File::Temp->new; # object stringifies to the filename
my $stderr = File::Temp->new;
my $cmd = "$^X -I$ilib $file $stdout $stderr";
ok $out = get_command_output("$cmd start"), "Started perl daemon";
like $out, qr/\[Started\]/, "Daemon started.";
sleep 2; # chill out for a bit, or we might miss writes to files
ok $out
= get_command_output("$cmd status" ), "Get status of system daemon.";
like $out, qr/\[Not Running\]/, "Daemon is stopped.";
# Check data written by the daemon
open my $fh, '<', $stdout
or die "Failed to open stdout file ($stdout) for inspection: $!";
like do { local $/; <$fh>; }, qr/STDOUT output success/,
"STDOUT file contains expected data";
open $fh, '<', $stderr
or die "Failed to open stderr file ($stderr) for inspection: $!";
is do { local $/; <$fh>; }, "STDERR output success\n",
"STDERR file contains expected data";
}
{
diag 'Test STDOUT and STDERR when we use custom arrayrefs as arguments';
# We're passing 'custom' argument so our daemon knows to use arrayrefs
# Consult the code of the daemon for details
my $out;
my $stdout = File::Temp->new; # object stringifies to the filename
my $stderr = File::Temp->new;
my $cmd = "$^X -I$ilib $file custom $stdout $stderr";
ok $out = get_command_output("$cmd start"), "Started perl daemon";
like $out, qr/\[Started\]/, "Daemon started.";
sleep 2; # chill out for a bit, or we might miss writes to files
ok $out
= get_command_output("$cmd status" ), "Get status of system daemon.";
like $out, qr/\[Not Running\]/, "Daemon is stopped.";
# Check daemon's first write
open my $fh, '<', $stdout
or die "Failed to open stdout file ($stdout) for inspection: $!";
like do { local $/; <$fh>; }, qr/STDOUT output success/,
"STDOUT file contains expected data";
open $fh, '<', $stderr
or die "Failed to open stderr file ($stderr) for inspection: $!";
is do { local $/; <$fh>; }, "STDERR output success\n",
"STDERR file contains expected data";
# Restart so we'd get a second STD[OUT|ERR] write
ok $out
= get_command_output("$cmd start"), "Get status of system daemon.";
like $out, qr/\[Started\]/s, "Daemon restarted.";
sleep 2; # chill out for a bit, or we might miss writes to files
ok $out
= get_command_output("$cmd status" ), "Get status of system daemon.";
like $out, qr/\[Not Running\]/, "Daemon is stopped.";
# Check daemon's second write
open $fh, '<', $stdout
or die "Failed to open stdout file ($stdout) for inspection: $!";
like do { local $/; <$fh>; },
qr/^STDOUT output success(?!.*STDOUT output success)/s,
"STDOUT file contains expected data";
open $fh, '<', $stderr
or die "Failed to open stderr file ($stderr) for inspection: $!";
like do { local $/; <$fh>; },
qr/^STDERR output success(?!.*STDERR output success)/s,
"STDERR file contains expected data";
}
unlink 'pid_tmp';
done_testing;

30
t/bin/05_stderr_stdout.pl Normal file
View file

@ -0,0 +1,30 @@
#!/usr/bin/perl
use warnings;
use strict;
use Daemon::Control;
my $custom = $ARGV[0] eq 'custom' ? shift : undef;
my $stdout = shift;
my $stderr = shift;
Daemon::Control->new({
name => "My Daemon",
lsb_start => '$syslog $remote_fs',
lsb_stop => '$syslog',
lsb_sdesc => 'My Daemon Short',
lsb_desc => 'My Daemon controls the My Daemon daemon.',
path => '/usr/sbin/mydaemon/init.pl',
program => sub {
print STDOUT "STDOUT output success\n";
print STDERR "STDERR output success\n";
},
pid_file => 'pid_tmp',
stderr_file => ($custom ? [ '>', $stderr ] : $stderr),
stdout_file => ($custom ? [ "> $stdout" ] : $stdout),
fork => 2,
})->run;