File Coverage

blib/lib/Siebel/Srvrmgr/Daemon/Heavy.pm
Criterion Covered Total %
statement 174 258 67.4
branch 45 124 36.2
condition 16 54 29.6
subroutine 25 25 100.0
pod 2 2 100.0
total 262 463 56.5


line stmt bran cond sub pod time code
1             package Siebel::Srvrmgr::Daemon::Heavy;
2              
3             =pod
4              
5             =head1 NAME
6              
7             Siebel::Srvrmgr::Daemon::Heavy - "heavier' implementation of Siebel::Srvrmgr::Daemon
8              
9             =head1 SYNOPSIS
10              
11             use Siebel::Srvrmgr::Daemon;
12              
13             my $daemon = Siebel::Srvrmgr::Daemon->new(
14             {
15             server => 'servername',
16             gateway => 'gateway',
17             enterprise => 'enterprise',
18             user => 'user',
19             password => 'password',
20             bin => 'c:\\siebel\\client\\bin\\srvrmgr.exe',
21             is_infinite => 1,
22             commands => [
23             Siebel::Srvrmgr::Daemon::Command->new(
24             command => 'load preferences',
25             action => 'LoadPreferences'
26             ),
27             Siebel::Srvrmgr::Daemon::Command->new(
28             command => 'list comp type',
29             action => 'ListCompTypes',
30             params => [$comp_types_file]
31             ),
32             Siebel::Srvrmgr::Daemon::Command->new(
33             command => 'list comp',
34             action => 'ListComps',
35             params => [$comps_file]
36             ),
37             Siebel::Srvrmgr::Daemon::Command->new(
38             command => 'list comp def',
39             action => 'ListCompDef',
40             params => [$comps_defs_file]
41             )
42             ]
43             }
44             );
45              
46              
47             =head1 DESCRIPTION
48              
49             This class is the "heavier" version of L<Siebel::Srvmrgr::Daemon>. By heavier, understand more complex code to be able to deal with a heavier usage
50             of srvrmgr.
51              
52             This class is indicated to be used in cenarios where several commands need to be executed in a short time interval: it will connect to srvrmgr by using
53             IPC for communication between the processes and once connected, the srvrmgr session will be reused as many times as desired instead of following the
54             sequence of connect -> run commands -> disconnect.
55              
56             The sessions are not "interactive" from the user point of view but the usage of this class enable the adoption of some logic to change how the commands will
57             be executed or even generate commands on the fly.
58              
59             This module is based on L<IPC::Open3::Callback> from Lucas Theisen (see SEE ALSO section) implemented in L<Siebel::Srvrmgr::Daemon::IPC>.
60              
61             Since it uses Perl IPC, this class may suffer from good support in OS plataforms that are not UNIX-like. Be sure to check out tests results of the distribution
62             before trying to use it.
63              
64             =cut
65              
66 4     4   277540 use Moose;
  4         844005  
  4         27  
67 4     4   21164 use namespace::autoclean;
  4         3102  
  4         17  
68 4     4   1235 use Siebel::Srvrmgr::Daemon::Condition;
  4         25731  
  4         176  
69 4     4   1501 use Siebel::Srvrmgr::Daemon::ActionFactory;
  4         27  
  4         166  
70             use Siebel::Srvrmgr::Regexes
71 4     4   2798 qw(SRVRMGR_PROMPT LOAD_PREF_RESP SIEBEL_ERROR ROWS_RETURNED);
  4         7  
  4         277  
72 4     4   915 use Siebel::Srvrmgr::Daemon::Command;
  4         33  
  4         119  
73 4     4   1626 use POSIX;
  4         13228  
  4         786  
74 4     4   6697 use Data::Dumper;
  4         5  
  4         166  
75 4     4   17 use Scalar::Util qw(weaken openhandle);
  4         4  
  4         156  
76 4     4   15 use Config;
  4         4  
  4         125  
77 4     4   1233 use Siebel::Srvrmgr::IPC;
  4         8  
  4         185  
78 4     4   2873 use IO::Select;
  4         4881  
  4         204  
79 4     4   2248 use Encode;
  4         21444  
  4         295  
80 4     4   22 use Carp qw(longmess);
  4         6  
  4         155  
81 4     4   1047 use Siebel::Srvrmgr;
  4         11  
  4         11223  
82              
83             extends 'Siebel::Srvrmgr::Daemon';
84              
85             our $SIG_INT = 0;
86             our $SIG_PIPE = 0;
87             our $SIG_ALARM = 0;
88              
89             # :TODO :16/08/2013 19:02:24:: add statistics for daemon, like number of runs and average of used buffer for each command
90              
91             =pod
92              
93             =head2 write_fh
94              
95             A filehandle reference to the C<srvrmgr> STDIN. This is a read-only attribute.
96              
97             =cut
98              
99             has write_fh => (
100             isa => 'FileHandle',
101             is => 'ro',
102             writer => '_set_write',
103             reader => 'get_write'
104             );
105              
106             =pod
107              
108             =head2 read_fh
109              
110             A filehandle reference to the C<srvrmgr> STDOUT.
111              
112             This is a read-only attribute.
113              
114             =cut
115              
116             has read_fh => (
117             isa => 'FileHandle',
118             is => 'ro',
119             writer => '_set_read',
120             reader => 'get_read'
121             );
122              
123             =pod
124              
125             =head2 error_fh
126              
127             A filehandle reference to the C<srvrmgr> STDERR.
128              
129             This is a read-only attribute.
130              
131             =cut
132              
133             has error_fh => (
134             isa => 'FileHandle',
135             is => 'ro',
136             writer => '_set_error',
137             reader => 'get_error'
138             );
139              
140             =pod
141              
142             =head2 read_timeout
143              
144             The timeout for trying to read from child process handlers in seconds. It defaults to 1 second.
145              
146             Changing this value may help improving performance, but should be used with care.
147              
148             =cut
149              
150             has read_timeout => (
151             isa => 'Int',
152             is => 'rw',
153             writer => 'set_read_timeout',
154             reader => 'get_read_timeout',
155             default => 1
156             );
157              
158             =pod
159              
160             =head2 child_pid
161              
162             An integer presenting the process id (PID) of the process created by the OS when the C<srvrmgr> program is executed.
163              
164             This is a read-only attribute.
165              
166             =cut
167              
168             has child_pid => (
169             isa => 'Int',
170             is => 'ro',
171             writer => '_set_pid',
172             reader => 'get_pid',
173             clearer => 'clear_pid',
174             predicate => 'has_pid',
175             trigger => \&_add_retry
176             );
177              
178             =head2 last_exec_cmd
179              
180             This is a string representing the last command submitted to the C<srvrmgr> program. The default value for it is an
181             empty string (meaning that no command was submitted yet).
182              
183             =cut
184              
185             has last_exec_cmd => (
186             isa => 'Str',
187             is => 'ro',
188             default => '',
189             reader => 'get_last_cmd',
190             writer => '_set_last_cmd'
191             );
192              
193             =pod
194              
195             =head2 params_stack
196              
197             This is an array reference with the stack of params passed to the respective class. It is maintained automatically by the class so the attribute is read-only.
198              
199             =cut
200              
201             has params_stack => (
202             isa => 'ArrayRef',
203             is => 'ro',
204             writer => '_set_params_stack',
205             reader => 'get_params_stack'
206             );
207              
208             =pod
209              
210             =head2 action_stack
211              
212             This is an array reference with the stack of actions to be taken. It is maintained automatically by the class, so the attribute is read-only.
213              
214             =cut
215              
216             has action_stack => (
217             isa => 'ArrayRef',
218             is => 'ro',
219             writer => '_set_action_stack',
220             reader => 'get_action_stack'
221             );
222              
223             =head2 ipc_buffer_size
224              
225             A integer describing the size of the buffer used to read output from srvrmgr program by using IPC.
226              
227             It defaults to 32768 bytes, but it can be adjusted to improve performance (lowering CPU usage by increasing memory utilization).
228              
229             Increase of this attribute should be considered experimental.
230              
231             =cut
232              
233             has ipc_buffer_size => (
234             isa => 'Int',
235             is => 'rw',
236             reader => 'get_buffer_size',
237             writer => 'set_buffer_size',
238             default => 32768
239             );
240              
241             =head2 srvrmgr_prompt
242              
243             An string representing the prompt recovered from srvrmgr program. The value of this attribute is set automatically during srvrmgr execution.
244              
245             =cut
246              
247             has srvrmgr_prompt =>
248             ( isa => 'Str', is => 'ro', reader => 'get_prompt', writer => '_set_prompt' );
249              
250             =head1 METHODS
251              
252             =cut
253              
254             sub _add_retry {
255              
256 2     2   6 my ( $self, $new, $old ) = @_;
257              
258             # if $old is undefined, this is the first call to run method
259 2 50       17 unless ( defined($old) ) {
260              
261 2         91 return 0;
262              
263             }
264             else {
265              
266 0 0       0 unless ( $new == $old ) {
267              
268 0         0 $self->_set_retries( $self->get_retries() + 1 );
269 0         0 return 1;
270              
271             }
272             else {
273              
274 0         0 return 0;
275              
276             }
277              
278             }
279              
280             }
281              
282             =pod
283              
284             =head2 BUILD
285              
286             This methods calls C<clear_pid> just to have a sane setting on C<child_pid> attribute.
287              
288             =cut
289              
290             sub BUILD {
291              
292 3     3 1 7 my $self = shift;
293 3         115 $self->clear_pid();
294              
295             }
296              
297             =pod
298              
299              
300             =head2 clear_pid
301              
302             Clears the defined PID associated with the child process that executes srvrmgr. This is usually associated with calling C<close_child>.
303              
304             Beware that this is different then removing the child process or even C<undef> the attribute. This just controls a flag that the attribute C<child_pid>
305             is defined or not. See L<Moose> attributes for details.
306              
307             =head2 has_pid
308              
309             Returns true or false if the C<child_pid> is defined. Beware that this is different then checking if there is an integer associated with C<child_pid>
310             attribute: this method might return false even though the old PID associated with C<child_pid> is still available. See L<Moose> attributes for details.
311              
312             =head2 get_prompt
313              
314             Returns the content of the attribute C<srvrmgr_prompt>.
315              
316             =head2 get_buffer_size
317              
318             Returns the value of the attribute C<ipc_buffer_size>.
319              
320             =head2 set_buffer_size
321              
322             Sets the attribute C<ipc_buffer_size>. Expects an integer as parameter, multiple of 1024.
323              
324             =head2 get_write
325              
326             Returns the file handle of STDIN from the process executing the srvrmgr program based on the value of the attribute C<write_fh>.
327              
328             =head2 get_read
329              
330             Returns the file handle of STDOUT from the process executing the srvrmgr program based on the value of the attribute C<read_fh>.
331              
332             =head2 get_error
333              
334             Returns the file handle of STDERR from the process executing the srvrmgr program based on the value of the attribute C<error_fh>.
335              
336             =head2 get_pid
337              
338             Returns the content of C<pid> attribute as an integer.
339              
340             =head2 is_infinite
341              
342             Returns the content of the attribute C<is_infinite>, returning true or false depending on this value.
343              
344             =head2 get_last_cmd
345              
346             Returns the content of the attribute C<last_cmd> as a string.
347              
348             =head2 get_params_stack
349              
350             Returns the content of the attribute C<params_stack>.
351              
352             =cut
353              
354             override '_setup_commands' => sub {
355              
356             my $self = shift;
357              
358             super();
359              
360             my $cmds_ref = $self->get_commands();
361              
362             my @cmd;
363             my @actions;
364             my @params;
365              
366             foreach my $cmd ( @{$cmds_ref} ) {
367              
368             push( @cmd, $cmd->get_command() );
369             push( @actions, $cmd->get_action() );
370             push( @params, $cmd->get_params() );
371              
372             }
373              
374             $self->_set_cmd_stack( \@cmd );
375             $self->_set_action_stack( \@actions );
376             $self->_set_params_stack( \@params );
377              
378             return 1;
379              
380             };
381              
382             =pod
383              
384             =head2 run
385              
386             This method will try to connect to a Siebel Enterprise through C<srvrmgr> program (if it is the first time the method is invoke) or reuse an already open
387             connection to submit the commands and respective actions defined during object creation. The path to the program is check and if it does not exists the
388             method will issue an warning message and immediatly returns false.
389              
390             Those operations will be executed in a loop as long the C<check> method from the class L<Siebel::Srvrmgr::Daemon::Condition> returns true.
391              
392             =cut
393              
394             # :WORKAROUND:10/05/2013 15:23:52:: using a state machine with FSA::Rules is difficult here because it is necessary to loop over output from
395             # srvrmgr but the program will hang if there is no output left to be read from srvrmgr.
396              
397             override 'run' => sub {
398              
399             my $self = shift;
400              
401             super();
402              
403             my $logger;
404             my $temp;
405             my $ignore_output = 0;
406              
407             my ( $read_h, $write_h, $error_h );
408              
409             unless ( $self->has_pid() ) {
410              
411             confess( $self->get_bin()
412             . ' returned un unrecoverable error, aborting execution' )
413             unless ( $self->_create_child() );
414              
415             # :WORKAROUND:31/07/2013 14:42:33:: must initialize the Log::Log4perl after forking the srvrmgr to avoid sharing filehandles
416             $logger = Siebel::Srvrmgr->gimme_logger( ref($self) );
417              
418             }
419             else {
420              
421             $logger = Siebel::Srvrmgr->gimme_logger( ref($self) );
422             $logger->info( 'Reusing PID ', $self->get_pid() )
423             if ( $logger->is_debug() );
424             $ignore_output = 1;
425              
426             }
427              
428             weaken($logger);
429             $logger->info('Starting run method');
430              
431             my @input_buffer;
432             my $timeout = $self->get_read_timeout; # avoid multiple method invocations
433              
434             # :TODO :06/08/2013 19:13:47:: create condition as a hidden attribute of this class
435             my $condition = Siebel::Srvrmgr::Daemon::Condition->new(
436             {
437             is_infinite => $self->is_infinite(),
438             total_commands => scalar( @{ $self->get_commands() } ),
439             cmd_sent => 0
440             }
441             );
442              
443             my $parser = $self->create_parser();
444             my $select = IO::Select->new();
445             my $data_ref = $self->_create_handle_buffer( $select, $logger );
446              
447             # :WARNING:16-07-2014 11:35:13:: cannot using SRVRMGR_PROMPT regex because it is too restrictive
448             # since we are reading a stream here. The regex is a copy of SRVRMGR_PROMPT without the "^" at the beginning
449             my $prompt_regex = qr/srvrmgr(\:[\w\_\-]+)?>\s(.*)?$/;
450             my $eol_regex = qr/\015\012$/;
451              
452             $logger->debug( 'sysread buffer size is ' . $self->get_buffer_size() )
453             if ( $logger->is_debug() );
454              
455             do {
456              
457             exit if ($SIG_INT);
458              
459             # :TODO:18-10-2013:arfreitas: move all code inside the while block to a different method to help and clean up lexicals
460             while ( my @ready = $select->can_read($timeout) ) {
461              
462             foreach my $fh (@ready) {
463              
464             my $fh_name = fileno($fh);
465             my $fh_bytes = $fh_name . '_bytes';
466              
467             $logger->debug( 'Reading filehandle ' . fileno($fh) )
468             if ( $logger->is_debug() );
469              
470             unless (( defined( $data_ref->{$fh_bytes} ) )
471             and ( $data_ref->{$fh_bytes} > 0 ) )
472             {
473              
474             $data_ref->{$fh_bytes} =
475             sysread( $fh, $data_ref->{$fh_name},
476             $self->get_buffer_size() );
477              
478             }
479             else {
480              
481             $logger->info(
482             'Caught part of a record, repeating sysread with offset'
483             ) if ( $logger->is_info() );
484              
485             # Like all Perl character operations, length() normally deals in
486             # logical characters, not physical bytes. For how many bytes a
487             # string encoded as UTF-8 would take up, use
488             # "length(Encode::encode_utf8(EXPR))" (you'll have to "use Encode"
489             # first). See Encode and perlunicode.
490             my $offset =
491             length( Encode::encode_utf8( $data_ref->{$fh_name} ) );
492              
493             $logger->debug("Offset is $offset")
494             if ( $logger->is_debug() );
495              
496             $data_ref->{$fh_bytes} =
497             sysread( $fh, $data_ref->{$fh_name},
498             $self->get_buffer_size(), $offset );
499              
500             }
501              
502             unless ( defined( $data_ref->{$fh_bytes} ) ) {
503              
504             $logger->fatal( 'sysread returned an error: ' . $! );
505              
506             $self->_check_child($logger);
507              
508             $logger->logdie( 'sysreading from '
509             . $fh_name
510             . ' returned an unrecoverable error' );
511              
512             }
513             else {
514              
515             if ( $logger->is_debug() ) {
516              
517             $logger->debug( 'Read '
518             . $data_ref->{$fh_bytes}
519             . ' bytes from '
520             . $fh_name );
521              
522             }
523              
524             if ( $data_ref->{$fh_bytes} == 0 ) {
525              
526             $logger->warn( 'got EOF from ' . fileno($fh) . '?' );
527             $select->remove($fh);
528             next;
529              
530             }
531              
532             unless ( ( $data_ref->{$fh_name} =~ $eol_regex )
533             or ( $data_ref->{$fh_name} =~ $prompt_regex ) )
534             {
535              
536             $logger->debug(
537             "Buffer data does not ends with CRLF or prompt, needs to read more from handle.\n"
538             . 'Buffer is ['
539             . $data_ref->{$fh_name}
540             . ']' );
541             next;
542              
543             }
544              
545             $self->normalize_eol( \$data_ref->{$fh_name} );
546              
547             if ( $fh == $self->get_read() ) {
548              
549             # :WORKAROUND:14/08/2013 18:40:46:: necessary to empty the stdout for possible (useless) information hanging in the buffer, but
550             # this information must be discarded since is from the previous processed command submitted
551             # :TODO :14/08/2013 18:41:43:: check why such information is not being recovered in the previous execution
552             $self->_process_stdout( \$data_ref->{$fh_name},
553             \@input_buffer, $logger, $condition )
554             unless ($ignore_output);
555              
556             $data_ref->{$fh_name} = undef;
557             $data_ref->{$fh_bytes} = 0;
558              
559             }
560             elsif ( $fh == $self->get_error() ) {
561              
562             $self->_process_stderr( \$data_ref->{$fh_name},
563             $logger );
564              
565             $data_ref->{$fh_name} = undef;
566             $data_ref->{$fh_bytes} = 0;
567              
568             }
569             else {
570             $logger->logdie(
571             'Somehow got a filehandle I dont know about!');
572             }
573             }
574              
575             } # end of foreach block
576              
577             } # end of while block
578              
579             $data_ref = undef;
580              
581             # below is the place for a Action object
582             if ( scalar(@input_buffer) >= 1 ) {
583              
584             # :TRICKY:5/1/2012 17:43:58:: copy params to avoid operations that erases the parameters due passing an array reference and messing with it
585             my @params;
586              
587             map { push( @params, $_ ) }
588             @{ $self->get_params_stack()->[ $condition->get_cmd_counter() ] };
589              
590             my $class =
591             $self->get_action_stack()->[ $condition->get_cmd_counter() ];
592              
593             if ( $logger->is_debug() ) {
594              
595             $logger->debug(
596             "Creating Siebel::Srvrmgr::Daemon::Action subclass $class instance"
597             );
598              
599             }
600              
601             my $action = Siebel::Srvrmgr::Daemon::ActionFactory->create(
602             $class,
603             {
604             parser => $parser,
605             params => \@params
606              
607             }
608             );
609              
610             # :TODO :16/08/2013 19:03:30:: move this log statement to Siebel::Srvrmgr::Daemon::Action
611             if ( $logger->is_debug() ) {
612              
613             $logger->debug('Lines from buffer sent for parsing');
614              
615             foreach my $line (@input_buffer) {
616              
617             $logger->debug($line);
618              
619             }
620              
621             $logger->debug('End of lines from buffer sent for parsing');
622              
623             }
624              
625             # :WORKAROUND:16/08/2013 18:54:51:: exceptions from validating output are not being seen
626             # :TODO :16/08/2013 18:55:18:: start using TryCatch to use exceptions for known problems
627             eval {
628              
629             $condition->set_output_used( $action->do( \@input_buffer ) );
630              
631             };
632              
633             $logger->logdie($@) if ($@);
634              
635             $logger->debug( 'Is output used? ' . $condition->is_output_used() )
636             if ( $logger->is_debug() );
637             @input_buffer = ();
638              
639             }
640             else {
641              
642             $logger->warn(
643             'The internal buffer is empty: check out if the read_timeout is not too low'
644             );
645              
646             }
647              
648             $logger->debug('Finished processing buffer')
649             if ( $logger->is_debug() );
650              
651             # :TODO:27/2/2012 17:43:42:: must deal with command stack when the loop is infinite (invoke reset method)
652              
653             # begin of session, sending command to the prompt
654             unless ( $condition->is_cmd_sent() or $condition->is_last_cmd() ) {
655              
656             $logger->debug('Preparing to execute command')
657             if ( $logger->is_debug() );
658              
659             $condition->add_cmd_counter()
660             if ( $condition->can_increment() );
661              
662             my $cmd = $self->get_cmd_stack()->[ $condition->get_cmd_counter() ];
663              
664             $self->_submit_cmd( $cmd, $logger );
665              
666             $ignore_output = 0;
667              
668             # srvrmgr.exe of Siebel 7.5.3.17 does not echo command printed to the input file handle
669             # this is necessary to give a hint to the parser about the command submitted
670              
671             if ( defined( $self->get_prompt() ) ) {
672              
673             push( @input_buffer, $self->get_prompt() . $cmd );
674             $self->_set_last_cmd( $self->get_prompt() . $cmd );
675              
676             }
677             else {
678              
679             $logger->logdie(
680             'prompt was not defined from read output, cannot continue');
681              
682             }
683              
684             $condition->set_output_used(0);
685             $condition->set_cmd_sent(1);
686              
687             }
688             else {
689              
690             if ( $logger->is_debug() ) {
691              
692             $logger->debug('Not yet read to execute a command');
693             $logger->debug(
694             'Condition max_cmd_idx = ' . $condition->max_cmd_idx() );
695             $logger->debug(
696             'Condition is_cmd_sent = ' . $condition->is_cmd_sent() );
697              
698             }
699              
700             }
701              
702             # :TODO :31/07/2013 16:43:15:: Condition class should have their own logger
703             # it is not possible to call check() twice because of the invocation of reduce_total_cmd() by check()
704             # if the Daemon has only one command, it will enter in a loop invoking srvrmgr everytime without doing
705             # nothing with it's output
706             $temp = $condition->check();
707              
708             $logger->info( 'Continue executing? ' . $temp )
709             if ( $logger->is_info() );
710              
711             } while ($temp);
712              
713             $self->_set_child_runs( $self->get_child_runs() + 1 );
714             $logger->debug( 'child_runs = ' . $self->get_child_runs() )
715             if ( $logger->is_debug() );
716             $logger->info('Exiting run sub');
717              
718             return 1;
719              
720             };
721              
722             sub _create_handle_buffer {
723              
724 17     17   35 my $self = shift;
725 17         22 my $select = shift; # IO::Select object
726 17         26 my $logger = shift; # Log::Log4perl object
727              
728             # to keep data from both handles while looping over them
729 17         19 my %data;
730              
731 17         568 foreach my $fh ( $self->get_read(), $self->get_error() ) {
732              
733 34         756 my $fh_name = fileno($fh);
734 34         64 my $fh_bytes = $fh_name . '_bytes';
735              
736 34         58 $data{$fh_name} = undef;
737 34         61 $data{$fh_bytes} = 0;
738 34         90 $select->add($fh);
739              
740             }
741              
742 17 50       344 if ( $logger->is_debug() ) {
743              
744 0 0       0 if ( openhandle( $self->get_read() ) ) {
745              
746 0         0 $logger->debug( 'fileno of child read handle = '
747             . fileno( $self->get_read() ) );
748              
749             }
750             else {
751              
752 0         0 $logger->debug('read_fh is not available');
753              
754             }
755              
756 0 0       0 if ( openhandle( $self->get_error() ) ) {
757              
758 0         0 $logger->debug( 'fileno of child error handle = '
759             . fileno( $self->get_error() ) )
760              
761             }
762             else {
763              
764 0         0 $logger->debug('error_fh is not available');
765              
766             }
767 0         0 $logger->debug( 'Setting '
768             . $self->get_read_timeout()
769             . ' seconds for read srvrmgr output time out' );
770              
771 0         0 my $assert = 'Input record separator is ';
772              
773             SWITCH: {
774              
775 0 0       0 if ( $/ eq \015 ) {
  0         0  
776 0         0 $logger->debug( $assert . 'CR' );
777 0         0 last SWITCH;
778             }
779 0 0       0 if ( $/ eq ( \015 . \012 ) ) {
780 0         0 $logger->debug( $assert . 'CRLF' );
781 0         0 last SWITCH;
782             }
783 0 0       0 if ( $/ eq \012 ) {
784 0         0 $logger->debug( $assert . 'LF' );
785 0         0 last SWITCH;
786             }
787             else {
788 0         0 $logger->debug("Unknown input record separator: [$/]");
789             }
790              
791             }
792              
793             }
794              
795 17         129 return \%data;
796              
797             }
798              
799             sub _create_child {
800              
801 2     2   3 my $self = shift;
802              
803 2         14 my $logger = Siebel::Srvrmgr->gimme_logger( ref($self) );
804 2         250 weaken($logger);
805              
806 2 50       76 if ( $self->get_retries() >= $self->get_max_retries() ) {
807              
808 0         0 $logger->fatal( 'Maximum retries to spawn srvrmgr reached: '
809             . $self->get_max_retries() );
810 0         0 $logger->warn(
811             'Application will exit with an error return code. Please review log for errors'
812             );
813 0         0 exit(1);
814              
815             }
816              
817 2 50       54 $logger->logdie( 'Cannot find program ' . $self->get_bin() . ' to execute' )
818             unless ( -e $self->get_bin() );
819              
820 2         17 my $params_ref = $self->_define_params();
821              
822 2         14 my ( $pid, $write_h, $read_h, $error_h ) = safe_open3($params_ref);
823 2         8693 $self->_set_pid($pid);
824 2         86 $self->_set_write($write_h);
825 2         64 $self->_set_read($read_h);
826 2         61 $self->_set_error($error_h);
827              
828 2 50       20 if ( $logger->is_debug() ) {
829              
830 0         0 $logger->debug( 'Forked srvrmgr with the following parameters: '
831 0         0 . join( ' ', @{$params_ref} ) );
832 0         0 $logger->debug( 'child PID is ' . $pid );
833 0         0 $logger->debug( 'IPC buffer size is ' . $self->get_buffer_size() );
834              
835             }
836              
837 2         41 $logger->info('Started srvrmgr');
838              
839 2 50       35 unless ( $self->_check_child($logger) ) {
840              
841 0         0 return 0;
842              
843             }
844             else {
845              
846 2         89 $self->_set_child_runs(0);
847 2         31 return 1;
848              
849             }
850              
851             }
852              
853             sub _process_stderr {
854              
855 2 50   2   5 exit if ($SIG_INT);
856 2         4 my $self = shift;
857 2         2 my $data_ref = shift;
858 2         3 my $logger = shift;
859 2         6 weaken($logger);
860              
861 2 50       5 if ( defined($$data_ref) ) {
862              
863 2         9 foreach my $line ( split( "\n", $$data_ref ) ) {
864              
865 2 50       5 exit if ($SIG_INT);
866              
867 2         7 $self->_check_error( $line, $logger );
868              
869             }
870              
871             }
872             else {
873              
874 0         0 $logger->warn('Received empty buffer to read');
875              
876             }
877              
878             }
879              
880             sub _process_stdout {
881              
882             # :TODO :07/08/2013 15:12:17:: should this be controlled in instances? or should it be global to the class?
883 22 50 33 22   125 exit if ( $SIG_INT or $SIG_PIPE );
884              
885 22         74 my $self = shift;
886 22         33 my $data_ref = shift;
887 22         41 my $buffer_ref = shift;
888 22         40 my $logger = shift;
889 22         31 my $condition = shift;
890              
891 22         81 weaken($logger);
892              
893             # :TODO :09/08/2013 19:35:30:: review and remove assigning the compiled regexes to scalar (probably unecessary)
894 22         147 my $prompt_regex = SRVRMGR_PROMPT;
895 22         99 my $load_pref_regex = LOAD_PREF_RESP;
896              
897 22 50       79 $logger->debug("Raw content is [$$data_ref]") if $logger->is_debug();
898              
899 22         968 foreach my $line ( split( "\n", $$data_ref ) ) {
900              
901 1089 50 33     3019 exit if ( $SIG_INT or $SIG_PIPE );
902              
903 1089 50       2505 if ( $logger->is_debug() ) {
904              
905 0 0       0 if ( defined($line) ) {
906              
907 0         0 $logger->debug("Recovered line [$line]");
908              
909             }
910             else {
911              
912 0         0 $logger->debug("Recovered line with undefined content");
913              
914             }
915              
916             }
917              
918 1089         6241 $self->_check_error( $line, $logger );
919              
920             SWITCH: {
921              
922             # :TRICKY:29/06/2011 21:23:11:: bufferization in srvrmgr.exe ruins the day: the prompt will never come out unless a little push is given
923             # :TODO :03/09/2013 12:11:27:: check if a print with an empty line is not required here
924 1089 100       1458 if ( $line =~ ROWS_RETURNED ) {
  1089         2896  
925              
926             # parsers will consider the lines below
927 17         28 push( @{$buffer_ref}, $line );
  17         42  
928 17         66 last SWITCH;
929              
930             }
931              
932             # prompt was returned, end of output
933             # first execution should bring only informations about Siebel
934 1072 100       3200 if ( $line =~ /$prompt_regex/ ) {
935              
936 22 100       1319 unless ( defined( $self->get_prompt() ) ) {
  20 50       76  
937              
938 2         81 $self->_set_prompt($line);
939              
940 2 50       18 $logger->info("defined prompt with [$line]")
941             if ( $logger->is_info() );
942              
943             # if prompt was undefined, that means that this is might be rest of output of previous command
944             # and thus can be safely ignored
945 2 50       24 if ( @{$buffer_ref} ) {
  2         10  
946              
947 2 50       8 if ( $buffer_ref->[0] eq '' ) {
948              
949 0         0 $logger->debug("Ignoring output [$line]");
950              
951 0         0 $condition->set_cmd_sent(0);
952 0         0 @{$buffer_ref} = ();
  0         0  
953              
954             }
955              
956             }
957              
958             }
959             elsif ( scalar( @{$buffer_ref} ) < 1 ) { # no command submitted
960              
961 0         0 $condition->set_cmd_sent(0);
962              
963             }
964             else {
965              
966 20 50 33     34 unless (( scalar( @{$buffer_ref} ) >= 1 )
  20   33     871  
967             and ( $buffer_ref->[0] eq $self->get_last_cmd() )
968             and $condition->is_cmd_sent() )
969             {
970              
971 0         0 $condition->set_cmd_sent(0);
972              
973             }
974              
975             }
976              
977 22         48 push( @{$buffer_ref}, $line );
  22         43  
978              
979 22         123 last SWITCH;
980              
981             }
982              
983             # no prompt detection, keep reading output from srvrmgr
984 1050         905 else { push( @{$buffer_ref}, $line ); }
  1050         2330  
985              
986             }
987              
988             }
989              
990             }
991              
992             sub _check_child {
993              
994 2     2   8 my $self = shift;
995 2         6 my $logger = shift;
996 2         12 weaken($logger);
997              
998 2 50       76 if ( $self->has_pid() ) {
999              
1000             # :WORKAROUND:19/4/2012 19:38:04:: somehow the child process of srvrmgr has to be waited for one second and receive one kill 0 signal before
1001             # it dies when something goes wrong
1002 2         65 kill 0, $self->get_pid();
1003              
1004 2 50       57 unless ( kill 0, $self->get_pid() ) {
1005              
1006 0         0 $logger->fatal( $self->get_bin()
1007             . " process returned a fatal error: ${^CHILD_ERROR_NATIVE}" );
1008              
1009 0         0 $logger->fatal( $? . ' child exit status = ' . ( $? >> 8 ) );
1010              
1011 0         0 $self->close_child($logger);
1012              
1013 0         0 return 0;
1014              
1015             }
1016             else {
1017              
1018 2         11 return 1;
1019              
1020             }
1021              
1022             # try to read immediatly from stderr if possible
1023 0 0       0 if ( openhandle( $self->get_error() ) ) {
1024              
1025 0         0 my $error;
1026              
1027 0         0 my $select = IO::Select->new();
1028 0         0 $select->add( $self->get_error() );
1029              
1030 0         0 while ( my $fh = $select->can_read( $self->get_read_timeout() ) ) {
1031              
1032 0         0 my $buffer;
1033 0         0 my $read = sysread( $fh, $buffer, $self->get_buffer_size() );
1034              
1035 0 0       0 if ( defined($read) ) {
1036              
1037 0 0       0 if ( $read > 0 ) {
1038              
1039 0         0 $error .= $buffer;
1040 0         0 next;
1041              
1042             }
1043             else {
1044              
1045 0 0       0 $logger->debug(
1046             'Reached EOF while trying to get error messages')
1047             if ( $logger->is_debug() );
1048              
1049             }
1050              
1051             }
1052             else {
1053              
1054 0         0 $logger->warn(
1055             'Could not sysread the STDERR from srvrmgr process: '
1056             . $! );
1057 0         0 last;
1058              
1059             }
1060              
1061             } # end of while block
1062              
1063 0 0       0 $self->_process_stderr( \$error, $logger ) if ( defined($error) );
1064              
1065             }
1066             else {
1067              
1068 0         0 $logger->fatal('Error pipe from child is closed');
1069              
1070             }
1071              
1072 0 0       0 $logger->fatal('Read pipe from child is closed')
1073             unless ( openhandle( $self->get_read() ) );
1074 0 0       0 $logger->fatal('Write pipe from child is closed')
1075             unless ( openhandle( $self->get_write() ) );
1076              
1077             } # end of if has_pid
1078             else {
1079              
1080 0         0 return 0;
1081              
1082             }
1083              
1084             }
1085              
1086             sub _my_cleanup {
1087              
1088 8     8   11 my $self = shift;
1089 8         11 my $logger = shift();
1090 8         36 weaken($logger);
1091              
1092 8 100 66     360 if ( $self->has_pid() and ( $self->get_pid() =~ /\d+/ ) ) {
1093              
1094 1         5 $self->close_child($logger);
1095              
1096             }
1097             else {
1098              
1099 7 50       23 if ( $logger->is_info() ) {
1100              
1101 0         0 $logger->info('No child process to terminate');
1102              
1103             }
1104              
1105             }
1106              
1107 8         58 return 1;
1108              
1109             }
1110              
1111             sub _submit_cmd {
1112              
1113 22     22   46 my $self = shift;
1114 22         41 my $cmd = shift;
1115 22         39 my $logger = shift;
1116 22         39 my $has_logger = 0;
1117              
1118 22 50 33     152 if ( ( defined($logger) ) and ( ref($logger) ) ) {
1119              
1120 22         96 weaken($logger);
1121 22         33 $has_logger = 1;
1122              
1123             }
1124              
1125 22         844 my $bytes = syswrite $self->get_write(), "$cmd\n";
1126              
1127 22 50       89 if ( defined($bytes) ) {
1128              
1129 22 50 33     145 if ( $has_logger && $logger->is_debug() ) {
1130              
1131 0         0 $logger->debug("Submitted $cmd, wrote $bytes bytes");
1132              
1133             }
1134              
1135             }
1136             else {
1137              
1138 0 0       0 if ($has_logger) {
1139              
1140 0         0 $logger->logdie( 'A failure occurred when trying to submit '
1141             . $cmd . ': '
1142             . $! );
1143              
1144             }
1145             else {
1146              
1147 0         0 die( 'A failure occurred when trying to submit '
1148             . $cmd . ': '
1149             . $! );
1150              
1151             }
1152              
1153             }
1154              
1155 22         187 return 1;
1156              
1157             }
1158              
1159             =pod
1160              
1161             =head2 close_child
1162              
1163             Finishes the child process associated with the execution of srvrmgr program, if the child's PID is available. Besides, this automatically calls C<clear_pid>.
1164              
1165             First this methods tries to submit the C<exit> command to srvrmgr, hoping to terminate the connection with the Siebel Enterprise. After that, the
1166             handles associated with the child will be closed. If after that the PID is still running, the method will call C<waitpid> in non-blocking mode.
1167              
1168             For MS Windows OS, this might not be sufficient: the PID will be checked again after C<waitpid>, and if it is still running, this method will try to use
1169             C<kill 9> to eliminate the process.
1170              
1171             If the child process is terminated succesfully, this method returns true. If there is no PID associated with the Daemon instance, this method will return false.
1172              
1173             Accepts as an optional parameter an instance of a L<Log::Log4perl> for logging messages.
1174              
1175             =cut
1176              
1177             sub close_child {
1178              
1179 3     3 1 30 my $self = shift;
1180 3         7 my $logger = shift;
1181              
1182 3         5 my $has_logger = 0;
1183              
1184 3 50 33     26 if ( ( defined($logger) ) and ( ref($logger) ) ) {
1185              
1186 3         11 weaken($logger);
1187 3         5 $has_logger = 1;
1188              
1189             }
1190              
1191 3 100       118 if ( $self->has_pid() ) {
1192              
1193 2 50 33     14 if ( $has_logger && $logger->is_warn() ) {
1194              
1195 2         67 $logger->warn( 'Trying to close child PID ' . $self->get_pid() );
1196              
1197             }
1198              
1199 2 50 33     850 if ( ( openhandle( $self->get_write() ) )
      33        
1200             and ( not($SIG_PIPE) )
1201             and ( not($SIG_ALARM) ) )
1202             {
1203              
1204 2         10 $self->_submit_cmd( 'exit', $logger );
1205              
1206 2 50 33     12 if ( $has_logger && $logger->is_debug() ) {
1207              
1208 0         0 $logger->debug('Submitted exit command to srvrmgr');
1209              
1210             }
1211              
1212             }
1213             else {
1214              
1215 0 0       0 $logger->warn('write_fh is already closed') if ($has_logger);
1216              
1217             }
1218              
1219 2         24 for ( 1 .. 4 ) {
1220              
1221 8         8001228 sleep 1;
1222              
1223 8 50       933 if ( kill( 0, $self->get_pid() ) ) {
1224              
1225 8         60 $logger->debug('child process is still there');
1226             }
1227             else {
1228              
1229 0         0 last;
1230              
1231             }
1232              
1233             }
1234              
1235 2 50       110 if ( kill 0, $self->get_pid() ) {
1236              
1237 2 50 33     34 if ( $has_logger && $logger->is_debug() ) {
1238              
1239 0         0 $logger->debug(
1240             'srvrmgr is still running, trying waitpid on it');
1241              
1242             }
1243              
1244 2         87 my $ret = waitpid( $self->get_pid(), 0 );
1245              
1246             SWITCH: {
1247              
1248 2 50       7 if ( $ret == $self->get_pid() ) {
  2         67  
1249              
1250             # :WORKAROUND:14/08/2013 17:44:00:: for Windows, not using shutdown when creating the socketpair causes the application to not
1251             # exit with waitpid. using waitpid without non-blocking mode just blocks the application to finish
1252 2 50       37 if ( $Config{osname} eq 'MSWin32' ) {
1253              
1254 0 0       0 if ( kill 0, $self->get_pid() ) {
1255              
1256 0 0       0 $logger->warn(
1257             'child is still running even after waitpid: last attempt with "kill 9"'
1258             ) if ($has_logger);
1259              
1260 0         0 kill 9, $self->get_pid();
1261              
1262             }
1263              
1264             }
1265              
1266 2 50 33     19 $logger->info('Child process finished successfully')
1267             if ( $has_logger && $logger->is_info() );
1268              
1269 2         23 last SWITCH;
1270              
1271             }
1272              
1273 0 0       0 if ( $ret == -1 ) {
1274              
1275 0 0 0     0 $logger->info(
1276             'No such PID ' . $self->get_pid() . ' to kill' )
1277             if ( $has_logger && $logger->is_info() );
1278 0         0 last SWITCH;
1279              
1280             }
1281             else {
1282              
1283 0 0 0     0 if ( $has_logger && $logger->is_warn() ) {
1284              
1285 0         0 $logger->warn('Could not kill the child process');
1286 0         0 $logger->warn( 'Child status = ' . $? );
1287 0         0 $logger->warn(
1288             'Child error = ' . ${^CHILD_ERROR_NATIVE} );
1289              
1290             }
1291              
1292             }
1293              
1294             }
1295              
1296             }
1297             else {
1298              
1299 0 0 0     0 $logger->warn('Child process is already gone')
1300             if ( $has_logger && $logger->is_warn() );
1301              
1302             }
1303              
1304 2         65 $self->clear_pid();
1305 2         13 return 1;
1306              
1307             }
1308             else {
1309              
1310 1 50 33     10 $logger->info('Has no child PID available to terminate')
1311             if ( $has_logger && $logger->is_info() );
1312 1         12 return 0;
1313              
1314             }
1315              
1316             }
1317              
1318             =pod
1319              
1320             =head1 CAVEATS
1321              
1322             This class is still considered experimental and should be used with care. Tests with MS Windows (and the nature of doing IPC within the plataform) makes it difficult do use this class in Microsoft OS's.
1323              
1324             The C<srvrmgr> program uses buffering, which makes difficult to read the generated output as expected.
1325              
1326             =head1 SEE ALSO
1327              
1328             =over
1329              
1330             =item *
1331              
1332             L<IPC::Open3>
1333              
1334             =item *
1335              
1336             L<Moose>
1337              
1338             =item *
1339              
1340             L<Siebel::Srvrmgr::Daemon::Condition>
1341              
1342             =item *
1343              
1344             L<Siebel::Srvrmgr::Daemon::Command>
1345              
1346             =item *
1347              
1348             L<Siebel::Srvrmgr::Daemon::ActionFactory>
1349              
1350             =item *
1351              
1352             L<Siebel::Srvrmgr::Regexes>
1353              
1354             =item *
1355              
1356             L<POSIX>
1357              
1358             =item *
1359              
1360             L<Siebel::Srvrmgr::Daemon::Command>
1361              
1362             =item *
1363              
1364             L<Siebel::Srvrmgr::Daemon::IPC>
1365              
1366             =item *
1367              
1368             L<https://github.com/lucastheisen/ipc-open3-callback>
1369              
1370             =back
1371              
1372             =head1 AUTHOR
1373              
1374             Alceu Rodrigues de Freitas Junior, E<lt>arfreitas@cpan.orgE<gt>.
1375              
1376             =head1 COPYRIGHT AND LICENSE
1377              
1378             This software is copyright (c) 2012 of Alceu Rodrigues de Freitas Junior, E<lt>arfreitas@cpan.orgE<gt>
1379              
1380             This file is part of Siebel Monitoring Tools.
1381              
1382             Siebel Monitoring Tools is free software: you can redistribute it and/or modify
1383             it under the terms of the GNU General Public License as published by
1384             the Free Software Foundation, either version 3 of the License, or
1385             (at your option) any later version.
1386              
1387             Siebel Monitoring Tools is distributed in the hope that it will be useful,
1388             but WITHOUT ANY WARRANTY; without even the implied warranty of
1389             MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1390             GNU General Public License for more details.
1391              
1392             You should have received a copy of the GNU General Public License
1393             along with Siebel Monitoring Tools. If not, see L<http://www.gnu.org/licenses/>.
1394              
1395             =cut
1396              
1397             __PACKAGE__->meta->make_immutable;
1398              
1399             1;
1400