File Coverage

blib/lib/AnyEvent/Run.pm
Criterion Covered Total %
statement 71 90 78.8
branch 19 36 52.7
condition 8 10 80.0
subroutine 10 11 90.9
pod 1 1 100.0
total 109 148 73.6


line stmt bran cond sub pod time code
1             package AnyEvent::Run;
2              
3 8     8   224689 use strict;
  8         16  
  8         346  
4 8     8   54 use base 'AnyEvent::Handle';
  8         16  
  8         10841  
5              
6 8     8   211716 use AnyEvent ();
  8         39  
  8         138  
7 8     8   41 use AnyEvent::Util ();
  8         16  
  8         117  
8 8     8   41 use Carp;
  8         23  
  8         717  
9 8     8   8889 use POSIX ();
  8         73238  
  8         1771  
10              
11             our $VERSION = 0.01;
12              
13             our $FD_MAX = eval { POSIX::sysconf(&POSIX::_SC_OPEN_MAX) - 1 } || 1023;
14              
15             BEGIN {
16 8     8   10936 if ( AnyEvent::WIN32 ) {
17             eval { require Win32 };
18             die "Win32 failed to load:\n$@" if $@;
19            
20             eval { require Win32::Console };
21             die "Win32::Console failed to load:\n$@" if $@;
22             Win32::Console->import();
23            
24             eval { require Win32API::File };
25             die "Win32API::File failed to load:\n$@" if $@;
26             Win32API::File->import('FdGetOsFHandle');
27            
28             eval { require Win32::Job };
29             die "Win32::Job failed to load:\n$@" if $@;
30             }
31             };
32              
33             sub new {
34 30     30 1 54668 my ( $class, %args ) = @_;
35            
36 30         100 my $cls = $args{class};
37 30         67 my $cmd = $args{cmd};
38            
39 30 50 66     363 unless ( $cls || $cmd ) {
40 0         0 croak "mandatory argument cmd or class is missing";
41             }
42            
43 30 100       101 if ( $cls ) {
44 5   100     80 my $method = $args{method} || 'main';
45             # double quotes around -e needed on Windows for some reason
46 5         76 $cmd = "$^X -M$cls -I" . join( ' -I', @INC ) . " -e \"${cls}::${method}()\"";
47             }
48            
49 30   100     269 $args{args} ||= [];
50            
51 30 50       368 my ($parent, $child) = AnyEvent::Util::portable_socketpair
52             or croak "unable to create AnyEvent::Run socketpair: $!";
53            
54 30         3595 $args{fh} = $child;
55            
56 30         1405 my $self = $class->SUPER::new(%args);
57              
58 30         54279 my $pid = fork;
59            
60 30 100       1507 if ( $pid == 0 ) {
61             # child
62            
63 6         929 close $child;
64            
65             # Stdio should not be tied.
66 6 50       1069 if (tied *STDOUT) {
67 0         0 carp "Cannot redirect into tied STDOUT. Untying it";
68 0         0 untie *STDOUT;
69             }
70 6 50       151 if (tied *STDERR) {
71 0         0 carp "Cannot redirect into tied STDERR. Untying it";
72 0         0 untie *STDERR;
73             }
74            
75             # Set priority if requested
76 6 100 66     853 if ( $args{priority} && $args{priority} =~ /^-?\d+$/ ) {
77 2         141 $self->_set_priority();
78             }
79            
80             # Redirect STDIN from the read end of the stdin pipe.
81 6         172 close STDIN if AnyEvent::WIN32;
82 6 50       1452 open STDIN, "<&" . fileno($parent)
83             or croak "can't redirect STDIN in child pid $$: $!";
84              
85             # Redirect STDOUT
86 6         65 close STDOUT if AnyEvent::WIN32;
87 6 50       1359 open STDOUT, ">&" . fileno($parent)
88             or croak "can't redirect stdout in child pid $$: $!";
89              
90             # Redirect STDERR
91 6         27 close STDERR if AnyEvent::WIN32;
92 6 50       1046 open STDERR, ">&" . fileno($parent)
93             or die "can't redirect stderr in child: $!";
94            
95             # Make STDOUT and STDERR auto-flush.
96 6         441 select STDERR; $| = 1;
  6         176  
97 6         435 select STDOUT; $| = 1;
  6         94  
98            
99 6         405 if ( AnyEvent::WIN32 ) {
100             # The Win32 pseudo fork sets up the std handles in the child
101             # based on the true win32 handles For the exec these get
102             # remembered, so manipulation of STDIN/OUT/ERR is not enough.
103             # Only necessary for the exec, as Perl CODE subroutine goes
104             # through 0/1/2 which are correct. But of course that coderef
105             # might invoke exec, so better do it regardless.
106             # HACK: Using Win32::Console as nothing else exposes SetStdHandle
107             Win32::Console::_SetStdHandle(
108             STD_INPUT_HANDLE(),
109             FdGetOsFHandle(fileno($parent))
110             );
111             Win32::Console::_SetStdHandle(
112             STD_OUTPUT_HANDLE(),
113             FdGetOsFHandle(fileno($parent))
114             );
115             Win32::Console::_SetStdHandle(
116             STD_ERROR_HANDLE(),
117             FdGetOsFHandle(fileno($parent))
118             );
119             }
120            
121 6 50       276 if ( ref $cmd eq 'CODE' ) {
122 0         0 unless ( AnyEvent::WIN32 ) {
123 0         0 my @fd_keep = (
124             fileno(STDIN),
125             fileno(STDOUT),
126             fileno(STDERR),
127             fileno($parent),
128             );
129            
130 0         0 for my $fd ( 0..$FD_MAX ) {
131 0 0       0 next if grep { $_ == $fd } @fd_keep;
  0         0  
132 0         0 POSIX::close($fd);
133             }
134             }
135            
136 0         0 $cmd->( @{$args{args}} );
  0         0  
137            
138 0         0 close $parent;
139            
140 0         0 if ( AnyEvent::WIN32 ) {
141             sleep 10; # give parent a chance to kill us
142             exit 1;
143             }
144             else {
145 0         0 POSIX::_exit(0);
146             }
147             }
148            
149 6         488 if ( AnyEvent::WIN32 ) {
150             my $exitcode = 0;
151            
152             # XXX: should close open fd's, but it doesn't seem to work right on win32
153              
154             my ($appname, $cmdline);
155              
156             if ( ref $cmd eq 'ARRAY' ) {
157             $appname = $cmd->[0];
158             $cmdline = join(' ', map { /\s/ && ! /"/ ? qq{"$_"} : $_ } (@{$cmd}, @{$args{args}}) );
159             }
160             else {
161             $appname = undef;
162             $cmdline = join(' ', $cmd, map { /\s/ && ! /"/ ? qq{"$_"} : $_ } @{$args{args}} );
163             }
164              
165             my $w32job;
166              
167             unless ( $w32job = Win32::Job->new() ) {
168             die Win32::FormatMessage( Win32::GetLastError() );
169             }
170              
171             my $w32pid;
172              
173             unless ( $w32pid = $w32job->spawn( $appname, $cmdline ) ) {
174             die Win32::FormatMessage( Win32::GetLastError() );
175             }
176             else {
177 0     0   0 my $ok = $w32job->watch( sub { 0 }, 60 );
178             my $hashref = $w32job->status();
179             $exitcode = $hashref->{$w32pid}->{exitcode};
180             }
181              
182             close $parent;
183            
184             sleep 10; # give parent a chance to kill us
185             exit($exitcode);
186             }
187            
188 6 100       139 if ( ref $cmd eq 'ARRAY' ) {
189 0         0 exec( @{$cmd}, @{$args{args}} )
  3         45  
  3         0  
190 3 0       8 or die "can't exec (" . @{$cmd} . ") in child pid $$: $!";
191             }
192             else {
193 3 0       10 exec( join(" ", $cmd, @{$args{args}} ) )
  3         0  
194             or die "can't exec ($cmd) in child pid $$: $!";
195             }
196            
197             # end of child
198             }
199            
200             # parent
201 24         1221 close $parent;
202            
203 24         1455 $self->{child_pid} = $pid;
204            
205 24         6882 return $self;
206             }
207              
208             sub _set_priority {
209 2     2   65 my $self = shift;
210            
211 2         45 my $pri = $self->{priority};
212            
213 2         28 if ( AnyEvent::WIN32 ) {
214             eval { require Win32::API };
215             die "Win32::API failed to load:\n$@" if $@;
216            
217             eval { require Win32::Process };
218             die "Win32::Process failed to load:\n$@" if $@;
219            
220             # ABOVE_NORMAL_PRIORITY_CLASS and BELOW_NORMAL_PRIORITY_CLASS aren't
221             # provided by Win32::Process so their values have been hardcoded.
222             $pri = $pri <= -16 ? Win32::Process::HIGH_PRIORITY_CLASS()
223             : $pri <= -6 ? 0x00008000 # ABOVE_NORMAL
224             : $pri <= 4 ? Win32::Process::NORMAL_PRIORITY_CLASS()
225             : $pri <= 14 ? 0x00004000 # BELOW_NORMAL
226             : Win32::Process::IDLE_PRIORITY_CLASS();
227            
228             my $getCurrentProcess = Win32::API->new('kernel32', 'GetCurrentProcess', ['V'], 'N');
229             my $setPriorityClass = Win32::API->new('kernel32', 'SetPriorityClass', ['N', 'N'], 'N');
230            
231             my $processHandle = eval { $getCurrentProcess->Call(0) };
232            
233             if ( !$processHandle || $@ ) {
234             carp "Can't get process handle ($^E) [$@]";
235             return;
236             }
237            
238             eval { $setPriorityClass->Call($processHandle, $pri) };
239            
240             if ( $@ ) {
241             carp "Couldn't set priority to $pri ($^E) [$@]";
242             }
243             }
244             else {
245 2         36 eval {
246 2 50       331 unless ( setpriority( 0, $$, $pri ) ) {
247 0         0 die "unable to set child priority to $pri\n";
248             }
249             };
250 2 50       59 carp $@ if $@;
251             }
252             }
253              
254             sub DESTROY {
255 24     24   13653396 my $self = shift;
256            
257             # XXX: doesn't play nice with linger option, so clear wbuf
258 24         208 $self->{wbuf} = '';
259            
260 24         553 $self->SUPER::DESTROY(@_);
261            
262 24 50       524 if ( $self->{child_pid} ) {
263 24         546 kill 9 => $self->{child_pid};
264 24         8779 waitpid $self->{child_pid}, 0;
265             }
266             }
267              
268             1;
269             __END__