File Coverage

blib/lib/Proc/Async.pm
Criterion Covered Total %
statement 173 203 85.2
branch 38 76 50.0
condition 29 38 76.3
subroutine 34 37 91.8
pod 11 12 91.6
total 285 366 77.8


line stmt bran cond sub pod time code
1             #-----------------------------------------------------------------
2             # Proc::Async
3             # Author: Martin Senger
4             # For copyright and disclaimer se below.
5             #
6             # ABSTRACT: Running and monitoring processes asynchronously
7             # PODNAME: Proc::Async
8             #-----------------------------------------------------------------
9              
10 17     17   478748 use warnings;
  17         35  
  17         533  
11 17     17   110 use strict;
  17         48  
  17         1004  
12             package Proc::Async;
13              
14             our $VERSION = '0.2.0'; # VERSION
15              
16 17     17   85 use Carp;
  17         19  
  17         982  
17 17     17   21341 use File::Temp qw{ tempdir };
  17         555442  
  17         1530  
18 17     17   196 use File::Path qw{ remove_tree };
  17         36  
  17         831  
19 17     17   103 use File::Spec;
  17         34  
  17         352  
20 17     17   100 use File::Find;
  17         33  
  17         975  
21 17     17   16240 use File::Slurp;
  17         237785  
  17         2048  
22 17     17   9470 use Proc::Async::Config;
  17         49  
  17         464  
23 17     17   16972 use Proc::Daemon;
  17         39291  
  17         424  
24 17     17   95 use Config;
  17         34  
  17         675  
25              
26 17     17   74 use constant STDOUT_FILE => '___proc_async_stdout___';
  17         21  
  17         1100  
27 17     17   84 use constant STDERR_FILE => '___proc_async_stderr___';
  17         21  
  17         645  
28 17     17   70 use constant CONFIG_FILE => '___proc_async_status.cfg';
  17         19  
  17         747  
29              
30             use constant {
31 17         1316 STATUS_UNKNOWN => 'unknown',
32             STATUS_CREATED => 'created',
33             STATUS_RUNNING => 'running',
34             STATUS_COMPLETED => 'completed',
35             STATUS_TERM_BY_REQ => 'terminated by request',
36             STATUS_TERM_BY_ERR => 'terminated by error',
37 17     17   70 };
  17         18  
38              
39             # options used in the start() method
40             use constant {
41 17         42523 ALLOW_SHELL => 'ALLOW_SHELL',
42             TIMEOUT => 'TIMEOUT',
43 17     17   70 };
  17         34  
44              
45             #
46             my $KNOWN_OPTIONS = {
47             ALLOW_SHELL() => 1,
48             TIMEOUT() => 1,
49             };
50              
51             #-----------------------------------------------------------------
52             # Start an external program and return its ID.
53             # starts ($args, [$options])
54             # starts (@args, [$options])
55             # $args ... an arrayref with the full command-line (including the
56             # external program name)
57             # @args ... an array with the full command-line (including the
58             # external program name)
59             # $options ... a hashref with additional options:
60             # ALLOW_SHELL => 1
61             # TIMEOUT => number of second to spend
62             #-----------------------------------------------------------------
63             sub start {
64 34     34 1 18831 my $class = shift;
65 34 50       162 croak ("START: Undefined external process.")
66             unless @_ > 0;
67 34         253 my ($args, $options) = _process_start_args (@_);
68 34         123 _check_options ($options);
69              
70             # create a job ID and a job directory
71 34         129 my $jobid = _generate_job_id ($options);
72 34         18212 my $dir = _id2dir ($jobid);
73              
74             # create configuration file
75 34         122 my ($cfg, $cfgfile) = _start_config ($jobid, $args, $options);
76              
77             # demonize itself
78 34         1030 my $daemon = Proc::Daemon->new(
79             work_dir => $dir,
80             child_STDOUT => File::Spec->catfile ($dir, STDOUT_FILE),
81             child_STDERR => File::Spec->catfile ($dir, STDERR_FILE),
82             );
83 34         866 my $daemon_pid = $daemon->Init();
84 30 100       32971641 if ($daemon_pid) {
85             # this is a parent of the already detached daemon
86 22         5931 return $jobid;
87             }
88              
89             #
90             # --- this is the daemon (child) branch
91             #
92              
93             # fork and start an external process
94 8         12637 my $pid = fork();
95              
96 8 100       887 if ($pid) {
    50          
97             #
98             # --- this branch is executed in the parent (wrapper) process;
99             #
100              
101             # update the configuration file
102 4         311 $cfg->param ("job.pid", $pid);
103 4         1556 update_status ($cfg,
104             STATUS_RUNNING,
105             "started at " . scalar localtime());
106 4         57 $cfg->param ("job.started", time());
107 4         69 $cfg->save();
108              
109             # wait for the child process to finish
110             # TBD: if TIMEOUT then use alarm and non-blocking waitpid
111 3         2291318 my $reaped_pid = waitpid ($pid, 0);
112 3         124 my $reaped_status = $?;
113              
114 3 50       69 if ($reaped_status == -1) {
    50          
115 0         0 update_status ($cfg,
116             STATUS_UNKNOWN,
117             "No such child process"); # can happen?
118              
119             } elsif ($reaped_status & 127) {
120 0 0       0 update_status ($cfg,
121             STATUS_TERM_BY_REQ,
122             "terminated by signal " . ($reaped_status & 127),
123             (($reaped_status & 128) ? "with" : "without") . " coredump",
124             "terminated at " . scalar localtime(),
125             _elapsed_time ($cfg));
126              
127             } else {
128 3         18 my $exit_code = $reaped_status >> 8;
129 3 100       14 if ($exit_code == 0) {
130 1         81 update_status ($cfg,
131             STATUS_COMPLETED,
132             "exit code $exit_code",
133             "completed at " . scalar localtime(),
134             _elapsed_time ($cfg));
135             } else {
136 2         157 update_status ($cfg,
137             STATUS_TERM_BY_ERR,
138             "exit code $exit_code",
139             "completed at " . scalar localtime(),
140             _elapsed_time ($cfg));
141             }
142             }
143 3         25 $cfg->save();
144              
145             # the wrapper of the daemon finishes; do not return anything
146 3         1105 exit (0);
147              
148             } elsif ($pid == 0) {
149             #
150             # --- this branch is executed in the just started child process
151             #
152              
153             # replace itself by an external process
154 4 100 100     384 if ($options->{ ALLOW_SHELL() } or @$args > 1) {
155             # this allows to execute things such as: 'date | wc'
156 2 0       0 exec (@$args) or
157             croak "Cannot execute the external process: " . _join_args ($args) . "\n";
158             } else {
159             # this is always save against interpreting $args by a shell
160 2 0       13 exec { $args->[0] } @$args or
  2         0  
161             croak "Cannot execute (using an indirect object) the external process: " . _join_args ($args) . "\n";
162             }
163              
164             } else {
165             #
166             # --- this branch is executed only when there is an error in the forking
167             #
168 0         0 croak "Cannot start an external process: " . _join_args ($args) . " - $!\n";
169             }
170             }
171              
172             #-----------------------------------------------------------------
173             # Pretty print of the list of arguments (given as an arrayref).
174             #-----------------------------------------------------------------
175             sub _join_args {
176 0     0   0 my $args = shift;
177 0         0 return join (" ", map {"'$_'"} @$args);
  0         0  
178             }
179              
180             #-----------------------------------------------------------------
181             # Return a pretty-formatted elapsed time of the just finished job.
182             #-----------------------------------------------------------------
183             sub _elapsed_time {
184 3     3   11 my $cfg = shift;
185 3         38 my $started = $cfg->param ("job.started");
186 3 50       20 return "elapsed time unknown" unless $started;
187 3         14 my $elapsed = time() - $started;
188 3         32 return "elapsed time $elapsed seconds";
189             }
190              
191             #-----------------------------------------------------------------
192             # Extract arguments for the start() method and return:
193             # ( [args], {options} )
194             # -----------------------------------------------------------------
195             sub _process_start_args {
196 38     38   8012 my @args;
197             my $options;
198 38 100 66     379 if (ref $_[0] and ref $_[0] eq 'ARRAY') {
199             # arguments for external process are given as an arrayref...
200 2         3 @args = @{ shift() };
  2         7  
201 2 100 66     12 $options = (ref $_[0] and ref $_[0] eq 'HASH') ? shift @_ : {};
202             } else {
203             # arguments for external process are given as an array...
204 36 100 66     300 $options = (ref $_[-1] and ref $_[-1] eq 'HASH') ? pop @_ : {};
205 36         459 @args = @_;
206             }
207 38         206 return (\@args, $options);
208             }
209              
210             #-----------------------------------------------------------------
211             # Update status and its details (just in memory - in the given $cfg).
212             #-----------------------------------------------------------------
213             sub update_status {
214 7     7 0 99 my ($cfg, $status, @details) = @_;
215              
216             # remove the existing status and its details
217 7         89 $cfg->remove ("job.status");
218 7         52 $cfg->remove ("job.status.detail");
219              
220             # put updated values
221 7         34 $cfg->param ("job.status", $status);
222 7         64 foreach my $detail (@details) {
223 13         44 $cfg->param ("job.status.detail", $detail);
224             }
225              
226             # note the finished time if the new status indicates the termination
227 7 100 66     293 if ($status eq STATUS_COMPLETED or
      100        
228             $status eq STATUS_TERM_BY_REQ or
229             $status eq STATUS_TERM_BY_ERR) {
230 3         15 $cfg->param ("job.ended", time());
231             }
232             }
233              
234             # -----------------------------------------------------------------
235             # Return status of the given job (given by $jobid). In array context,
236             # it also returns (optional) details of the status.
237             # -----------------------------------------------------------------
238             sub status {
239 74     74 1 229 my ($class, $jobid) = @_;
240 74 50       657 return unless defined wantarray; # don't bother doing more
241 74         442 my $dir = _id2dir ($jobid);
242 74         523 my ($cfg, $cfgfile) = $class->get_configuration ($dir);
243 74   50     267 my $status = $cfg->param ('job.status') || STATUS_UNKNOWN;
244 74 100       261 my @details = ($cfg->param ('job.status.detail') ? $cfg->param ('job.status.detail') : ());
245 74 100       924 return wantarray ? ($status, @details) : $status;
246             }
247              
248             #-----------------------------------------------------------------
249             # Return true if the status of the job indicates that the external
250             # program had finished (well or badly).
251             # -----------------------------------------------------------------
252             sub is_finished {
253 59     59 1 47009936 my ($class, $jobid) = @_;
254 59         446 my $status = $class->status ($jobid);
255             return
256 59   100     817 $status eq STATUS_COMPLETED ||
257             $status eq STATUS_TERM_BY_REQ ||
258             $status eq STATUS_TERM_BY_ERR;
259             }
260              
261             #-----------------------------------------------------------------
262             # Return the name of the working directory for the given $jobid.
263             # Or undef if such working directory does not exist.
264             # -----------------------------------------------------------------
265             sub working_dir {
266 19     19 1 14666 my ($class, $jobid) = @_;
267 19         360 my $dir = _id2dir ($jobid);
268 19 100 66     1579 return -e $dir && -d $dir ? $dir : undef;
269             }
270              
271             #-----------------------------------------------------------------
272             # Return a list of (some) filenames in a job directory that is
273             # specified by the given $jobid. The filenames are relative to this
274             # job directory, and they may include subdirectories if there are
275             # subdirectories within this job directory. The files with the special
276             # names (see the constants STDOUT_FILE, STDERR_FILE, CONFIG_FILE) are
277             # ignored. If there is an empty directory, it is also ignored.
278             #
279             # For example, if the contents of a job directory is:
280             # ___proc_async_stdout___
281             # ___proc_async_stderr___
282             # ___proc_async_status.cfg
283             # a.file
284             # a.dir/
285             # file1
286             # file2
287             # b.dir/
288             # file3
289             # empty.dir/
290             #
291             # then the returned list will look like this:
292             # ('a.file',
293             # 'a.dir/file1',
294             # 'a.dir/file2',
295             # 'b.dir/file3')
296             #
297             # It can croak if the $jobid is empty. If it does not represent an
298             # existing (and readable) directory, it returns an empty list (without
299             # croaking).
300             # -----------------------------------------------------------------
301             sub result_list {
302 21     21 1 49 my ($class, $jobid) = @_;
303 21         42 my $dir = _id2dir ($jobid);
304 21 50       483 return () unless -e $dir;
305              
306 21         56 my @files = ();
307             find (
308             sub {
309 210     210   329 my $regex = quotemeta ($dir);
310 210 100 100     8876 unless (m{^\.\.?$} || -d) {
311 105         161 my $file = $File::Find::name;
312 105         637 $file =~ s{^$regex[/\\]?}{};
313 105 100 100     2163 push (@files, $file)
      100        
314             unless
315             $file eq STDOUT_FILE or
316             $file eq STDERR_FILE or
317             $file eq CONFIG_FILE;
318             }
319             },
320 21         2548 $dir);
321 21         175 return @files;
322             }
323              
324             #-----------------------------------------------------------------
325             # Return the content of the given $file from the job given by
326             # $jobid. The $file is a relative filename; must be one of those
327             # returned by method result_list().
328             #
329             # Return undef if the $file does not exist (or if it does not exist in
330             # the list returned by result_list().
331             # -----------------------------------------------------------------
332             sub result {
333 14     14 1 13678 my ($class, $jobid, $file) = @_;
334 14         63 my @allowed_files = $class->result_list ($jobid);
335 14         35 my $dir = _id2dir ($jobid);
336 14         21 my $is_allowed = exists { map {$_ => 1} @allowed_files }->{$file};
  28         112  
337 14 50       56 return unless $is_allowed;
338 14         161 return read_file (File::Spec->catfile ($dir, $file));
339             }
340              
341             #-----------------------------------------------------------------
342             # Return the content of the STDOUT from the job given by $jobid. It
343             # may be an empty string if the job did not produce any STDOUT, or if
344             # the job does not exist anymore.
345             # -----------------------------------------------------------------
346             sub stdout {
347 11     11 1 141 my ($class, $jobid) = @_;
348 11         29 my $dir = _id2dir ($jobid);
349 11         120 my $file = File::Spec->catfile ($dir, STDOUT_FILE);
350 11         43 my $content = "";
351 11         55 eval {
352 11         185 $content = read_file ($file);
353             };
354 11         1479 return $content;
355             }
356              
357             #-----------------------------------------------------------------
358             # Return the content of the STDERR from the job given by $jobid. It
359             # may be an empty string if the job did not produce any STDERR, or if
360             # the job does not exist anymore.
361             # -----------------------------------------------------------------
362             sub stderr {
363 7     7 1 21 my ($class, $jobid) = @_;
364 7         21 my $dir = _id2dir ($jobid);
365 7         63 my $file = File::Spec->catfile ($dir, STDERR_FILE);
366 7         21 my $content = "";
367 7         14 eval {
368 7         56 $content = read_file ($file);
369             };
370 7         539 return $content;
371             }
372              
373             #-----------------------------------------------------------------
374             # Remove files belonging to the given job, including its directory.
375             # -----------------------------------------------------------------
376             sub clean {
377 23     23 1 14369 my ($class, $jobid) = @_;
378 23         194 my $dir = _id2dir ($jobid);
379 23         24105 my $file_count = remove_tree ($dir); #, {verbose => 1});
380 23         345 return $file_count;
381             }
382              
383             # -----------------------------------------------------------------
384             # Send a signal to the given job. $signal is a positive integer
385             # between 1 and 64. Default is 9 which means the KILL signal. Return
386             # true on success, zero on failure (no such job, no such process). It
387             # may also croak if the $jobid is invalid or missing, at all, or if
388             # the $signal is invalid.
389             # -----------------------------------------------------------------
390             sub signal {
391 0     0 1 0 my ($class, $jobid, $signal) = @_;
392 0         0 my $dir = _id2dir ($jobid);
393 0 0       0 $signal = 9 unless $signal; # Note that $signal zero is also changed to 9
394 0 0       0 croak "Bad signal: $signal.\n"
395             unless $signal =~ m{^[+]?\d+$};
396 0         0 my ($cfg, $cfgfile) = $class->get_configuration ($dir);
397 0         0 my $pid = $cfg->param ('job.pid');
398 0 0       0 return 0 unless $pid;
399 0         0 return kill $signal, $pid;
400             }
401              
402             #-----------------------------------------------------------------
403             # Check given $options (a hashref), some may be removed.
404             # -----------------------------------------------------------------
405             sub _check_options {
406 34     34   41 my $options = shift;
407              
408             # TIMEOUT may not be used on some architectures; must be a
409             # positive integer
410 34 50       122 if (exists $options->{TIMEOUT}) {
411 0         0 my $timeout = $options->{TIMEOUT};
412 0 0       0 if (_is_int ($timeout)) {
413 0 0       0 if ($timeout == 0) {
    0          
414 0         0 delete $options->{TIMEOUT};
415             } elsif ($timeout < 0) {
416 0         0 delete $options->{TIMEOUT};
417 0         0 carp "Warning: Option TIMEOUT is negative. Ignored.\n";
418             }
419             } else {
420 0         0 delete $options->{TIMEOUT};
421 0         0 carp "Warning: Option TIMEOUT is not a number (found '$options->{TIMEOUT}'). Ignored.\n";
422             }
423 0 0       0 if (exists $options->{TIMEOUT}) {
424 0   0     0 my $has_nonblocking = $Config{d_waitpid} eq "define" || $Config{d_wait4} eq "define";
425 0 0       0 unless ($has_nonblocking) {
426 0         0 delete $options->{TIMEOUT};
427 0         0 carp "Warning: Option TIMEOUT cannot be used on this system. Ignored.\n";
428             }
429             }
430             }
431              
432             # check for unknown options
433 34         303 foreach my $key (sort keys %$options) {
434 7 50       49 carp "Warning: Unknown option '$key'. Ignored.\n"
435             unless exists $KNOWN_OPTIONS->{$key};
436             }
437              
438             }
439              
440             sub _is_int {
441 0     0   0 my ($str) = @_;
442 0 0       0 return unless defined $str;
443 0 0       0 return $str =~ /^[+-]?\d+$/ ? 1 : undef;
444             }
445              
446             #-----------------------------------------------------------------
447             # Create a configuration instance and load it from the configuration
448             # file (if exists) for the given job. Return ($cfg, $cfgfile).
449             # -----------------------------------------------------------------
450             sub get_configuration {
451 110     110 1 1073 my ($class, $jobid) = @_;
452 110         276 my $dir = _id2dir ($jobid);
453 110         1717 my $cfgfile = File::Spec->catfile ($dir, CONFIG_FILE);
454 110         1680 my $cfg = Proc::Async::Config->new ($cfgfile);
455 110         535 return ($cfg, $cfgfile);
456             }
457              
458             #-----------------------------------------------------------------
459             # Create and fill the configuration file. Return the filename and a
460             # configuration instance.
461             # -----------------------------------------------------------------
462             sub _start_config {
463 35     35   639 my ($jobid, $args, $options) = @_;
464              
465             # create configuration file
466 35         142 my ($cfg, $cfgfile) = Proc::Async->get_configuration ($jobid);
467              
468             # ...and fill it
469 35         218 $cfg->param ("job.id", $jobid);
470 35         200 foreach my $arg (@$args) {
471 157         414 $cfg->param ("job.arg", $arg);
472             }
473 35         153 foreach my $key (sort keys %$options) {
474 9         59 $cfg->param ("option.$key", $options->{$key});
475             }
476 35         267 $cfg->param ("job.status", STATUS_CREATED);
477              
478 35         289 $cfg->save();
479 35         122 return ($cfg, $cfgfile);
480             }
481              
482             #-----------------------------------------------------------------
483             # Create and return a unique ID.
484             #### (the ID may be influenced by some of the $options).
485             #-----------------------------------------------------------------
486             sub _generate_job_id {
487             # my $options = shift; # an optional hashref
488             # if ($options and exists $options->{DIR}) {
489             # return tempdir ( CLEANUP => 0, DIR => $options->{DIR} );
490             # } else {
491             # return tempdir ( CLEANUP => 0 );
492             # }
493 36     36   4214 return tempdir (CLEANUP => 0, DIR => File::Spec->tmpdir);
494             }
495              
496             #-----------------------------------------------------------------
497             # Return a name of a directory asociated with the given job ID; in
498             # this implementation, it returns the same value as the job ID; it
499             # croaks if called without a parameter OR if $jobid points to a
500             # strange (not expected) place.
501             #-----------------------------------------------------------------
502             sub _id2dir {
503 314     314   1638 my $jobid = shift;
504 314 50       911 croak ("Missing job ID.\n")
505             unless $jobid;
506              
507             # does the $jobid start in the temporary directory?
508 314         6473 my $tmpdir = File::Spec->tmpdir; # this must be the same as used in _generate_job_id
509 314 50       3178 croak ("Invalid job ID '$jobid'.\n")
510             unless $jobid =~ m{^\Q$tmpdir\E[/\\]};
511              
512 314         948 return $jobid;
513             }
514              
515             1;
516              
517              
518              
519             =pod
520              
521             =head1 NAME
522              
523             Proc::Async - Running and monitoring processes asynchronously
524              
525             =head1 VERSION
526              
527             version 0.2.0
528              
529             =head1 SYNOPSIS
530              
531             use Proc::Async;
532              
533             # start an external program
534             $jobid = Proc::Async->start ('blastx', '-query', '/data/my.seq', '-out', 'blastout');
535              
536             # later, usually from another program (or in another time),
537             # investigate what is the external program doing
538             if (Proc::Async->is_finished ($jobid)) {
539             @files = Proc::Async->result_list ($jobid);
540             foreach my $file (@files) {
541             print Proc::Async->result ($file);
542             }
543             print Proc::Async->stdout();
544             print Proc::Async->stderr();
545             }
546              
547             $status = Proc::Async->status ($jobid);
548              
549             =head1 DESCRIPTION
550              
551             This module can execute an external process, monitor its state, get
552             its results and, if needed, kill it prematurely and remove its
553             results. There are, of course, many modules that cover similar
554             functionality, including functions directly built-in in Perl. So why
555             to have this module, at all? The main feature is hidden in the second
556             part of the module name, the word B. The individual methods (to
557             execute, to monitor, to get results, etc.) can be called (almost)
558             independently from each other, from separate Perl programs, and there
559             may be any delay between them.
560              
561             It focuses mainly on invoking external programs from the CGI scripts
562             in the web applications. Here is a typical scenario: Your CGI script
563             starts an external program which may take some time before it
564             finishes. The CGI scripts does not wait for it and returns back,
565             remembering (e.g. in a form of a hidden variable in the returned HTML
566             page) the only thing, the ID of the just started job (a
567             C). Meanwhile, the invoked external program has been
568             I (it became a daemon process, a process nobody waits
569             for). Now you have another CGI script that can use the remembered
570             C to monitor status and get results of the previously started
571             process.
572              
573             The core functionality, the demonization, is done by the module
574             C. If you plan to write a single program that starts a
575             daemon process and waits for it, then you may need just the
576             C module. But if you wish to split individual calls into
577             two or more programs then the C may be your choice.
578              
579             =head1 METHODS
580              
581             All methods of this module are I methods, there is no C
582             instance constructor. It does not make much sense to have an instance
583             if you wish to use it from a separate program, does it? The
584             communication between individual calls is done in a temporary
585             directory (as it is explained later in this documentation but it is
586             not important for the module usage).
587              
588             =head2 start($args [,$options]) I start(@args, [$options])
589              
590             This method starts an external program, makes a daemon process from
591             it, does not wait for its completion and returns a token, a job
592             ID. This token will be used as an argument in all other
593             methods. Therefore, there is no sense to call any of the other
594             methods without calling the C first.
595              
596             C<$args> is an arrayref with the full command-line (including the
597             external program name). Or, it can be given as a normal list C<@args>.
598              
599             For example:
600              
601             my $jobid = Proc::Async->start (qw{ wget -O cpan.index.html http://search.cpan.org/index.html });
602              
603             or
604              
605             my $jobid = Proc::Async->start ( [qw{ wget -O cpan.index.html http://search.cpan.org/index.html }] );
606              
607             If the given array of arguments has only one element, it is still
608             considered as an array. Therefore, you cannot use a single string
609             representing the full command-line:
610              
611             # this will not work
612             $jobid = start ("date -u");
613              
614             This is a feature not a bug. It prevents to let the shell interprets
615             the meta-characters inside the arguments. More about it in the Perl's
616             documentation (try: C). But sometimes you are willing
617             to sacrifice safety and to let a shell to act for your benefit. An
618             example is the usage of a pipe character in the command line. In order
619             to allow it, you need to specify an option C
620             in the start() method:
621              
622             # this works
623             $jobid = start ("date -u", { Proc::Async::ALLOW_SHELL() => 1 });
624              
625             # ...and this works, as well
626             # (it prints number 3 to the standard output)
627             $jobid = start ("echo one two three | wc -w", { Proc::Async::ALLOW_SHELL() => 1 });
628              
629             The options (so far only one is recognized) are given as a hashref
630             that is the last argument of the C method. The keys of this
631             hash are defined as constants in this module:
632              
633             use constant {
634             ALLOW_SHELL => 'ALLOW_SHELL',
635              
636             };
637              
638             For each job, this method creates a temporary directory (within your
639             system temporary directory, which is, on Unix system, usually C)
640             and change there (C) before executing the wanted external
641             program. Keep this directory change in mind if your external programs
642             are in the same directory as your Perl program that invokes them. You
643             can use, for example, the C module to locate them correctly:
644              
645             use FindBin qw($Bin);
646             ...
647             my @args = ("$Bin/my-external-program", ....);
648             $jobid = Proc::Async->start (\@args);
649              
650             If you need to access this job directory (in case that you need more
651             than provided by the methods of this module), use the method
652             C to get its path and name.
653              
654             =head2 status($jobid)
655              
656             In scalar context, it returns status of the given process (given by
657             its $jobid). The status is expressed by a plain text using the
658             following constants:
659              
660             use constant {
661             STATUS_UNKNOWN => 'unknown',
662             STATUS_CREATED => 'created',
663             STATUS_RUNNING => 'running',
664             STATUS_COMPLETED => 'completed',
665             STATUS_TERM_BY_REQ => 'terminated by request',
666             STATUS_TERM_BY_ERR => 'terminated by error',
667             };
668              
669             In array context, it additionally returns (optional) details of the
670             status. There can be zero to more details accompanying the status,
671             e.g. the exit code, or the signal number that caused the process to
672             die. The details are in plain text, no constants used. For example:
673              
674             $jobid = Proc::Async->start ('date');
675             @status = Proc::Async->status ($jobid);
676             print join ("\n", @status);
677              
678             will print:
679              
680             running
681             started at Sat May 18 09:35:27 2013
682              
683             or
684              
685             $jobid = Proc::Async->start ('sleep', 5);
686             ...
687             @status = Proc::Async->status ($jobid);
688             print join ("\n", @status);
689              
690             will print:
691              
692             completed
693             exit code 0
694             completed at Sat May 18 09:45:12 2013
695             elapsed time 5 seconds
696              
697             or, a case when the started job was killed:
698              
699             $jobid = Proc::Async->start ('sleep', 60);
700             Proc::Async->signal ($jobid, 9);
701             @status = Proc::Async->status ($jobid);
702             print join ("\n", @status);
703              
704             will print:
705              
706             terminated by request
707             terminated by signal 9
708             without coredump
709             terminated at Sat May 18 09:41:56 2013
710             elapsed time 0 seconds
711              
712             =head2 is_finished($jobid)
713              
714             A convenient method that returns true if the status of the job
715             indicates that the external program had finished (well or badly). Or
716             false if not. Which includes the case when the state is unknown.
717              
718             =head2 signal($jobid [,$signal])
719              
720             It sends a signal to the given job (given by the
721             C<$jobid>). C<$signal> is a positive integer between 1 and 64. Default
722             is 9 which means the KILL signal. The available signals are the ones
723             listed out by C on your system.
724              
725             It returns true on success, zero on failure (no such job, no such
726             process). It can also croak if the C<$signal> is invalid.
727              
728             =head2 result_list($jobid)
729              
730             It returns a list of (some) filenames that exist in the job directory
731             that is specified by the given $jobid. The filenames are relative to
732             this job directory, and they may include subdirectories if there are
733             subdirectories within this job directory (it all depends what your
734             external program created there). For example:
735              
736             $jobid = Proc::Async->start (qw{ wget -o log.file -O output.file http://www.perl.org/index.html });
737             ...
738             @files = Proc::Async->result_list ($jobid);
739             print join ("\n", @files);
740              
741             prints:
742              
743             output.file
744             log.file
745              
746             The names of the files returned by the C can be used in
747             the method C in order to get the file content.
748              
749             If the given $jobid does not represent an existing (and readable)
750             directory, it returns an empty list (without croaking).
751              
752             If the external program created new files inside new directories, the
753             C returns names of these files, too. In other words, it
754             returns names of all files found within the job directory (however
755             deep in sub-directories), except special files (see the next
756             paragraph) and empty sub-directories.
757              
758             There are also files with the special names, as defined by the
759             following constants:
760              
761             use constant STDOUT_FILE => '___proc_async_stdout___';
762             use constant STDERR_FILE => '___proc_async_stderr___';
763             use constant CONFIG_FILE => '___proc_async_status.cfg';
764              
765             These files contain standard streams of the external programs (their
766             content can be fetched by the methods C and C) and
767             internal information about the status of the executed program.
768              
769             Another example: If the contents of a job directory is the following:
770              
771             ___proc_async_stdout___
772             ___proc_async_stderr___
773             ___proc_async_status.cfg
774             a.file
775             a.dir/
776             file1
777             file2
778             b.dir/
779             file3
780             empty.dir/
781              
782             then the returned list will look like this:
783              
784             ('a.file',
785             'a.dir/file1',
786             'a.dir/file2',
787             'b.dir/file3')
788              
789             =head2 result($jobid, $file)
790              
791             It returns the content of the given $file from the job given by
792             $jobid. The $file is a relative filename; must be one of those
793             returned by method C. It returns undef if the $file
794             does not exist (or if it does not exist in the list returned by
795             C).
796              
797             For getting content of the standard stream, use the following methods:
798              
799             =head2 stdout($jobid)
800              
801             It returns the content of the STDOUT from the job given by $jobid. It
802             may be an empty string if the job did not produce any STDOUT, or if
803             the job does not exist anymore.
804              
805             =head2 stderr($jobid)
806              
807             It returns the content of the STDERR from the job given by $jobid. It
808             may be an empty string if the job did not produce any STDERR, or if
809             the job does not exist anymore.
810              
811             If you execute an external program that cannot be found you will find
812             an error message about it here, as well:
813              
814             my $jobid = Proc::Async->start ('a-bad-program');
815             ...
816             print join ("\n", Proc::Async->status ($jobid);
817              
818             terminated by error
819             exit code 2
820             completed at Sat May 18 11:02:04 2013
821             elapsed time 0 seconds
822              
823             print Proc::Async->stderr();
824              
825             Can't exec "a-bad-program": No such file or directory at lib/Proc/Async.pm line 148.
826              
827             =head2 working_dir($jobid)
828              
829             It returns the name of the working directory for the given $jobid. Or
830             undef if such working directory does not exist.
831              
832             You may notice that the $jobid looks like a name of a working
833             directory. Actually, in the current implementation, it is, indeed, the
834             same. But it may change in the future. Therefore, better use this
835             method and do not rely on such sameness.
836              
837             =head2 clean($jobid)
838              
839             It deletes all files belonging to the given job, including its job
840             directory. It returns the number of file successfully deleted. If you
841             ask for a status of the job after being cleaned up, you get
842             C.
843              
844             =head2 get_configuration($jobid)
845              
846             Use this method only if you wish to look at the internals (for example
847             to get exact starting and ending time of a job). It creates a
848             configuration (an instance of C) and fills it
849             from the configuration file (if such file exists) for the given
850             job. It returns a two-element array, the first element being a
851             configuration instance, the second element the file name where the
852             configuration was filled from:
853              
854             my $jobid = Proc::Async->start ('date', '-u');
855             ...
856             my ($cfg, $cfgfile) = Proc::Async->get_configuration ($jobid);
857             foreach my $name ($cfg->param) {
858             foreach my $value ($cfg->param ($name)) {
859             print STDOUT "$name=$value\n";
860             }
861             }
862              
863             will print:
864              
865             job.arg=date
866             job.arg=-u
867             job.ended=1368865570
868             job.id=/tmp/q74Bgd8mXX
869             job.pid=22273
870             job.started=1368865570
871             job.status=completed
872             job.status.detail=exit code 0
873             job.status.detail=completed at Sat May 18 11:26:10 2013
874             job.status.detail=elapsed time 0 seconds
875              
876             =head1 ADDITIONAL FILES
877              
878             The module distribution has several example and helping files (which
879             are not installed when the module is fetched by the C or
880             C).
881              
882             =head3 scripts/procasync
883              
884             It is a command-line oriented script that can invoke any of the
885             functionality of this module. Its purpose is to test the module and,
886             perhaps more importantly, to show how to use the module's
887             methods. Otherwise, it does not make much sense (that is why it is not
888             normally installed).
889              
890             It has its own (but only short) documentation:
891              
892             scripts/procasync -help
893              
894             or
895              
896             perldoc scripts/procasync
897              
898             Some examples are:
899              
900             scripts/procasync -start date
901             scripts/procasync -start 'date -u'
902             scripts/procasync -start 'sleep 100'
903              
904             The C<-start> arguments can be repeated if its arguments have spaces:
905              
906             scripts/procasync -start cat -start '/data/filename with spaces'
907              
908             All lines above print a job ID that must be used in a consequent usage:
909              
910             scripts/procasync -jobid /tmp/hBsXcrafhn -status
911             scripts/procasync -jobid /tmp/hBsXcrafhn -stdout -stderr -rlist
912             scripts/procasync -jobid /tmp/hBsXcrafhn -wdir
913             ...etc...
914              
915             =head3 examples/README
916              
917             Because this module is focused mainly on its usage within CGI scripts,
918             there is an example of a simple web application. The C file
919             explains how to install it and run it from your web server. Here
920             L
921             is its screenshot.
922              
923             =head3 t/data/extester
924              
925             This script can be used for testing this module (as it is used in the
926             regular Perl tests and in the web application mentioned above). It can
927             be invoked as an external program and, depending on its command line
928             arguments, it creates some standard and/or standard error streams,
929             exits with the specified exit code, etc. It has its own documentation:
930              
931             perldoc t/data/extester
932              
933             An example of its command-line:
934              
935             extester -stdout an-out -stderr an-err -exit 5 -create a.tmp=5 few/new/dirs/b.tmp=3 an/empty/dir/=0
936              
937             which writes given short texts into stdout and stderr, creates two
938             files (C and C, the latter one together with the given
939             sub-directories hierarchy) and it exits with exit code 5.
940              
941             =head1 BUGS
942              
943             Please report any bugs or feature requests to
944             L.
945              
946             =head2 Missing features
947              
948             =over
949              
950             =item Standard input
951              
952             Currently, there is no support for providing standard input for the
953             started external process.
954              
955             =back
956              
957             =head1 AUTHOR
958              
959             Martin Senger
960              
961             =head1 COPYRIGHT AND LICENSE
962              
963             This software is copyright (c) 2013 by Martin Senger, CBRC-KAUST (Computational Biology Research Center - King Abdullah University of Science and Technology) All Rights Reserved.
964              
965             This is free software; you can redistribute it and/or modify it under
966             the same terms as the Perl 5 programming language system itself.
967              
968             =cut
969              
970              
971             __END__