File Coverage

blib/lib/Metabrik/System/Process.pm
Criterion Covered Total %
statement 9 237 3.8
branch 0 116 0.0
condition n/a
subroutine 3 33 9.0
pod 1 28 3.5
total 13 414 3.1


line stmt bran cond sub pod time code
1             #
2             # $Id$
3             #
4             # system::process Brik
5             #
6             package Metabrik::System::Process;
7 3     3   1164 use strict;
  3         9  
  3         85  
8 3     3   16 use warnings;
  3         6  
  3         88  
9              
10 3     3   19 use base qw(Metabrik::Shell::Command);
  3         13  
  3         4422  
11              
12             sub brik_properties {
13             return {
14 0     0 1   revision => '$Revision$',
15             tags => [ qw(unstable) ],
16             author => 'GomoR ',
17             license => 'http://opensource.org/licenses/BSD-3-Clause',
18             attributes => {
19             datadir => [ qw(datadir) ],
20             force_kill => [ qw(0|1) ],
21             close_output_on_start => [ qw(0|1) ],
22             use_pidfile => [ qw(0|1) ],
23             pidfile => [ qw(file) ],
24             },
25             attributes_default => {
26             force_kill => 0,
27             close_output_on_start => 1,
28             use_pidfile => 1,
29             },
30             commands => {
31             parse_ps_output => [ qw(data) ],
32             list => [ ],
33             is_running => [ qw(process) ],
34             is_running_from_string => [ qw(string) ],
35             get_pid_from_string => [ qw(string) ],
36             get_pid_list_from_string => [ qw(string) ],
37             get_pid_list_from_regex => [ qw(regex) ],
38             get_process_info => [ qw(process) ],
39             kill => [ qw(process|pid) ],
40             start => [ qw($sub) ],
41             start_with_pidfile => [ qw($sub) ],
42             list_daemons => [ ],
43             get_latest_daemon_id => [ ],
44             kill_from_pidfile => [ qw(pidfile) ],
45             is_running_from_pidfile => [ qw(pidfile) ],
46             grep_by_name => [ qw(process_name) ],
47             get_new_pidfile => [ ],
48             get_latest_pidfile => [ ],
49             write_pidfile => [ qw(pidfile|OPTIONAL) ],
50             delete_pidfile => [ qw(pidfile) ],
51             wait_for_pidfile => [ qw(pidfile) ],
52             info_process_is_running => [ ],
53             info_process_is_not_running => [ ],
54             verbose_process_is_running => [ ],
55             verbose_process_is_not_running => [ ],
56             error_process_is_running => [ ],
57             error_process_is_not_running => [ ],
58             },
59             require_modules => {
60             'Daemon::Daemonize' => [ ],
61             'POSIX' => [ qw(:sys_wait_h) ],
62             'Time::HiRes' => [ qw(usleep) ],
63             'Metabrik::File::Find' => [ ],
64             },
65             require_binaries => {
66             ps => [ ],
67             },
68             need_packages => {
69             ubuntu => [ qw(procps) ],
70             debian => [ qw(procps) ],
71             kali => [ qw(procps) ],
72             },
73             };
74             }
75              
76             sub parse_ps_output {
77 0     0 0   my $self = shift;
78 0           my ($data) = @_;
79              
80 0 0         $self->brik_help_run_undef_arg('parse_ps_output', $data) or return;
81              
82 0           my @a = ();
83 0           my $first = shift @$data;
84 0           my $header = [ split(/\s+/, $first) ];
85 0           my $count = scalar(@$header) - 1;
86 0           for my $this (@$data) {
87 0           my @toks = split(/\s+/, $this);
88 0           my $h = {};
89 0           for my $n (0..$count) {
90 0           $h->{$header->[$n]} = $toks[$n];
91             }
92 0           my $ntoks = scalar(@toks) - 1;
93 0 0         if ($ntoks > $count) {
94 0           for my $n (($count+1)..$ntoks) {
95 0           $h->{$header->[-1]} .= ' '.$toks[$n];
96             }
97             }
98 0           push @a, $h;
99             }
100              
101 0           return \@a;
102             }
103              
104             sub list {
105 0     0 0   my $self = shift;
106              
107 0 0         my $res = $self->capture("ps awuxw") or return;
108              
109 0           return $self->parse_ps_output($res);
110             }
111              
112             sub is_running {
113 0     0 0   my $self = shift;
114 0           my ($process) = @_;
115              
116 0 0         $self->brik_help_run_undef_arg('is_running', $process) or return;
117              
118 0 0         my $list = $self->list or return;
119 0           for my $this (@$list) {
120 0           my $command = $this->{COMMAND};
121 0 0         if ($command =~ m{^\[.*\]$}) { # Example: "[migration/0]"
122 0           $self->log->debug("is_running: list[$command] process[$process]");
123 0 0         if ($command eq $process) {
124 0           return 1;
125             }
126             }
127             else {
128 0           my @toks = split(/\s+/, $command);
129 0           $toks[0] =~ s/^.*\/(.*?)$/$1/;
130 0           $self->log->debug("is_running: list[$toks[0]] process[$process]");
131 0 0         if ($toks[0] eq $process) {
132 0           return 1;
133             }
134             }
135             }
136              
137 0           return 0;
138             }
139              
140             sub is_running_from_string {
141 0     0 0   my $self = shift;
142 0           my ($string) = @_;
143              
144 0 0         $self->brik_help_run_undef_arg('is_running_from_string', $string) or return;
145              
146 0 0         my $list = $self->list or return;
147 0           for my $this (@$list) {
148 0           my $command = $this->{COMMAND};
149 0 0         if ($command =~ m{$string}i) {
150 0           return 1;
151             }
152             }
153              
154 0           return 0;
155             }
156              
157             sub get_pid_from_string {
158 0     0 0   my $self = shift;
159 0           my ($string) = @_;
160              
161 0 0         $self->brik_help_run_undef_arg('get_pid_from_string', $string) or return;
162              
163 0 0         my $list = $self->list or return;
164 0           for my $this (@$list) {
165 0           my $command = $this->{COMMAND};
166 0 0         if ($command =~ m{$string}i) {
167 0           return $this->{PID};
168             }
169             }
170              
171 0           return 0;
172             }
173              
174             sub get_pid_list_from_string {
175 0     0 0   my $self = shift;
176 0           my ($string) = @_;
177              
178 0 0         $self->brik_help_run_undef_arg('get_pid_list_from_string', $string)
179             or return;
180              
181 0           my @pids = ();
182              
183 0 0         my $list = $self->list or return;
184 0           for my $this (@$list) {
185 0           my $command = $this->{COMMAND};
186 0 0         if ($command =~ m{$string}i) {
187 0           push @pids, $this->{PID};
188             }
189             }
190              
191 0           return \@pids;
192             }
193              
194             sub get_pid_list_from_regex {
195 0     0 0   my $self = shift;
196 0           my ($regex) = @_;
197              
198 0 0         $self->brik_help_run_undef_arg('get_pid_list_from_regex', $regex)
199             or return;
200              
201 0           my @pids = ();
202              
203 0 0         my $list = $self->list or return;
204 0           for my $this (@$list) {
205 0           my $command = $this->{COMMAND};
206 0 0         if ($command =~ m{$regex}i) {
207 0           push @pids, $this->{PID};
208             }
209             }
210              
211 0           return \@pids;
212             }
213              
214             sub get_process_info {
215 0     0 0   my $self = shift;
216 0           my ($process) = @_;
217              
218 0 0         $self->brik_help_run_undef_arg('get_process_info', $process) or return;
219              
220 0           my @results = ();
221 0 0         my $list = $self->list or return;
222 0           for my $this (@$list) {
223 0 0         my $command = $this->{COMMAND} or next;
224 0           my @toks = split(/\s+/, $command);
225 0           $toks[0] =~ s/^.*\/(.*?)$/$1/;
226 0 0         if ($toks[0] eq $process) {
227 0           push @results, $this;
228             }
229             }
230              
231 0           return \@results;
232             }
233              
234             sub kill {
235 0     0 0   my $self = shift;
236 0           my ($process) = @_;
237              
238 0 0         $self->brik_help_run_undef_arg('kill', $process) or return;
239              
240 0 0         my $signal = $self->force_kill ? 'KILL' : 'TERM';
241              
242 0 0         if ($process =~ /^\d+$/) {
243 0           kill($signal, $process);
244 0           my $kid = waitpid(-1, POSIX::WNOHANG());
245             }
246             else {
247 0 0         my $list = $self->get_process_info($process) or return;
248 0           for my $this (@$list) {
249 0           kill($signal, $this->{PID});
250 0           my $kid = waitpid(-1, POSIX::WNOHANG());
251             }
252             }
253              
254 0           return 1;
255             }
256              
257             #
258             # start process sub from user program has to call write_pidfile Command to create the pidfile.
259             # Then parent process can use wait_for_pidfile Command with the $pidfile returned from
260             # start Command to wait for the process to really start.
261             #
262             sub start {
263 0     0 0   my $self = shift;
264 0           my ($sub) = @_;
265              
266 0           my %opts = (
267             close => $self->close_output_on_start,
268             );
269              
270 0           my $r;
271             # Daemonize the given subroutine
272 0 0         if (defined($sub)) {
273 0           $r = Daemon::Daemonize->daemonize(
274             %opts,
275             run => $sub,
276             );
277             }
278             # Or myself.
279             else {
280 0           $r = Daemon::Daemonize->daemonize(
281             %opts,
282             );
283             }
284              
285 0 0         if ($self->use_pidfile) {
286 0 0         my $pidfile = $self->get_new_pidfile or return;
287              
288 0           $self->log->verbose("start: new daemon started with pidfile [$pidfile]");
289              
290 0           return $pidfile;
291             }
292              
293 0           return 1;
294             }
295              
296             sub start_with_pidfile {
297 0     0 0   my $self = shift;
298 0           my ($sub) = @_;
299              
300 0           my %opts = (
301             close => $self->close_output_on_start,
302             );
303              
304 0 0         my $pidfile = $self->get_new_pidfile or return;
305              
306 0           my $r;
307             # Daemonize the given subroutine
308 0 0         if (defined($sub)) {
309             $r = Daemon::Daemonize->daemonize(
310             %opts,
311             run => sub {
312 0     0     $self->write_pidfile($pidfile);
313 0           &$sub();
314             },
315 0           );
316             }
317             # Or myself.
318             else {
319 0           $r = Daemon::Daemonize->daemonize(
320             %opts,
321             );
322             }
323              
324 0           $self->log->verbose("start: new daemon started with pidfile [$pidfile]");
325              
326 0           return $pidfile;
327             }
328              
329             sub list_daemons {
330 0     0 0   my $self = shift;
331              
332 0           my $datadir = $self->datadir;
333              
334 0 0         my $ff = Metabrik::File::Find->new_from_brik_init($self) or return;
335 0           my $list = $ff->files($datadir, 'daemonpid\.\d+');
336              
337 0           my %daemons = ();
338 0           for (@$list) {
339 0           my ($id) = $_ =~ m{\.(\d+)$};
340 0 0         my $pid = Daemon::Daemonize->read_pidfile($_) or next;
341 0           $daemons{$id} = { file => $_, pid => $pid };
342             }
343              
344 0           return \%daemons;
345             }
346              
347             sub get_latest_daemon_id {
348 0     0 0   my $self = shift;
349              
350 0 0         my $list = $self->list_daemons or return;
351              
352 0           my $id = 0;
353 0           for (keys %$list) {
354 0 0         if ($_ > $id) {
355 0           $id = $_;
356             }
357             }
358              
359 0           return $id;
360             }
361              
362             sub kill_from_pidfile {
363 0     0 0   my $self = shift;
364 0           my ($pidfile) = @_;
365              
366 0 0         $self->brik_help_run_undef_arg('kill_from_pidfile', $pidfile) or return;
367 0 0         $self->brik_help_run_file_not_found('kill_from_pidfile', $pidfile) or return;
368              
369 0 0         if (my $pid = Daemon::Daemonize->check_pidfile($pidfile)) {
370 0           $self->log->verbose("kill_from_pidfile: file[$pidfile] and pid[$pid]");
371 0           $self->kill($pid);
372 0           Daemon::Daemonize->delete_pidfile($pidfile);
373             }
374              
375 0           return 1;
376             }
377              
378             sub is_running_from_pidfile {
379 0     0 0   my $self = shift;
380 0           my ($pidfile) = @_;
381              
382 0 0         $self->brik_help_run_undef_arg('is_running_from_pidfile', $pidfile) or return;
383              
384             # Not file found, so probably not running
385 0 0         if (! -f $pidfile) {
386 0           return 0;
387             }
388              
389 0 0         if (my $pid = Daemon::Daemonize->check_pidfile($pidfile)) {
390 0           $self->log->debug("is_running_from_pidfile: yes");
391 0           return 1;
392             }
393              
394 0           $self->log->debug("is_running_from_pidfile: no");
395              
396 0           return 0;
397             }
398              
399             sub grep_by_name {
400 0     0 0   my $self = shift;
401 0           my ($process_name) = @_;
402              
403 0 0         $self->brik_help_run_undef_arg('grep_by_name', $process_name) or return;
404              
405 0 0         my $list = $self->list or return;
406 0           for my $p (@$list) {
407 0 0         if (lc($p->{COMMAND}) =~ m{$process_name}i) {
408 0           return $p;
409             }
410             }
411              
412 0           return 0;
413             }
414              
415             sub get_new_pidfile {
416 0     0 0   my $self = shift;
417              
418             # Use provided one.
419 0           my $pidfile = $self->pidfile;
420 0 0         if (defined($pidfile)) {
421 0           return $pidfile;
422             }
423              
424 0           my $id = $self->get_latest_daemon_id;
425 0 0         defined($id) ? $id++ : ($id = 1);
426 0           $pidfile = sprintf("%s/daemonpid.%05d", $self->datadir, $id);
427              
428 0           return $pidfile;
429             }
430              
431             sub get_latest_pidfile {
432 0     0 0   my $self = shift;
433              
434             # Return user provided one.
435 0           my $pidfile = $self->pidfile;
436 0 0         if (defined($pidfile)) {
437 0           return $pidfile;
438             }
439              
440 0           my $id = $self->get_latest_daemon_id;
441 0 0         if (defined($id)) {
442 0           $pidfile = sprintf("%s/daemonpid.%05d", $self->datadir, $id);
443             }
444             else {
445 0           return $self->log->error("get_latest_pidfile: no pidfile found");
446             }
447              
448 0           return $pidfile;
449             }
450              
451             #
452             # To be called by a sub used by start Command
453             #
454             sub write_pidfile {
455 0     0 0   my $self = shift;
456              
457 0 0         my $pidfile = $self->get_new_pidfile or return;
458              
459 0           my $pid = $$;
460              
461 0           my $r = Daemon::Daemonize->write_pidfile($pidfile, $pid);
462 0 0         if (! defined($r)) {
463 0           return $self->log->erro("write_pidfile: failed to write pidfile [$pidfile]: $!");
464             }
465              
466 0           return $pid;
467             }
468              
469             #
470             # To be used by parent process
471             #
472             sub delete_pidfile {
473 0     0 0   my $self = shift;
474 0           my ($pidfile) = @_;
475              
476 0 0         $self->brik_help_run_undef_arg('delete_pidfile', $pidfile) or return;
477              
478 0 0         if (! -f $pidfile) {
479             # Nothing to delete
480 0           return 0;
481             }
482              
483 0           my $r = Daemon::Daemonize->delete_pidfile($pidfile);
484 0 0         if (! defined($r)) {
485 0           return $self->log->erro("delete_pidfile: failed to delete pidfile [$pidfile]: $!");
486             }
487              
488 0           return 1;
489             }
490              
491             # XXX: Move to system::file and use a helper here
492             #
493             # To be used by parent process
494             #
495             sub wait_for_pidfile {
496 0     0 0   my $self = shift;
497 0           my ($pidfile) = @_;
498              
499 0 0         $self->brik_help_run_undef_arg('wait_pidfile', $pidfile) or return;
500              
501 0           my $found = 0;
502             # 50 * 100 ms = 5s
503 0           for (0..49) {
504 0 0         if (-e $pidfile) {
505 0           $found++;
506 0           last;
507             }
508 0           Time::HiRes::usleep(100_000); # 100_000us => 100ms => 0.1s
509             # 0.1s * 50 = 5s
510             }
511              
512 0           return $found;
513             }
514              
515             sub info_process_is_running {
516 0     0 0   my $self = shift;
517              
518 0           return $self->log->info("process is running");
519             }
520              
521             sub info_process_is_not_running {
522 0     0 0   my $self = shift;
523              
524 0           return $self->log->info("process is NOT running");
525             }
526              
527             sub verbose_process_is_running {
528 0     0 0   my $self = shift;
529              
530 0           return $self->log->verbose("process is running");
531             }
532              
533             sub verbose_process_is_not_running {
534 0     0 0   my $self = shift;
535              
536 0           return $self->log->verbose("process is NOT running");
537             }
538              
539             sub error_process_is_running {
540 0     0 0   my $self = shift;
541              
542 0           return $self->log->error("process is running");
543             }
544              
545             sub error_process_is_not_running {
546 0     0 0   my $self = shift;
547              
548 0           return $self->log->error("process is NOT running");
549             }
550              
551             # XXX: Broken.
552             sub _brik_fini {
553 0     0     my $self = shift;
554              
555 0           my $pidfile = $self->get_latest_pidfile;
556 0 0         if (-f $pidfile) {
557 0           $self->delete_pidfile($pidfile);
558             }
559              
560 0           return $self->SUPER::brik_fini;
561             }
562              
563             1;
564              
565             __END__