File Coverage

blib/lib/Siebel/Srvrmgr/Daemon/Heavy.pm
Criterion Covered Total %
statement 167 233 71.6
branch 45 106 42.4
condition 10 27 37.0
subroutine 25 25 100.0
pod 2 2 100.0
total 249 393 63.3


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