File Coverage

blib/lib/POE/Component/CPAN/YACSmoke.pm
Criterion Covered Total %
statement 186 361 51.5
branch 67 168 39.8
condition 21 80 26.2
subroutine 20 44 45.4
pod 10 10 100.0
total 304 663 45.8


line stmt bran cond sub pod time code
1             package POE::Component::CPAN::YACSmoke;
2              
3 12     12   45641 use strict;
  12         31  
  12         1645  
4 12     12   75 use warnings;
  12         26  
  12         583  
5 12     12   1044 use POE qw(Wheel::Run);
  12         55387  
  12         129  
6 12     12   607502 use Storable;
  12         73508  
  12         1229  
7 12     12   130 use vars qw($VERSION);
  12         25  
  12         1078  
8              
9             $VERSION = '1.36';
10              
11             my $GOT_KILLFAM;
12              
13             BEGIN {
14 12     12   29 $GOT_KILLFAM = 0;
15 12         27 eval {
16 12         71198 require Proc::ProcessTable;
17 0         0 $GOT_KILLFAM = 1;
18             };
19             }
20              
21             sub spawn {
22 11     11 1 9252 my $package = shift;
23 11         66 my %opts = @_;
24 11         121 $opts{lc $_} = delete $opts{$_} for keys %opts;
25 11         39 my $options = delete $opts{options};
26              
27 11 50       81 if ( $^O eq 'MSWin32' ) {
28 0         0 eval { require Win32; };
  0         0  
29 0 0       0 if ($@) { die "Win32 but failed to load:\n$@" }
  0         0  
30 0         0 eval { require Win32::Job; };
  0         0  
31 0 0       0 if ($@) { die "Win32::Job but failed to load:\n$@" }
  0         0  
32 0         0 eval { require Win32::Process; };
  0         0  
33 0 0       0 if ($@) { die "Win32::Process but failed to load:\n$@" }
  0         0  
34             }
35              
36 11         49 my $self = bless \%opts, $package;
37 11 50       330 $self->{session_id} = POE::Session->create(
38             object_states => [
39             $self => { shutdown => '_shutdown',
40             submit => '_command',
41             push => '_command',
42             unshift => '_command',
43             recent => '_command',
44             check => '_command',
45             indices => '_command',
46             author => '_command',
47             flush => '_command',
48             'package' => '_command',
49             },
50             $self => [ qw(_start _spawn_wheel _wheel_error _wheel_closed _wheel_stdout _wheel_stderr _wheel_idle _wheel_kill _sig_child _sig_handle) ],
51             ],
52             heap => $self,
53             ( ref($options) eq 'HASH' ? ( options => $options ) : () ),
54             )->ID();
55 11         1888 return $self;
56             }
57              
58             sub session_id {
59 0     0 1 0 return $_[0]->{session_id};
60             }
61              
62             sub pending_jobs {
63 2     2 1 6 return @{ $_[0]->{job_queue} };
  2         20  
64             }
65              
66             sub current_job {
67 0     0 1 0 my $self = shift;
68 0 0       0 return unless $self->{_current_job};
69 0         0 my $item = Storable::dclone( $self->{_current_job} );
70 0         0 return $item;
71             }
72              
73             sub current_log {
74 0     0 1 0 my $self = shift;
75 0 0       0 return unless $self->{_wheel_log};
76 0         0 my $item = Storable::dclone( $self->{_wheel_log} );
77 0         0 return $item;
78             }
79              
80             sub pause_queue {
81 1     1 1 775 my $self = shift;
82 1         6 $self->{paused} = 1;
83             }
84              
85             sub resume_queue {
86 1     1 1 3 my $self = shift;
87 1         4 my $pause = delete $self->{paused};
88 1 50       991 $poe_kernel->post( $self->{session_id}, '_spawn_wheel' ) if $pause;
89             }
90              
91             sub paused {
92 3     3 1 5004105 return $_[0]->{paused};
93             }
94              
95             sub statistics {
96 1     1 1 4422 my $self = shift;
97 1         3 my @stats;
98 1         12 push @stats, $self->{stats}->{$_} for qw(started totaljobs avg_run min_run max_run);
99 1 50       8 return @stats if wantarray;
100 0         0 return \@stats;
101             }
102              
103             sub shutdown {
104 0     0 1 0 my $self = shift;
105 0         0 $poe_kernel->post( $self->{session_id}, 'shutdown' );
106             }
107              
108             sub _start {
109 11     11   5738 my ($kernel,$self) = @_[KERNEL,OBJECT];
110 11         70 $kernel->sig( 'HUP', '_sig_handle' );
111 11         766 $self->{session_id} = $_[SESSION]->ID();
112 11 50       85 if ( $self->{alias} ) {
113 11         74 $kernel->alias_set( $self->{alias} );
114             } else {
115 0         0 $kernel->refcount_increment( $self->{session_id} => __PACKAGE__ );
116             }
117 11         438 $self->{job_queue} = [ ];
118 11 50       67 $self->{idle} = 600 unless $self->{idle};
119 11 50       88 $self->{timeout} = 3600 unless $self->{timeout};
120 11         152 $self->{stats} = {
121             started => time(),
122             totaljobs => 0,
123             avg_run => 0,
124             min_run => 0,
125             max_run => 0,
126             _sum => 0,
127             };
128 11 50       46 $ENV{APPDATA} = $self->{appdata} if $self->{appdata};
129 11         51 undef;
130             }
131              
132             sub _sig_handle {
133 1     1   243 $poe_kernel->sig_handled();
134             }
135              
136             sub _shutdown {
137 11     11   8840 my ($kernel,$self) = @_[KERNEL,OBJECT];
138 11         107 $kernel->sig( 'HUP' );
139 11         1095 $kernel->sig( 'KILL' );
140 11         554 $kernel->alias_remove( $_ ) for $kernel->alias_list();
141 11 50       1294 $kernel->refcount_decrement( $self->{session_id} => __PACKAGE__ ) unless $self->{alias};
142 11         29 $kernel->refcount_decrement( $_->{session}, __PACKAGE__ ) for @{ $self->{job_queue} };
  11         44  
143 11         66 $self->{_shutdown} = 1;
144 11         51 undef;
145             }
146              
147             sub _command {
148 11     11   19842 my ($kernel,$self,$state,$sender) = @_[KERNEL,OBJECT,STATE,SENDER];
149 11 50       52 return if $self->{_shutdown};
150 11         25 my $args;
151 11 50       58 if ( ref( $_[ARG0] ) eq 'HASH' ) {
152 11         21 $args = { %{ $_[ARG0] } };
  11         76  
153             } else {
154 0         0 $args = { @_[ARG0..$#_] };
155             }
156              
157 11 100       65 $state = 'push' if $state eq 'submit';
158              
159 11         45 $args->{lc $_} = delete $args->{$_} for grep { $_ !~ /^_/ } keys %{ $args };
  43         263  
  11         41  
160              
161 11   33     94 my $ref = $kernel->alias_resolve( $args->{session} ) || $sender;
162 11         420 $args->{session} = $ref->ID();
163              
164 11 50 66     151 if ( !$args->{module} and $state !~ /^(recent|check|indices|package|author|flush)$/i ) {
165 0         0 warn "No 'module' specified for $state";
166 0         0 return;
167             }
168              
169 11 50       55 unless ( $args->{event} ) {
170 0         0 warn "No 'event' specified for $state";
171 0         0 return;
172             }
173              
174 11 50 66     74 if ( $state =~ /^(package|author)$/ and !$args->{search} ) {
175 0         0 warn "No 'search' criteria specified for $state";
176 0         0 return;
177             }
178              
179 11         32 $args->{submitted} = time();
180              
181 11 100       192 if ( $state eq 'recent' ) {
    100          
    100          
    100          
    100          
    100          
182 1 50       4 if ( $^O eq 'MSWin32' ) {
183 0         0 $args->{program} = \&_recent_modules;
184 0   0     0 $args->{program_args} = [ $args->{perl} || $self->{perl} || $^X ];
185             }
186             else {
187 1   33     30 my $perl = $args->{perl} || $self->{perl} || $^X;
188 1         2 my $code = 'my $smoke = CPAN::YACSmoke->new(); print "$_\n" for $smoke->{plugin}->download_list();';
189 1         4 $args->{program} = [ $perl, '-MCPAN::YACSmoke', '-e', $code ];
190             }
191             }
192             elsif ( $state eq 'check' ) {
193 1 50       5 if ( $^O eq 'MSWin32' ) {
194 0         0 $args->{program} = \&_check_yacsmoke;
195 0   0     0 $args->{program_args} = [ $args->{perl} || $self->{perl} || $^X ];
196             }
197             else {
198 1   33     28 my $perl = $args->{perl} || $self->{perl} || $^X;
199 1         4 $args->{program} = [ $perl, '-MCPAN::YACSmoke', '-e', 1 ];
200             }
201 1         3 $args->{debug} = 1;
202             }
203             elsif ( $state eq 'indices' ) {
204 1 50       18 $args->{prioritise} = 0 unless $args->{prioritise};
205 1 50       38 if ( $^O eq 'MSWin32' ) {
206 0         0 $args->{program} = \&_reload_indices;
207 0   0     0 $args->{program_args} = [ $args->{perl} || $self->{perl} || $^X ];
208             }
209             else {
210 1   33     7 my $perl = $args->{perl} || $self->{perl} || $^X;
211 1         2 my $code = 'CPANPLUS::Backend->new()->reload_indices( update_source => 1 );';
212 1         6 $args->{program} = [ $perl, '-MCPANPLUS::Backend', '-e', $code ];
213             }
214             }
215             elsif ( $state eq 'author' ) {
216 1 50       6 if ( $^O eq 'MSWin32' ) {
217 0         0 $args->{program} = \&_author_search;
218 0   0     0 $args->{program_args} = [ $args->{perl} || $self->{perl} || $^X, $args->{type} || 'cpanid', $args->{search} ];
      0        
219             }
220             else {
221 1   33     37 my $perl = $args->{perl} || $self->{perl} || $^X;
222 1         3 my $code = 'my $type = shift; my $search = shift; my $cb = CPANPLUS::Backend->new(); my %mods = map { $_->package() => 1 } map { $_->modules() } $cb->search( type => $type, allow => [ qr/$search/ ], [ verbose => 0 ] ); print qq{$_\n} for sort keys %mods;';
223 1   50     11 $args->{program} = [ $perl, '-MCPANPLUS::Backend', '-e', $code, $args->{type} || 'cpanid', $args->{search} ];
224             }
225             }
226             elsif ( $state eq 'package' ) {
227 1 50       6 if ( $^O eq 'MSWin32' ) {
228 0         0 $args->{program} = \&_package_search;
229 0   0     0 $args->{program_args} = [ $args->{perl} || $self->{perl} || $^X, $args->{type} || 'package', $args->{search} ];
      0        
230             }
231             else {
232 1   33     38 my $perl = $args->{perl} || $self->{perl} || $^X;
233 1         2 my $code = 'my $type = shift; my $search = shift; my $cb = CPANPLUS::Backend->new(); my %mods = map { $_->package() => 1 } $cb->search( type => $type, allow => [ qr/$search/ ], [ verbose => 0 ] ); print qq{$_\n} for sort keys %mods;';
234 1   50     10 $args->{program} = [ $perl, '-MCPANPLUS::Backend', '-e', $code, $args->{type} || 'package', $args->{search} ];
235             }
236             }
237             elsif ( $state eq 'flush' ) {
238 1 50       12 $args->{prioritise} = 0 unless $args->{prioritise};
239 1 50       25 if ( $^O eq 'MSWin32' ) {
240 0         0 $args->{program} = \&_flush;
241 0   0     0 $args->{program_args} = [ $args->{perl} || $self->{perl} || $^X, ( $args->{type} and $args->{type} eq 'all' ? 'all' : 'old' ) ];
      0        
242             }
243             else {
244 1   33     6 my $perl = $args->{perl} || $self->{perl} || $^X;
245 1         2 my $code = 'my $type = shift; my $smoke = CPAN::YACSmoke->new(); $smoke->flush($type) if $smoke->can("flush");';
246 1   33     7 $args->{program} = [ $perl, '-MCPAN::YACSmoke', '-e', $code, ( $args->{type} and $args->{type} eq 'all' ? 'all' : 'old' ) ];
247             }
248             }
249             else {
250 5 50       27 if ( $^O eq 'MSWin32' ) {
251 0         0 $args->{program} = \&_test_module;
252 0   0     0 $args->{program_args} = [ $args->{perl} || $self->{perl} || $^X, $args->{module} ];
253             }
254             else {
255 5   33     137 my $perl = $args->{perl} || $self->{perl} || $^X;
256 5         12 my $code = 'my $module = shift; my $smoke = CPAN::YACSmoke->new(); $smoke->test($module);';
257 5         27 $args->{program} = [ $perl, '-MCPAN::YACSmoke', '-e', $code, $args->{module} ];
258             }
259             }
260              
261 11         78 $kernel->refcount_increment( $args->{session}, __PACKAGE__ );
262              
263 11         377 $args->{cmd} = $state;
264              
265 11 100 66     155 if ( $state eq 'unshift' or $state eq 'recent' or $args->{prioritise} ) {
      66        
266 1         2 unshift @{ $self->{job_queue} }, $args;
  1         3  
267             }
268             else {
269 10         18 push @{ $self->{job_queue} }, $args;
  10         32  
270             }
271              
272 11         72 $kernel->yield( '_spawn_wheel' );
273              
274 11         807 undef;
275             }
276              
277             sub _sig_child {
278 11     11   11507 my ($kernel,$self,$thing,$pid,$status) = @_[KERNEL,OBJECT,ARG0..ARG2];
279 11         33 push @{ $self->{_wheel_log} }, "$thing $pid $status";
  11         74  
280 11 100       590 warn "$thing $pid $status\n" if $self->{debug};
281 11         178 $kernel->delay( '_wheel_idle' );
282 11         862 my $job = delete $self->{_current_job};
283 11         243 $job->{status} = $status;
284 11         37 my $log = delete $self->{_wheel_log};
285 11 100       289 if ( $job->{cmd} eq 'recent' ) {
    100          
286 1         5 pop @{ $log };
  1         4  
287 1         3 s/\x0D$// for @{ $log };
  1         3  
288 1         9 $job->{recent} = $log;
289             }
290             elsif ( $job->{cmd} =~ /^(package|author)$/ ) {
291 2         4 pop @{ $log };
  2         7  
292 2         5 s/\x0D$// for @{ $log };
  2         10  
293 2         6 @{ $job->{results} } = grep { $_ !~ /^\[/ } @{ $log };
  2         10  
  2         9  
  2         5  
294             }
295             else {
296 8         57 $job->{log} = $log;
297             }
298 11         76 $job->{end_time} = time();
299 11 100       165 unless ( $self->{debug} ) {
300 6         60 delete $job->{program};
301 6         22 delete $job->{program_args};
302             }
303             # Stats
304 11         46 my $run_time = $job->{end_time} - $job->{start_time};
305 11 50       70 $self->{stats}->{max_run} = $run_time if $run_time > $self->{stats}->{max_run};
306 11 50       78 $self->{stats}->{min_run} = $run_time if $self->{stats}->{min_run} == 0;
307 11 50       60 $self->{stats}->{min_run} = $run_time if $run_time < $self->{stats}->{min_run};
308 11         112 $self->{stats}->{_sum} += $run_time;
309 11         36 $self->{stats}->{totaljobs}++;
310 11         84 $self->{stats}->{avg_run} = $self->{stats}->{_sum} / $self->{stats}->{totaljobs};
311 11         45 $self->{debug} = delete $job->{global_debug};
312             #$ENV{APPDATA} = delete $job->{backup_env} if $job->{appdata};
313 11         114 $kernel->post( $job->{session}, $job->{event}, $job );
314 11         1343 $kernel->refcount_decrement( $job->{session}, __PACKAGE__ );
315 11         1220 $kernel->yield( '_spawn_wheel' );
316 11         1113 $kernel->sig_handled();
317             }
318              
319             sub _spawn_wheel {
320 23     23   64470 my ($kernel,$self) = @_[KERNEL,OBJECT];
321 23 50       110 return if $self->{wheel};
322 23 50       187 return if $self->{_shutdown};
323 23 100       109 return if $self->{paused};
324 22         36 my $job = shift @{ $self->{job_queue} };
  22         69  
325 22 100       115 return unless $job;
326 11         22 my $backup_env;
327 11 50       41 if ( $job->{appdata} ) {
328 0         0 $backup_env = $ENV{APPDATA};
329 0         0 $ENV{APPDATA} = $job->{appdata};
330             }
331 11         171 $self->{wheel} = POE::Wheel::Run->new(
332             Program => $job->{program},
333             ProgramArgs => $job->{program_args},
334             StdoutEvent => '_wheel_stdout',
335             StderrEvent => '_wheel_stderr',
336             ErrorEvent => '_wheel_error',
337             CloseEvent => '_wheel_close',
338             );
339 11 50       93809 if ( $job->{appdata} ) {
340 0         0 delete $ENV{APPDATA};
341 0 0       0 $ENV{APPDATA} = $backup_env if $backup_env;
342             }
343 11 50       191 unless ( $self->{wheel} ) {
344 0         0 warn "Couldn\'t spawn a wheel for $job->{module}\n";
345 0         0 $kernel->refcount_decrement( $job->{session}, __PACKAGE__ );
346 0         0 return;
347             }
348 11 100       168 if ( defined $job->{debug} ) {
349 5         106 $job->{global_debug} = delete $self->{debug};
350 5         33 $self->{debug} = $job->{debug};
351             }
352 11         103 $self->{_wheel_log} = [ ];
353 11         85 $self->{_current_job} = $job;
354 11         995 $job->{PID} = $self->{wheel}->PID();
355 11         239 $job->{start_time} = time();
356 11         340 $kernel->sig_child( $job->{PID}, '_sig_child' );
357 11 100       5287 $kernel->delay( '_wheel_idle', 60 ) unless $job->{cmd} eq 'indices';
358 11         2449 undef;
359             }
360              
361             sub _wheel_error {
362 11     11   30394 $poe_kernel->delay( '_wheel_idle' );
363 11         3104 delete $_[OBJECT]->{wheel};
364 11         7483 undef;
365             }
366              
367             sub _wheel_closed {
368 0     0   0 $poe_kernel->delay( '_wheel_idle' );
369 0         0 delete $_[OBJECT]->{wheel};
370 0         0 undef;
371             }
372              
373             sub _wheel_stdout {
374 0     0   0 my ($self, $input, $wheel_id) = @_[OBJECT, ARG0, ARG1];
375 0         0 $self->{_wheel_time} = time();
376 0         0 push @{ $self->{_wheel_log} }, $input;
  0         0  
377 0 0       0 warn $input, "\n" if $self->{debug};
378 0         0 undef;
379             }
380              
381             sub _wheel_stderr {
382 10     10   7769 my ($self, $input, $wheel_id) = @_[OBJECT, ARG0, ARG1];
383 10         82 $self->{_wheel_time} = time();
384 10 0 33     145 if ( $^O eq 'MSWin32' and !$self->{_current_job}->{GRP_PID} and my ($pid) = $input =~ /(\d+)/ ) {
      33        
385 0         0 $self->{_current_job}->{GRP_PID} = $pid;
386 0 0       0 warn "Grp PID: $pid\n" if $self->{debug};
387 0         0 return;
388             }
389 10 100       71 push @{ $self->{_wheel_log} }, $input unless $self->{_current_job}->{cmd} eq 'recent';
  9         39  
390 10 100       1236 warn $input, "\n" if $self->{debug};
391 10         65 undef;
392             }
393              
394             sub _wheel_idle {
395 0     0     my ($kernel,$self) = @_[KERNEL,OBJECT];
396 0           my $now = time();
397 0 0         if ( $now - $self->{_wheel_time} >= $self->{idle} ) {
398 0           $self->{_current_job}->{idle_kill} = 1;
399 0           $kernel->yield( '_wheel_kill', 'Killing current run due to excessive idle' );
400 0           return;
401             }
402 0 0         if ( $now - $self->{_current_job}->{start_time} >= $self->{timeout} ) {
403 0           $self->{_current_job}->{excess_kill} = 1;
404 0           $kernel->yield( '_wheel_kill', 'Killing current run due to excessive run-time' );
405 0           return;
406             }
407 0           $kernel->delay( '_wheel_idle', 60 );
408 0           return;
409             }
410              
411             sub _wheel_kill {
412 0     0     my ($kernel,$self,$reason) = @_[KERNEL,OBJECT,ARG0];
413 0           push @{ $self->{_wheel_log} }, $reason;
  0            
414 0 0         warn $reason, "\n" if $self->{debug};
415 0 0 0       if ( $^O eq 'MSWin32' and $self->{wheel} ) {
416 0           my $grp_pid = $self->{_current_job}->{GRP_PID};
417 0 0         return unless $grp_pid;
418 0 0         warn Win32::FormatMessage( Win32::GetLastError() )
419             unless Win32::Process::KillProcess( $grp_pid, 0 );
420             }
421             else {
422 0 0         if ( !$self->{no_grp_kill} ) {
    0          
423 0 0         $self->{wheel}->kill(-9) if $self->{wheel};
424             }
425             elsif ( $GOT_KILLFAM ) {
426 0 0         _kill_family( 9, $self->{wheel}->PID() ) if $self->{wheel};
427             }
428             else {
429 0 0         $self->{wheel}->kill(9) if $self->{wheel};
430             }
431             }
432 0           return;
433             }
434              
435             sub _check_yacsmoke {
436 0     0     my $perl = shift;
437 0           my $cmdline = $perl . q{ -MCPAN::YACSmoke -e 1};
438 0 0         my $job = Win32::Job->new()
439             or die Win32::FormatMessage( Win32::GetLastError() );
440 0 0         my $pid = $job->spawn( $perl, $cmdline )
441             or die Win32::FormatMessage( Win32::GetLastError() );
442 0           warn $pid, "\n";
443 0     0     my $ok = $job->watch( sub { 0 }, 60 );
  0            
444 0           my $hashref = $job->status();
445 0           return $hashref->{$pid}->{exitcode};
446             }
447              
448             sub _test_module {
449 0     0     my $perl = shift;
450 0           my $module = shift;
451 0           my $cmdline = $perl . ' -MCPAN::YACSmoke -e "my $module = shift; my $smoke = CPAN::YACSmoke->new(); $smoke->test($module);" ' . $module;
452 0 0         my $job = Win32::Job->new()
453             or die Win32::FormatMessage( Win32::GetLastError() );
454 0 0         my $pid = $job->spawn( $perl, $cmdline )
455             or die Win32::FormatMessage( Win32::GetLastError() );
456 0           warn $pid, "\n";
457 0     0     my $ok = $job->watch( sub { 0 }, 60 );
  0            
458 0           my $hashref = $job->status();
459 0           return $hashref->{$pid}->{exitcode};
460             }
461              
462             sub _flush {
463 0     0     my $perl = shift;
464 0           my $type = shift;
465 0           my $cmdline = $perl . ' -MCPAN::YACSmoke -e "my $type = shift; my $smoke = CPAN::YACSmoke->new(); $smoke->flush($type) if $smoke->can(q{flush});" ' . $type;
466 0 0         my $job = Win32::Job->new()
467             or die Win32::FormatMessage( Win32::GetLastError() );
468 0 0         my $pid = $job->spawn( $perl, $cmdline )
469             or die Win32::FormatMessage( Win32::GetLastError() );
470 0           warn $pid, "\n";
471 0     0     my $ok = $job->watch( sub { 0 }, 60 );
  0            
472 0           my $hashref = $job->status();
473 0           return $hashref->{$pid}->{exitcode};
474             }
475             sub _recent_modules {
476 0     0     my $perl = shift;
477 0           my $cmdline = $perl . ' -MCPAN::YACSmoke -e "my $smoke = CPAN::YACSmoke->new();print qq{$_\n} for $smoke->{plugin}->download_list();"';
478 0 0         my $job = Win32::Job->new()
479             or die Win32::FormatMessage( Win32::GetLastError() );
480 0 0         my $pid = $job->spawn( $perl, $cmdline )
481             or die Win32::FormatMessage( Win32::GetLastError() );
482 0           warn $pid, "\n";
483 0     0     my $ok = $job->watch( sub { 0 }, 60 );
  0            
484 0           my $hashref = $job->status();
485 0           return $hashref->{$pid}->{exitcode};
486             }
487              
488             sub _reload_indices {
489 0     0     my $perl = shift;
490 0           my $cmdline = $perl . ' -MCPANPLUS::Backend -e "CPANPLUS::Backend->new()->reload_indices( update_source => 1 );"';
491 0 0         my $job = Win32::Job->new()
492             or die Win32::FormatMessage( Win32::GetLastError() );
493 0 0         my $pid = $job->spawn( $perl, $cmdline )
494             or die Win32::FormatMessage( Win32::GetLastError() );
495 0           warn $pid, "\n";
496 0     0     my $ok = $job->watch( sub { 0 }, 60 );
  0            
497 0           my $hashref = $job->status();
498 0           return $hashref->{$pid}->{exitcode};
499             }
500              
501             sub _author_search {
502 0     0     my $perl = shift;
503 0           my $type = shift;
504 0           my $search = shift;
505 0           my $cmdline = $perl . ' -MCPAN::YACSmoke -e "my $type = shift; my $search = shift; my $cb = CPANPLUS::Backend->new(); my %mods = map { $_->package() => 1 } map { $_->modules() } $cb->search( type => $type, allow => [ qr/$search/ ], [ verbose => 0 ] ); print qq{$_\n} for sort keys %mods;" ' . $type . " " . $search;
506 0 0         my $job = Win32::Job->new()
507             or die Win32::FormatMessage( Win32::GetLastError() );
508 0 0         my $pid = $job->spawn( $perl, $cmdline )
509             or die Win32::FormatMessage( Win32::GetLastError() );
510 0           warn $pid, "\n";
511 0     0     my $ok = $job->watch( sub { 0 }, 60 );
  0            
512 0           my $hashref = $job->status();
513 0           return $hashref->{$pid}->{exitcode};
514             }
515              
516             sub _package_search {
517 0     0     my $perl = shift;
518 0           my $type = shift;
519 0           my $search = shift;
520 0           my $cmdline = $perl . ' -MCPAN::YACSmoke -e "my $type = shift; my $search = shift; my $cb = CPANPLUS::Backend->new(); my %mods = map { $_->package() => 1 } $cb->search( type => $type, allow => [ qr/$search/ ], [ verbose => 0 ] ); print qq{$_\n} for sort keys %mods;" ' . $type . " " . $search;
521 0 0         my $job = Win32::Job->new()
522             or die Win32::FormatMessage( Win32::GetLastError() );
523 0 0         my $pid = $job->spawn( $perl, $cmdline )
524             or die Win32::FormatMessage( Win32::GetLastError() );
525 0           warn $pid, "\n";
526 0     0     my $ok = $job->watch( sub { 0 }, 60 );
  0            
527 0           my $hashref = $job->status();
528 0           return $hashref->{$pid}->{exitcode};
529             }
530              
531             sub _kill_family {
532 0     0     my ($signal, @pids) = @_;
533 0           my $pt = Proc::ProcessTable->new;
534 0           my (@procs) = @{$pt->table};
  0            
535 0           my (@kids) = _get_pids( \@procs, @pids );
536 0           @pids = (@pids, @kids);
537 0           kill $signal, reverse @pids;
538             }
539              
540             sub _get_pids {
541 0     0     my($procs, @kids) = @_;
542 0           my @pids;
543 0           foreach my $kid (@kids) {
544 0           foreach my $proc (@$procs) {
545 0 0         if ($proc->ppid == $kid) {
546 0           my $pid = $proc->pid;
547 0           push @pids, $pid, _get_pids( $procs, $pid );
548             }
549             }
550             }
551 0           @pids;
552             }
553              
554             1;
555             __END__