File Coverage

blib/lib/XAS/Lib/Spawn/Unix.pm
Criterion Covered Total %
statement 6 96 6.2
branch 0 40 0.0
condition 0 6 0.0
subroutine 2 11 18.1
pod 0 7 0.0
total 8 160 5.0


line stmt bran cond sub pod time code
1             package XAS::Lib::Spawn::Unix;
2              
3             our $VERSION = '0.01';
4              
5 1     1   1019 use POSIX qw(:errno_h :sys_wait_h);
  1         1  
  1         9  
6              
7             use XAS::Class
8 1         9 version => $VERSION,
9             base => 'XAS::Base',
10             utils => ':env dotid compress trim exitcode',
11             mixins => 'run stop status pause resume wait _parse_command',
12 1     1   429 ;
  1         2  
13              
14             # ----------------------------------------------------------------------
15             # Public Methods
16             # ----------------------------------------------------------------------
17              
18             sub run {
19 0     0 0   my $self = shift;
20              
21 0           my $pid;
22 0           my $umask = oct($self->umask);
23 0           my $env = $self->environment;
24 0           my @args = $self->_parse_command;
25 0           my $priority = $self->priority;
26 0 0         my $uid = ($self->user eq 'root') ? 0 : getpwnam($self->user);
27 0 0         my $gid = ($self->group eq 'root') ? 0 : getgrnam($self->group);
28 0           my $directory = $self->directory->path;
29 0           my $oldenv = env_store();
30 0           my $newenv = $self->merger->merge($oldenv, $env);
31              
32             my $spawn = sub {
33              
34             # become a session leader
35              
36 0     0     setsid();
37              
38             # redirect the standard file handles to dev null
39              
40 0           open(STDIN, '<', '/dev/null');
41 0           open(STDOUT, '>', '/dev/null');
42 0           open(STDERR, '>', '/dev/null');
43              
44 0           eval { # set priority, fail silently
45 0           my $p = getpriority(0, $$);
46 0           setpriority(0, $$, $p + $priority);
47             };
48              
49 0           $( = $) = $gid; # set new group id
50 0           $< = $> = $uid; # set new user id
51              
52 0           env_create($newenv); # create the new environment
53              
54 0           chdir($directory); # change directory
55 0           umask($umask); # set protection mask
56 0           exec(@args); # become a new process
57              
58 0           exit 0;
59              
60 0           };
61              
62 0 0         unless ($pid = fork) {
63              
64             # child
65              
66 0           $spawn->();
67              
68             }
69              
70             # parent
71              
72 0 0         unless(defined($pid)) {
73              
74 0           $self->throw_msg(
75             dotid($self->class) . '.detach.creation',
76             'unexpected',
77             'unable to spawn a new process',
78             );
79              
80             }
81              
82 0           return $pid;
83              
84             }
85              
86             sub status {
87 0     0 0   my $self = shift;
88              
89 0           my $stat = 0;
90              
91 0 0         if ($self->pid) {
92              
93 0           my $pid = $self->pid;
94              
95 0           $stat = $self->proc_status($pid);
96              
97             }
98              
99 0           return $stat;
100              
101             }
102              
103             sub pause {
104 0     0 0   my $self = shift;
105              
106 0           my $stat = 0;
107 0           my $alias = $self->alias;
108              
109 0 0         if ($self->pid) {
110              
111 0           my $pid = ($self->pid * -1);
112 0           my $code = $self->status();
113              
114 0 0 0       if (($code == 3) || ($code == 2)) { # process is running or ready
115              
116 0 0         if (kill('STOP', $pid)) {
117              
118 0           $stat = 1;
119              
120             }
121              
122             }
123              
124             }
125              
126 0           return $stat;
127              
128             }
129              
130             sub resume {
131 0     0 0   my $self = shift;
132              
133 0           my $stat = 0;
134              
135 0 0         if ($self->pid) {
136              
137 0           my $pid = ($self->pid * -1);
138 0           my $code = $self->status();
139              
140 0 0         if ($code == 6) { # process is suspended ready
141              
142 0 0         if (kill('CONT', $pid)) {
143              
144 0           $stat = 1;
145              
146             }
147              
148             }
149              
150             }
151              
152 0           return $stat;
153              
154             }
155              
156             sub stop {
157 0     0 0   my $self = shift;
158              
159 0           my $stat = 0;
160              
161 0 0         if ($self->pid) {
162              
163 0           my $pid = ($self->pid * -1);
164              
165 0 0         if (kill('TERM', $pid)) {
166              
167 0           $stat = 1;
168              
169             }
170              
171             }
172              
173 0           return $stat;
174              
175             }
176              
177             sub kill {
178 0     0 0   my $self = shift;
179              
180 0           my $stat = 0;
181              
182 0 0         if ($self->pid) {
183              
184 0           my $pid = ($self->pid * -1);
185              
186 0 0         if (kill('KILL', $pid)) {
187              
188 0           $stat = 1;
189              
190             }
191              
192             }
193              
194 0           return $stat;
195              
196             }
197              
198             sub wait {
199 0     0 0   my $self = shift;
200              
201 0           my $stat = 0;
202              
203 0 0         if (my $pid = $self->pid) {
204              
205 0           sleep(1); # emulate the 1000ms wait in the Win32 mixin
206              
207             # Try to wait on the process.
208              
209 0           my $result = waitpid($pid, WNOHANG);
210              
211 0 0 0       if ($result == $pid) {
    0          
212              
213             # Process finished. Grab the exit value.
214              
215 0           my ($rc, $sig) = exitcode();
216              
217 0           $self->{'errorlevel'} = $rc;
218 0           $self->{'pid'} = 0;
219              
220             } elsif ($result == -1 and $! == ECHILD) {
221              
222             # Process already reaped. We don't know the exist status.
223              
224 0           $self->{'errorlevel'} = -1;
225 0           $self->{'pid'} = 0;
226            
227             } else {
228              
229             # Process still running
230              
231 0           $stat = 1;
232              
233             }
234              
235             }
236              
237 0           return $stat;
238              
239             }
240              
241             # ----------------------------------------------------------------------
242             # Private Methods
243             # ----------------------------------------------------------------------
244              
245             sub _parse_command {
246 0     0     my $self = shift;
247              
248 0           my @args = split(' ', $self->command);
249 0           my @path = split(':', $ENV{PATH});
250 0           my @extensions = ('');
251            
252             # Stolen from Proc::Background
253             #
254             # If there is only one element in the @args array, then it may be a
255             # command to be passed to the shell and should not be checked, in
256             # case the command sets environmental variables in the beginning,
257             # i.e. 'VAR=arg ls -l'. If there is more than one element in the
258             # array, then check that the first element is a valid executable
259             # that can be found through the PATH and find the absolute path to
260             # the executable. If the executable is found, then replace the
261             # first element it with the absolute path.
262              
263 0 0         if (scalar(@args) > 1) {
264              
265 0 0         $args[0] = $self->_resolve_path($args[0], \@extensions, \@path) or return;
266              
267             }
268              
269 0           return @args;
270              
271             }
272              
273             1;
274              
275             __END__
276              
277             =head1 NAME
278              
279             XAS::Lib::Spawn::Unix - A mixin class for spawing processes within the XAS environment
280              
281             =head1 DESCRIPTION
282              
283             This module is a mixin class to handle the spawing as process under a
284             Unix like system.
285              
286             =head1 SEE ALSO
287              
288             =over 4
289              
290             =item L<XAS::Lib::Spawn|XAS::Lib::Spawn>
291              
292             =item L<XAS|XAS>
293              
294             =back
295              
296             =head1 AUTHOR
297              
298             Kevin L. Esteb, E<lt>kevin@kesteb.usE<gt>
299              
300             =head1 COPYRIGHT AND LICENSE
301              
302             Copyright (c) 2012-2016 Kevin L. Esteb
303              
304             This is free software; you can redistribute it and/or modify it under
305             the terms of the Artistic License 2.0. For details, see the full text
306             of the license at http://www.perlfoundation.org/artistic_license_2_0.
307              
308             =cut