File Coverage

blib/lib/System/Process.pm
Criterion Covered Total %
statement 145 162 89.5
branch 35 54 64.8
condition 9 12 75.0
subroutine 26 28 92.8
pod 1 1 100.0
total 216 257 84.0


line stmt bran cond sub pod time code
1             package System::Process;
2              
3             =head1 NAME
4              
5             System::Process
6              
7             =head1 DESCRIPTION
8              
9             Manipulate system process as perl object. This is a simple wrapper over ps on
10             *nix systems. For Windows systems - under construction.
11              
12             =head1 SYNOPSIS
13              
14             use System::Process;
15              
16             my $process_object = pidinfo pid => 5321;
17             if ($process_object) {
18             print $process_object->command();
19             }
20              
21             =head1 METHODS
22              
23             =over
24              
25             =item B
26              
27             pidinfo(%)
28              
29             params is hash (pid=>4444) || (file=>'/path/to/pid/file' || pattern => 'my\scool\sname')
30              
31             returns System::Process::Unit object that supports following methods if pid or file option specified.
32             If pattern option specified - returns arrayref of System::Process objects.
33              
34             =back
35              
36             =head1 PROCESS OBJECT METHODS
37              
38             If your ps uax header simillar to my:
39             USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
40             You will get these methods:
41              
42             =over
43              
44             =item B
45              
46             Returns %CPU.
47              
48             =item B
49              
50             Returns TIME.
51              
52             =item B
53              
54             Returns STAT.
55              
56             =item B
57              
58             Returns TTY.
59              
60             =item B
61              
62             Returns USER
63              
64             =item B
65              
66             Returns %MEM
67              
68             =item B
69              
70             Returns %RSS
71              
72             =item B
73              
74             Returns VSZ
75              
76             =item B
77              
78             Returns COMMAND
79              
80             =item B
81              
82             Returns START
83              
84             =item B
85              
86             Returns PID
87              
88             =back
89              
90             Anyway, you will get methods named as lowercase header values.
91              
92             =over
93              
94             =item B
95              
96             Checks possibility of 'kill' process.
97             Returns 1 if possible
98              
99             =item B
100              
101             Kills a process with specified signal
102             $process_object->kill(9);
103              
104             =item B
105              
106             Refreshes data for current pid.
107              
108             =item B
109              
110             Writes pid to desired file.
111             $process_object->write_pid('/my/pid/path');
112              
113             =item B
114              
115             Returns true if process alive.
116              
117             =back
118              
119             =cut
120              
121              
122 2     2   61599 use strict;
  2         5  
  2         69  
123 2     2   10 use warnings;
  2         3  
  2         69  
124              
125 2     2   8 no warnings qw/once/;
  2         6  
  2         102  
126              
127 2     2   11 use Carp;
  2         1  
  2         154  
128 2     2   1162 use POSIX;
  2         11106  
  2         11  
129              
130             our $VERSION = 0.19;
131             our $ABSTRACT = "Simple OO wrapper over ps.";
132             our $TEST = 0;
133              
134             sub import {
135 2     2   17 my ($class, $import) = @_;
136              
137 2 100 66     12 if ($import && $import =~ m/test/s) {
138 1         1 $TEST = 1;
139 1         1 $System::Process::Unit::TEST = 1;
140             }
141              
142 2 50       11 if ($^O =~ m/MSWin32/is) {
143 0         0 croak "Not implemented for windows yet.";
144             }
145 2         38 *{main::pidinfo} = \&pidinfo;
146             }
147              
148              
149             sub pidinfo {
150 13     13 1 3363 my (%params, $pid);
151              
152 13 50       41 if (scalar @_ & 1) {
153 0         0 %params = (
154             pid => shift
155             );
156             }
157             else {
158 13         43 %params = @_;
159             }
160            
161 13 100 100     90 if ($params{pid} && $params{file}) {
162 1         121 croak 'Choose one';
163             }
164              
165 12 100 66     47 if (!$params{pid} && !$params{file} && !$params{pattern}) {
      66        
166 1         69 croak 'Missing pid or file param';
167             }
168              
169 11 100       35 if ($params{file}) {
    100          
170 2 50       29 return undef unless -r $params{file};
171              
172 2         73 open PID, $params{file};
173 2         26 $pid = ;
174 2         13 close PID;
175 2 100       8 return undef unless $pid;
176 1         3 chomp $pid;
177             }
178             elsif ($params{pattern}) {
179 1         9 return System::Process::Unit->new_bundle($params{pattern});
180              
181             }
182             else {
183 8         16 $pid = $params{pid};
184             }
185            
186 9 100       67 if ($pid !~ m/^\d+$/s) {
187 3         535 croak "PID must be a digits sequence";
188             }
189            
190 6 50       26 if ($pid > POSIX::UINT_MAX) {
191 0         0 croak "PID value ($pid) is too big";
192             }
193              
194 6         35 return System::Process::Unit->new($pid);
195             }
196              
197              
198             1;
199              
200             package System::Process::Unit;
201 2     2   5272 use strict;
  2         4  
  2         65  
202 2     2   6 use warnings;
  2         2  
  2         53  
203 2     2   6 use Carp;
  2         2  
  2         305  
204              
205             our $TEST = 0;
206             our $AUTOLOAD;
207              
208             my @allowed_subs = qw/
209             cpu
210             time
211             stat
212             tty
213             user
214             mem
215             rss
216             vsz
217             command
218             start
219             /;
220              
221             my $hal;
222             %$hal = map {(__PACKAGE__ . '::' . $_, 1)} @allowed_subs;
223              
224              
225             sub AUTOLOAD {
226 4     4   44 my $program = $AUTOLOAD;
227              
228 4 50       21 croak "Undefined subroutine $program" unless $hal->{$program};
229              
230             my $sub = sub {
231 6     6   13 my $self = shift;
232 6         13 return $self->internal_info($program);
233 4         21 };
234 2     2   9 no strict 'refs';
  2         3  
  2         62  
235 4         8 *{$program} = $sub;
  4         22  
236 2     2   19 use strict 'refs';
  2         3  
  2         1888  
237 4         13 goto &$sub;
238             }
239              
240              
241             sub new {
242 6     6   14 my ($class, $pid) = @_;
243              
244 6         11 my $self = {};
245 6         32 bless $self, $class;
246 6         17 $self->pid($pid);
247 6 50       15 unless ($self->process_info()) {
248 0         0 return undef;
249             }
250              
251 6         95 return $self;
252             }
253              
254              
255             sub new_bundle {
256 1     1   2 my ($class, $pattern) = @_;
257              
258 1         4 return get_bundle($class, $pattern);
259             }
260              
261              
262             sub refresh {
263 1     1   3 my $self = shift;
264 1         6 my $pid = $self->pid();
265              
266 1         5 $self = System::Process::pidinfo(pid => $pid);
267 1         11 return 1;
268             }
269              
270              
271             sub process_info {
272 6     6   11 my $self = shift;
273              
274 6         16 my $command = 'ps u ' . $self->pid();
275 6         32086 my @res = `$command`;
276 6         144 my $parse_result = parse_output(@res);
277              
278             # return $self->parse_output(@res);
279              
280 6 50       26 return $parse_result unless $parse_result;
281              
282 6         44 $self->internal_info($parse_result);
283 6         40 return 1;
284             }
285              
286              
287             sub get_bundle {
288 1     1   1 my ($class, $pattern) = @_;
289              
290 1         2 my $command = qq/ps uax/;
291 1         20 my @res;
292              
293 1 50       2 if ($TEST) {
294 1         4 @res = (
295             'PID CPU USER COMMAND',
296             '65532 123 test_user blahblahblah',
297             '65533 123 test_user blahblahblah',
298             '65531 123 root rm -rf',
299             '65534 123 test_user blahblahblah',
300             );
301             }
302             else {
303 0         0 @res = `$command`;
304             }
305              
306 1         2 my $header = shift @res;
307              
308             @res = grep {
309 4 100       24 if (m/$pattern/s) {
  4         20  
310 3         7 1;
311             }
312             else {
313 1         2 0;
314             }
315             } map {
316 1         3 s/\s*$//;
317 4         5 $_;
318             } @res;
319              
320 1 50       3 return [] unless scalar @res;
321              
322 1         2 my $bundle = [];
323              
324 1         2 for my $r (@res) {
325 3         6 my $res = parse_output($header, $r);
326 3         6 my $object = {
327             pid => $res->{pid},
328             _procinfo => $res,
329             };
330              
331 3         5 bless $object, $class;
332 3         4 push @$bundle, $object;
333             }
334 1         4 return $bundle;
335             }
336              
337              
338             sub write_pid {
339 0     0   0 my ($self, $file) = @_;
340              
341 0 0       0 return 0 unless $self->pid();
342 0 0       0 open PID, '>', $file or return 0;
343              
344 0 0       0 print PID $self->pid() or return 0;
345              
346 0         0 close PID;
347 0         0 return 1;
348             }
349              
350              
351             sub parse_n_generate {
352 1     1   14 my ($self, @params) = @_;
353              
354 1         6 my $res = parse_output(@params);
355 1         5 $self->internal_info($res);
356 1         2 return 1;
357             }
358              
359              
360             sub parse_output {
361 10 50   10   51 if (ref $_[0] eq __PACKAGE__) {
362 0         0 shift;
363             }
364              
365 10         35 my (@out) = @_;
366              
367 10 50       27 return 0 unless $out[1];
368              
369 10         77 my @header = split /\s+/, $out[0];
370 10         70 my @values = split /\s+/, $out[1];
371 10         25 my $res;
372              
373             my $last_key;
374              
375 10         47 for (0 .. $#values) {
376 89 100       130 unless (@header) {
377 7         21 unshift @values, $res->{$last_key};
378 7         30 $res->{$last_key} = join ' ', @values;
379 7         13 last;
380             }
381             else {
382 82         122 my $k = $last_key = shift @header;
383 82         95 my $v = shift @values;
384 82         239 $res->{$k} = $v;
385             }
386             }
387              
388 10         47 for my $key (keys %$res) {
389 82         92 my $k2 = lc $key;
390 82         151 $k2 =~ s/[^A-Za-z]//gs;
391 82         229 $res->{$k2} = delete $res->{$key};
392             }
393            
394 10         34 return $res;
395             }
396              
397              
398             sub pid {
399 17     17   866 my ($self, $pid) = @_;
400              
401 17 100       41 if ($pid) {
402 6         46 $self->{pid} = $pid;
403             }
404 17         98 return $self->{pid};
405             }
406              
407              
408             sub internal_info {
409 13     13   22 my ($self, $param) = @_;
410              
411 13 100       38 if (ref $param eq 'HASH') {
412 7         23 $self->{_procinfo} = $param;
413 7         17 return 1;
414             }
415             else {
416 6         34 $param =~ s|^.+::||;
417 6         49 return $self->{_procinfo}->{$param};
418             }
419             }
420              
421              
422             sub cankill {
423 1     1   2 my $self = shift;
424              
425 1         7 my $pid = $self->pid();
426              
427 1 50       22 if (kill 0, $pid) {
428 1         12 return 1;
429             }
430 0         0 return 0;
431             }
432              
433              
434             sub is_alive {
435 0     0   0 my ($self) = @_;
436            
437 0         0 $self->refresh();
438 0         0 return $self->cankill();
439             }
440              
441             sub kill {
442 1     1   12 my ($self, $signal) = @_;
443              
444 1 50       8 if (!defined $signal) {
445 0         0 croak 'Signal must be specified';
446             }
447 1         9 return kill $signal, $self->pid();
448             }
449              
450              
451             sub DESTROY {
452 9     9   1001714 my $self = shift;
453 9         167 undef $self;
454             }
455              
456              
457             1;
458              
459             __END__;