| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | package Proc::Launcher::Manager; | 
| 2 | 3 |  |  | 3 |  | 1758 | use strict; | 
|  | 3 |  |  |  |  | 6 |  | 
|  | 3 |  |  |  |  | 82 |  | 
| 3 | 3 |  |  | 3 |  | 15 | use warnings; | 
|  | 3 |  |  |  |  | 4 |  | 
|  | 3 |  |  |  |  | 115 |  | 
| 4 |  |  |  |  |  |  |  | 
| 5 |  |  |  |  |  |  | our $VERSION = '0.0.35'; # VERSION | 
| 6 |  |  |  |  |  |  |  | 
| 7 | 3 |  |  | 3 |  | 2615 | use Mouse; | 
|  | 3 |  |  |  |  | 105136 |  | 
|  | 3 |  |  |  |  | 17 |  | 
| 8 |  |  |  |  |  |  |  | 
| 9 | 3 |  |  | 3 |  | 1017 | use Carp; | 
|  | 3 |  |  |  |  | 6 |  | 
|  | 3 |  |  |  |  | 274 |  | 
| 10 | 3 |  |  | 3 |  | 17 | use File::Path; | 
|  | 3 |  |  |  |  | 5 |  | 
|  | 3 |  |  |  |  | 186 |  | 
| 11 | 3 |  |  | 3 |  | 2856 | use POSIX qw(:sys_wait_h); | 
|  | 3 |  |  |  |  | 23193 |  | 
|  | 3 |  |  |  |  | 20 |  | 
| 12 |  |  |  |  |  |  |  | 
| 13 | 3 |  |  | 3 |  | 6228 | use Proc::Launcher; | 
|  | 3 |  |  |  |  | 10 |  | 
|  | 3 |  |  |  |  | 112 |  | 
| 14 | 3 |  |  | 3 |  | 1894 | use Proc::Launcher::Supervisor; | 
|  | 3 |  |  |  |  | 6 |  | 
|  | 3 |  |  |  |  | 4031 |  | 
| 15 |  |  |  |  |  |  |  | 
| 16 |  |  |  |  |  |  | =head1 NAME | 
| 17 |  |  |  |  |  |  |  | 
| 18 |  |  |  |  |  |  | Proc::Launcher::Manager - manage multiple Proc::Launcher objects | 
| 19 |  |  |  |  |  |  |  | 
| 20 |  |  |  |  |  |  | =head1 VERSION | 
| 21 |  |  |  |  |  |  |  | 
| 22 |  |  |  |  |  |  | version 0.0.35 | 
| 23 |  |  |  |  |  |  |  | 
| 24 |  |  |  |  |  |  | =head1 SYNOPSIS | 
| 25 |  |  |  |  |  |  |  | 
| 26 |  |  |  |  |  |  | use Proc::Launcher::Manager; | 
| 27 |  |  |  |  |  |  |  | 
| 28 |  |  |  |  |  |  | my $shared_config = { x => 1, y => 2 }; | 
| 29 |  |  |  |  |  |  |  | 
| 30 |  |  |  |  |  |  | my $monitor = Proc::Launcher::Manager->new( app_name  => 'MyApp' ); | 
| 31 |  |  |  |  |  |  |  | 
| 32 |  |  |  |  |  |  | # a couple of different components | 
| 33 |  |  |  |  |  |  | $monitor->register( daemon_name  => 'component1', | 
| 34 |  |  |  |  |  |  | start_method => sub { MyApp->start_component1( $config ) } | 
| 35 |  |  |  |  |  |  | ); | 
| 36 |  |  |  |  |  |  | $monitor->register( daemon_name  => 'component2', | 
| 37 |  |  |  |  |  |  | start_method => sub { MyApp->start_component2( $config ) } | 
| 38 |  |  |  |  |  |  | ); | 
| 39 |  |  |  |  |  |  |  | 
| 40 |  |  |  |  |  |  | # using class/method/context rather than a code ref | 
| 41 |  |  |  |  |  |  | $monitor->register( daemon_name  => 'webui', | 
| 42 |  |  |  |  |  |  | class        => 'MyApp::WebUI', | 
| 43 |  |  |  |  |  |  | start_method => 'start_webui', | 
| 44 |  |  |  |  |  |  | context      => $config, | 
| 45 |  |  |  |  |  |  | ); | 
| 46 |  |  |  |  |  |  |  | 
| 47 |  |  |  |  |  |  | # start all registered daemons.  processes that are already | 
| 48 |  |  |  |  |  |  | # running won't be restarted. | 
| 49 |  |  |  |  |  |  | $monitor->start(); | 
| 50 |  |  |  |  |  |  |  | 
| 51 |  |  |  |  |  |  | # stop all daemons | 
| 52 |  |  |  |  |  |  | $monitor->stop(); | 
| 53 |  |  |  |  |  |  | sleep 1; | 
| 54 |  |  |  |  |  |  | $monitor->force_stop(); | 
| 55 |  |  |  |  |  |  |  | 
| 56 |  |  |  |  |  |  | # display all processes stdout/stderr from the log file that's | 
| 57 |  |  |  |  |  |  | # been generated since we started | 
| 58 |  |  |  |  |  |  | $monitor->read_log( sub { print "$_[0]\n" } ); | 
| 59 |  |  |  |  |  |  |  | 
| 60 |  |  |  |  |  |  | # get a specific daemon or perform actions on one | 
| 61 |  |  |  |  |  |  | my $webui = $monitor->daemon('webui'); | 
| 62 |  |  |  |  |  |  | $monitor->daemon('webui')->start(); | 
| 63 |  |  |  |  |  |  |  | 
| 64 |  |  |  |  |  |  | # start the process supervisor.  this will start up an event loop | 
| 65 |  |  |  |  |  |  | # and won't exit until it is killed.  any processes not already | 
| 66 |  |  |  |  |  |  | # running will be started.  all processes will be monitored and | 
| 67 |  |  |  |  |  |  | # automatically restarted if they exit. | 
| 68 |  |  |  |  |  |  | $monitor->supervisor->start(); | 
| 69 |  |  |  |  |  |  |  | 
| 70 |  |  |  |  |  |  | # shut down/restart the supervisor | 
| 71 |  |  |  |  |  |  | $monitor->supervisor->stop(); | 
| 72 |  |  |  |  |  |  | $monitor->supervisor->force_stop(); | 
| 73 |  |  |  |  |  |  | $monitor->supervisor->restart(); | 
| 74 |  |  |  |  |  |  |  | 
| 75 |  |  |  |  |  |  |  | 
| 76 |  |  |  |  |  |  | =head1 DESCRIPTION | 
| 77 |  |  |  |  |  |  |  | 
| 78 |  |  |  |  |  |  | This library makes it easier to deal with multiple L | 
| 79 |  |  |  |  |  |  | processes by providing methods to start and stop all daemons with a | 
| 80 |  |  |  |  |  |  | single command.  Please see the documentation in L to | 
| 81 |  |  |  |  |  |  | understand how this these libraries differ from other similar forking | 
| 82 |  |  |  |  |  |  | and controller modules. | 
| 83 |  |  |  |  |  |  |  | 
| 84 |  |  |  |  |  |  | It also provides a supervisor() method which will fork a daemon that | 
| 85 |  |  |  |  |  |  | will monitor the other daemons at regular intervals and restart any | 
| 86 |  |  |  |  |  |  | that have stopped.  Note that only one supervisor can be running at | 
| 87 |  |  |  |  |  |  | any given time for each pid_dir. | 
| 88 |  |  |  |  |  |  |  | 
| 89 |  |  |  |  |  |  | There is no tracking of inter-service dependencies nor predictable | 
| 90 |  |  |  |  |  |  | ordering of service startup.  Instead, daemons should be designed to | 
| 91 |  |  |  |  |  |  | wait for needed resources.  See L if you need to | 
| 92 |  |  |  |  |  |  | handle dependencies. | 
| 93 |  |  |  |  |  |  |  | 
| 94 |  |  |  |  |  |  | =cut | 
| 95 |  |  |  |  |  |  |  | 
| 96 |  |  |  |  |  |  | #_* Roles | 
| 97 |  |  |  |  |  |  |  | 
| 98 |  |  |  |  |  |  | with 'Proc::Launcher::Roles::Launchable'; | 
| 99 |  |  |  |  |  |  |  | 
| 100 |  |  |  |  |  |  | #_* Attributes | 
| 101 |  |  |  |  |  |  |  | 
| 102 |  |  |  |  |  |  | has 'debug'      => ( is         => 'ro', | 
| 103 |  |  |  |  |  |  | isa        => 'Bool', | 
| 104 |  |  |  |  |  |  | default    => 0, | 
| 105 |  |  |  |  |  |  | ); | 
| 106 |  |  |  |  |  |  |  | 
| 107 |  |  |  |  |  |  | has 'pid_dir'    => ( is         => 'ro', | 
| 108 |  |  |  |  |  |  | isa        => 'Str', | 
| 109 |  |  |  |  |  |  | lazy       => 1, | 
| 110 |  |  |  |  |  |  | default    => sub { | 
| 111 |  |  |  |  |  |  | my $dir = join "/", $ENV{HOME}, "logs"; | 
| 112 |  |  |  |  |  |  | unless ( -d $dir ) {  mkpath( $dir ); } | 
| 113 |  |  |  |  |  |  | return $dir; | 
| 114 |  |  |  |  |  |  | }, | 
| 115 |  |  |  |  |  |  | ); | 
| 116 |  |  |  |  |  |  |  | 
| 117 |  |  |  |  |  |  | has 'launchers'  => ( is         => 'rw', | 
| 118 |  |  |  |  |  |  | isa        => 'HashRef[Proc::Launcher]', | 
| 119 |  |  |  |  |  |  | ); | 
| 120 |  |  |  |  |  |  |  | 
| 121 |  |  |  |  |  |  |  | 
| 122 |  |  |  |  |  |  | has 'supervisor' => ( is         => 'rw', | 
| 123 |  |  |  |  |  |  | lazy       => 1, | 
| 124 |  |  |  |  |  |  | default    => sub { | 
| 125 |  |  |  |  |  |  | my $self = shift; | 
| 126 |  |  |  |  |  |  |  | 
| 127 |  |  |  |  |  |  | return Proc::Launcher->new( | 
| 128 |  |  |  |  |  |  | daemon_name  => 'supervisor', | 
| 129 |  |  |  |  |  |  | pid_dir      => $self->pid_dir, | 
| 130 |  |  |  |  |  |  | start_method => sub { | 
| 131 |  |  |  |  |  |  | Proc::Launcher::Supervisor->new( manager => $self )->monitor(); | 
| 132 |  |  |  |  |  |  | }, | 
| 133 |  |  |  |  |  |  | ); | 
| 134 |  |  |  |  |  |  | }, | 
| 135 |  |  |  |  |  |  | ); | 
| 136 |  |  |  |  |  |  |  | 
| 137 |  |  |  |  |  |  |  | 
| 138 |  |  |  |  |  |  | #_* Methods | 
| 139 |  |  |  |  |  |  |  | 
| 140 |  |  |  |  |  |  | =head1 METHODS | 
| 141 |  |  |  |  |  |  |  | 
| 142 |  |  |  |  |  |  | =over 8 | 
| 143 |  |  |  |  |  |  |  | 
| 144 |  |  |  |  |  |  | =item register( %options ) | 
| 145 |  |  |  |  |  |  |  | 
| 146 |  |  |  |  |  |  | Create a new L object with the specified options.  If | 
| 147 |  |  |  |  |  |  | the specified daemon already exists, no changes will be made. | 
| 148 |  |  |  |  |  |  |  | 
| 149 |  |  |  |  |  |  | =cut | 
| 150 |  |  |  |  |  |  |  | 
| 151 |  |  |  |  |  |  | sub register { | 
| 152 | 4 |  |  | 4 | 1 | 4341 | my ( $self, %options ) = @_; | 
| 153 |  |  |  |  |  |  |  | 
| 154 | 4 |  |  |  |  | 26 | $options{pid_dir} = $self->pid_dir; | 
| 155 |  |  |  |  |  |  |  | 
| 156 | 4 |  |  |  |  | 11 | my $daemon; | 
| 157 | 4 | 50 |  |  |  | 33 | unless ( $self->{daemons}->{ $options{daemon_name} } ) { | 
| 158 | 4 |  |  |  |  | 109 | $daemon = Proc::Launcher->new( %options ); | 
| 159 |  |  |  |  |  |  | } | 
| 160 |  |  |  |  |  |  |  | 
| 161 |  |  |  |  |  |  | # just capturing the position of the tail end of the log file | 
| 162 | 4 |  |  |  |  | 34 | $self->read_log( undef, $daemon ); | 
| 163 |  |  |  |  |  |  |  | 
| 164 | 4 |  |  |  |  | 389 | $self->{daemons}->{ $options{daemon_name} } = $daemon; | 
| 165 |  |  |  |  |  |  | } | 
| 166 |  |  |  |  |  |  |  | 
| 167 |  |  |  |  |  |  | =item daemon( 'daemon_name' ) | 
| 168 |  |  |  |  |  |  |  | 
| 169 |  |  |  |  |  |  | Return the L object for a specified daemon. | 
| 170 |  |  |  |  |  |  |  | 
| 171 |  |  |  |  |  |  | =cut | 
| 172 |  |  |  |  |  |  |  | 
| 173 |  |  |  |  |  |  | sub daemon { | 
| 174 | 25 |  |  | 25 | 1 | 53 | my ( $self, $daemon_name ) = @_; | 
| 175 |  |  |  |  |  |  |  | 
| 176 | 25 |  |  |  |  | 215 | return $self->{daemons}->{ $daemon_name }; | 
| 177 |  |  |  |  |  |  | } | 
| 178 |  |  |  |  |  |  |  | 
| 179 |  |  |  |  |  |  |  | 
| 180 |  |  |  |  |  |  | =item daemons() | 
| 181 |  |  |  |  |  |  |  | 
| 182 |  |  |  |  |  |  | Return a list of L objects. | 
| 183 |  |  |  |  |  |  |  | 
| 184 |  |  |  |  |  |  | =cut | 
| 185 |  |  |  |  |  |  |  | 
| 186 |  |  |  |  |  |  | sub daemons { | 
| 187 | 12 |  |  | 12 | 1 | 29 | my ( $self ) = @_; | 
| 188 |  |  |  |  |  |  |  | 
| 189 | 12 |  |  |  |  | 27 | my @daemons; | 
| 190 |  |  |  |  |  |  |  | 
| 191 | 12 |  |  |  |  | 144 | for my $daemon_name ( $self->daemons_names() ) { | 
| 192 | 25 |  |  |  |  | 88 | push @daemons, $self->{daemons}->{ $daemon_name }; | 
| 193 |  |  |  |  |  |  | } | 
| 194 |  |  |  |  |  |  |  | 
| 195 | 12 |  |  |  |  | 87 | return @daemons; | 
| 196 |  |  |  |  |  |  | } | 
| 197 |  |  |  |  |  |  |  | 
| 198 |  |  |  |  |  |  | =item daemons_names() | 
| 199 |  |  |  |  |  |  |  | 
| 200 |  |  |  |  |  |  | Return a list of the names of all registered daemons. | 
| 201 |  |  |  |  |  |  |  | 
| 202 |  |  |  |  |  |  | =cut | 
| 203 |  |  |  |  |  |  |  | 
| 204 |  |  |  |  |  |  | sub daemons_names { | 
| 205 | 19 |  |  | 19 | 1 | 1093 | my ( $self ) = @_; | 
| 206 |  |  |  |  |  |  |  | 
| 207 | 19 |  |  |  |  | 39 | return ( sort keys %{ $self->{daemons} } ); | 
|  | 19 |  |  |  |  | 408 |  | 
| 208 |  |  |  |  |  |  | } | 
| 209 |  |  |  |  |  |  |  | 
| 210 |  |  |  |  |  |  | =item is_running() | 
| 211 |  |  |  |  |  |  |  | 
| 212 |  |  |  |  |  |  | Return a list of the names of daemons that are currently running. | 
| 213 |  |  |  |  |  |  |  | 
| 214 |  |  |  |  |  |  | This will begin by calling rm_zombies() on one of daemon objects, | 
| 215 |  |  |  |  |  |  | sleeping a second, and then calling rm_zombies() again.  So far the | 
| 216 |  |  |  |  |  |  | test cases have always passed when using this strategy, and | 
| 217 |  |  |  |  |  |  | inconsistently passed with any subset thereof. | 
| 218 |  |  |  |  |  |  |  | 
| 219 |  |  |  |  |  |  | While it seems that this is more complicated than shutting down a | 
| 220 |  |  |  |  |  |  | single process, it's really just trying to be a bit more efficient. | 
| 221 |  |  |  |  |  |  | When managing a single process, is_running might be called a few times | 
| 222 |  |  |  |  |  |  | until the process exits.  Since we might be managing a lot of daemons, | 
| 223 |  |  |  |  |  |  | this method is likely to be a bit more efficient and will hopefully | 
| 224 |  |  |  |  |  |  | only need to be called once (after the daemons have been given | 
| 225 |  |  |  |  |  |  | necessary time to shut down). | 
| 226 |  |  |  |  |  |  |  | 
| 227 |  |  |  |  |  |  | This may be reworked a bit in the future since calling sleep will halt | 
| 228 |  |  |  |  |  |  | all processes in single-threaded cooperative multitasking frameworks. | 
| 229 |  |  |  |  |  |  |  | 
| 230 |  |  |  |  |  |  | =cut | 
| 231 |  |  |  |  |  |  |  | 
| 232 |  |  |  |  |  |  | sub is_running { | 
| 233 | 6 |  |  | 6 | 1 | 858 | my ( $self ) = @_; | 
| 234 |  |  |  |  |  |  |  | 
| 235 | 6 |  |  |  |  | 59 | my @daemon_names = $self->daemons_names(); | 
| 236 |  |  |  |  |  |  |  | 
| 237 | 6 | 100 |  |  |  | 47 | unless ( scalar @daemon_names ) { | 
| 238 | 1 |  |  |  |  | 439 | print "is_running() called when no daemons registered\n"; | 
| 239 | 1 |  |  |  |  | 10 | return; | 
| 240 |  |  |  |  |  |  | } | 
| 241 |  |  |  |  |  |  |  | 
| 242 |  |  |  |  |  |  | # clean up deceased child processes before checking if processes | 
| 243 |  |  |  |  |  |  | # are running. | 
| 244 | 5 |  |  |  |  | 70 | $self->daemon($daemon_names[0])->rm_zombies(); | 
| 245 |  |  |  |  |  |  |  | 
| 246 |  |  |  |  |  |  | # # give any processes that have ceased a second to shut down | 
| 247 | 5 |  |  |  |  | 5000857 | sleep 1; | 
| 248 |  |  |  |  |  |  |  | 
| 249 |  |  |  |  |  |  | # again clean up deceased child processes before checking if | 
| 250 |  |  |  |  |  |  | # processes are running. | 
| 251 | 5 |  |  |  |  | 72 | $self->daemon($daemon_names[0])->rm_zombies(); | 
| 252 |  |  |  |  |  |  |  | 
| 253 | 5 |  |  |  |  | 21 | my @running = (); | 
| 254 |  |  |  |  |  |  |  | 
| 255 | 5 |  |  |  |  | 59 | for my $daemon_name ( @daemon_names ) { | 
| 256 | 15 | 100 |  |  |  | 49 | if ( $self->daemon($daemon_name)->is_running() ) { | 
| 257 | 6 |  |  |  |  | 23 | push @running, $daemon_name | 
| 258 |  |  |  |  |  |  | } | 
| 259 |  |  |  |  |  |  | } | 
| 260 |  |  |  |  |  |  |  | 
| 261 | 5 |  |  |  |  | 145 | return @running; | 
| 262 |  |  |  |  |  |  | } | 
| 263 |  |  |  |  |  |  |  | 
| 264 |  |  |  |  |  |  | =item start( $data ) | 
| 265 |  |  |  |  |  |  |  | 
| 266 |  |  |  |  |  |  | Call the start() method on all registered daemons.  If the daemon is | 
| 267 |  |  |  |  |  |  | already running it will not be restarted. | 
| 268 |  |  |  |  |  |  |  | 
| 269 |  |  |  |  |  |  | =cut | 
| 270 |  |  |  |  |  |  |  | 
| 271 |  |  |  |  |  |  | sub start { | 
| 272 | 4 |  |  | 4 | 1 | 372 | my ( $self, $data ) = @_; | 
| 273 |  |  |  |  |  |  |  | 
| 274 | 4 |  |  |  |  | 11 | my $started; | 
| 275 |  |  |  |  |  |  |  | 
| 276 | 4 |  |  |  |  | 183 | for my $daemon ( $self->daemons() ) { | 
| 277 | 10 | 50 |  |  |  | 61 | if ( $daemon->is_running() ) { | 
| 278 | 0 |  |  |  |  | 0 | print "daemon already running: ", $daemon->daemon_name, "\n"; | 
| 279 |  |  |  |  |  |  | } | 
| 280 |  |  |  |  |  |  | else { | 
| 281 | 10 |  |  |  |  | 2338 | print "starting daemon: ", $daemon->daemon_name, "\n"; | 
| 282 | 10 |  |  |  |  | 79 | $daemon->start(); | 
| 283 | 10 |  |  |  |  | 175 | $started++; | 
| 284 |  |  |  |  |  |  | } | 
| 285 |  |  |  |  |  |  | } | 
| 286 |  |  |  |  |  |  |  | 
| 287 | 4 |  |  |  |  | 316 | return $started; | 
| 288 |  |  |  |  |  |  | } | 
| 289 |  |  |  |  |  |  |  | 
| 290 |  |  |  |  |  |  |  | 
| 291 |  |  |  |  |  |  | =item stop() | 
| 292 |  |  |  |  |  |  |  | 
| 293 |  |  |  |  |  |  | Call the stop() method on all daemons. | 
| 294 |  |  |  |  |  |  |  | 
| 295 |  |  |  |  |  |  | =cut | 
| 296 |  |  |  |  |  |  |  | 
| 297 |  |  |  |  |  |  | sub stop { | 
| 298 | 2 |  |  | 2 | 1 | 8 | my ( $self ) = @_; | 
| 299 |  |  |  |  |  |  |  | 
| 300 | 2 |  |  |  |  | 49 | for my $daemon ( $self->daemons() ) { | 
| 301 | 4 |  |  |  |  | 8467 | print "stopping daemon: ", $daemon->daemon_name, "\n"; | 
| 302 | 4 |  |  |  |  | 46 | $daemon->stop(); | 
| 303 |  |  |  |  |  |  | } | 
| 304 |  |  |  |  |  |  |  | 
| 305 | 2 |  |  |  |  | 70 | return 1; | 
| 306 |  |  |  |  |  |  | } | 
| 307 |  |  |  |  |  |  |  | 
| 308 |  |  |  |  |  |  | =item force_stop() | 
| 309 |  |  |  |  |  |  |  | 
| 310 |  |  |  |  |  |  | Call the force_stop method on all daemons. | 
| 311 |  |  |  |  |  |  |  | 
| 312 |  |  |  |  |  |  | =cut | 
| 313 |  |  |  |  |  |  |  | 
| 314 |  |  |  |  |  |  | sub force_stop { | 
| 315 | 1 |  |  | 1 | 1 | 3 | my ( $self ) = @_; | 
| 316 |  |  |  |  |  |  |  | 
| 317 | 1 |  |  |  |  | 8 | for my $daemon ( $self->daemons() ) { | 
| 318 | 3 |  |  |  |  | 1865 | print "forcefully stopping daemon: ", $daemon->daemon_name, "\n"; | 
| 319 | 3 |  |  |  |  | 18 | $daemon->force_stop(); | 
| 320 |  |  |  |  |  |  | } | 
| 321 |  |  |  |  |  |  |  | 
| 322 | 1 |  |  |  |  | 11 | return 1; | 
| 323 |  |  |  |  |  |  | } | 
| 324 |  |  |  |  |  |  |  | 
| 325 |  |  |  |  |  |  | =item disable() | 
| 326 |  |  |  |  |  |  |  | 
| 327 |  |  |  |  |  |  | Call the disable() method on all daemons. | 
| 328 |  |  |  |  |  |  |  | 
| 329 |  |  |  |  |  |  | =cut | 
| 330 |  |  |  |  |  |  |  | 
| 331 |  |  |  |  |  |  | sub disable { | 
| 332 | 1 |  |  | 1 | 1 | 1315 | my ( $self ) = @_; | 
| 333 |  |  |  |  |  |  |  | 
| 334 | 1 |  |  |  |  | 6 | for my $daemon ( $self->daemons() ) { | 
| 335 | 3 |  |  |  |  | 2279 | print "disabling daemon: ", $daemon->daemon_name, "\n"; | 
| 336 | 3 |  |  |  |  | 41 | $daemon->disable(); | 
| 337 |  |  |  |  |  |  | } | 
| 338 |  |  |  |  |  |  |  | 
| 339 | 1 |  |  |  |  | 56 | return 1; | 
| 340 |  |  |  |  |  |  | } | 
| 341 |  |  |  |  |  |  |  | 
| 342 |  |  |  |  |  |  | =item enable() | 
| 343 |  |  |  |  |  |  |  | 
| 344 |  |  |  |  |  |  | Call the enable() method on all daemons. | 
| 345 |  |  |  |  |  |  |  | 
| 346 |  |  |  |  |  |  | =cut | 
| 347 |  |  |  |  |  |  |  | 
| 348 |  |  |  |  |  |  | sub enable { | 
| 349 | 1 |  |  | 1 | 1 | 8 | my ( $self ) = @_; | 
| 350 |  |  |  |  |  |  |  | 
| 351 | 1 |  |  |  |  | 5 | for my $daemon ( $self->daemons() ) { | 
| 352 | 3 |  |  |  |  | 361 | print "enabling daemon: ", $daemon->daemon_name, "\n"; | 
| 353 | 3 |  |  |  |  | 18 | $daemon->enable(); | 
| 354 |  |  |  |  |  |  | } | 
| 355 |  |  |  |  |  |  |  | 
| 356 | 1 |  |  |  |  | 8 | return 1; | 
| 357 |  |  |  |  |  |  | } | 
| 358 |  |  |  |  |  |  |  | 
| 359 |  |  |  |  |  |  | =item read_log() | 
| 360 |  |  |  |  |  |  |  | 
| 361 |  |  |  |  |  |  | =cut | 
| 362 |  |  |  |  |  |  |  | 
| 363 |  |  |  |  |  |  | sub read_log { | 
| 364 | 5 |  |  | 5 | 1 | 16 | my ( $self, $output_callback, @daemons ) = @_; | 
| 365 |  |  |  |  |  |  |  | 
| 366 | 5 | 100 |  |  |  | 22 | unless ( scalar @daemons ) { @daemons = $self->daemons() } | 
|  | 1 |  |  |  |  | 6 |  | 
| 367 |  |  |  |  |  |  |  | 
| 368 | 5 |  |  |  |  | 16 | for my $daemon ( @daemons ) { | 
| 369 | 4 |  |  |  |  | 21 | $daemon->read_log( $output_callback ); | 
| 370 |  |  |  |  |  |  | } | 
| 371 |  |  |  |  |  |  | } | 
| 372 |  |  |  |  |  |  |  | 
| 373 |  |  |  |  |  |  | =item tail( $output_callback, $timeout_secs ) | 
| 374 |  |  |  |  |  |  |  | 
| 375 |  |  |  |  |  |  | Poll all daemons for output for the specified number of seconds.  Any | 
| 376 |  |  |  |  |  |  | output received in that time will be sent to $output_callback.  Each | 
| 377 |  |  |  |  |  |  | line of output will be prefixed with the daemon name. | 
| 378 |  |  |  |  |  |  |  | 
| 379 |  |  |  |  |  |  |  | 
| 380 |  |  |  |  |  |  |  | 
| 381 |  |  |  |  |  |  | =cut | 
| 382 |  |  |  |  |  |  |  | 
| 383 |  |  |  |  |  |  | sub tail { | 
| 384 | 2 |  |  | 2 | 1 | 6 | my ( $self, $output_callback, $timeout ) = @_; | 
| 385 |  |  |  |  |  |  |  | 
| 386 |  |  |  |  |  |  | # create an array of Log::Tail objects to be passed to select() | 
| 387 | 2 |  |  |  |  | 4 | my @tails; | 
| 388 |  |  |  |  |  |  |  | 
| 389 |  |  |  |  |  |  | # select() will return the paths associated with each daemon, so | 
| 390 |  |  |  |  |  |  | # we need to be able to look up the name of a daemon based on it's | 
| 391 |  |  |  |  |  |  | # log file path. | 
| 392 |  |  |  |  |  |  | my %daemons; | 
| 393 | 2 |  |  |  |  | 21 | for my $daemon ( $self->daemons() ) { | 
| 394 | 2 |  |  |  |  | 16 | push @tails, $daemon->file_tail; | 
| 395 | 2 |  |  |  |  | 33 | $daemons{ $daemon->log_file } = $daemon->daemon_name; | 
| 396 |  |  |  |  |  |  | } | 
| 397 |  |  |  |  |  |  |  | 
| 398 |  |  |  |  |  |  | # calculate the time when we're done processing. | 
| 399 | 2 |  |  |  |  | 14 | my $end = time + $timeout; | 
| 400 |  |  |  |  |  |  |  | 
| 401 |  |  |  |  |  |  | # display all new log output to stdout | 
| 402 | 2 |  |  |  |  | 6 | my $count; | 
| 403 | 2 |  |  |  |  | 6 | while ( 1 ) { | 
| 404 |  |  |  |  |  |  |  | 
| 405 | 8 |  |  |  |  | 38 | my ($nfound,$timeleft,@pending)= | 
| 406 |  |  |  |  |  |  | File::Tail::select(undef,undef,undef,1,@tails); | 
| 407 |  |  |  |  |  |  |  | 
| 408 | 8 | 100 |  |  |  | 4003601 | if ($nfound) { | 
| 409 | 6 |  |  |  |  | 13 | foreach (@pending) { | 
| 410 | 6 |  | 33 |  |  | 27 | my $daemon_name = $daemons{ $_->{input} } || $_->{input}; | 
| 411 | 6 |  |  |  |  | 31 | my $text = $_->read; | 
| 412 |  |  |  |  |  |  |  | 
| 413 | 6 |  |  |  |  | 143 | for my $line ( split /\n/, $text ) { | 
| 414 | 3 |  |  |  |  | 12 | my $output = sprintf( "%-8s: %-1s", $daemon_name, $line ); | 
| 415 | 3 |  |  |  |  | 14 | $output_callback->( "$output\n" ); | 
| 416 |  |  |  |  |  |  | } | 
| 417 |  |  |  |  |  |  | } | 
| 418 |  |  |  |  |  |  | } | 
| 419 |  |  |  |  |  |  | else { | 
| 420 |  |  |  |  |  |  | # if we spawned any child procs, reap any that died | 
| 421 | 2 |  |  |  |  | 82 | waitpid(-1, WNOHANG); | 
| 422 | 2 |  |  |  |  | 2000263 | sleep 1; | 
| 423 |  |  |  |  |  |  | } | 
| 424 |  |  |  |  |  |  |  | 
| 425 |  |  |  |  |  |  | # if timeout was specified, quit when the timeout has passed. | 
| 426 | 8 | 50 |  |  |  | 62 | if ( $timeout ) { | 
| 427 | 8 | 100 |  |  |  | 34 | last if time > $end; | 
| 428 |  |  |  |  |  |  | } | 
| 429 |  |  |  |  |  |  | } | 
| 430 |  |  |  |  |  |  |  | 
| 431 | 2 |  |  |  |  | 57 | return 1; | 
| 432 |  |  |  |  |  |  | } | 
| 433 |  |  |  |  |  |  |  | 
| 434 |  |  |  |  |  |  |  | 
| 435 |  |  |  |  |  |  |  | 
| 436 |  |  |  |  |  |  | =back | 
| 437 |  |  |  |  |  |  |  | 
| 438 |  |  |  |  |  |  | =cut | 
| 439 |  |  |  |  |  |  |  | 
| 440 | 3 |  |  | 3 |  | 21 | no Mouse; | 
|  | 3 |  |  |  |  | 7 |  | 
|  | 3 |  |  |  |  | 13 |  | 
| 441 |  |  |  |  |  |  |  | 
| 442 |  |  |  |  |  |  | 1; |