1 # ----------------------------------------------------------------
2 # Copyright (C) 2010 Equinox Software, Inc.
3 # Bill Erickson <erickson@esilibrary.com>
5 # This program is free software; you can redistribute it and/or
6 # modify it under the terms of the GNU General Public License
7 # as published by the Free Software Foundation; either version 2
8 # of the License, or (at your option) any later version.
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
14 # ----------------------------------------------------------------
15 package OpenSRF::Server;
18 use OpenSRF::Transport;
19 use OpenSRF::Application;
20 use OpenSRF::Utils::Config;
21 use OpenSRF::Transport::PeerHandle;
22 use OpenSRF::Utils::SettingsClient;
23 use OpenSRF::Utils::Logger qw($logger);
24 use OpenSRF::DomainObject::oilsResponse qw/:status/;
25 use OpenSRF::Transport::SlimJabber::Client;
27 use POSIX qw/:sys_wait_h :errno_h/;
28 use Fcntl qw(F_GETFL F_SETFL O_NONBLOCK);
29 use Time::HiRes qw/usleep/;
32 our $chatty = 1; # disable for production
34 use constant STATUS_PIPE_DATA_SIZE => 12;
35 use constant WRITE_PIPE_DATA_SIZE => 12;
38 my($class, $service, %args) = @_;
39 my $self = bless(\%args, $class);
41 $self->{service} = $service; # service name
42 $self->{num_children} = 0; # number of child processes
43 $self->{osrf_handle} = undef; # xmpp handle
44 $self->{routers} = []; # list of registered routers
45 $self->{active_list} = []; # list of active children
46 $self->{idle_list} = []; # list of idle children
47 $self->{sighup_pending} = [];
48 $self->{pid_map} = {}; # map of child pid to child for cleaner access
49 $self->{sig_pipe} = 0; # true if last syswrite failed
51 $self->{stderr_log} = $self->{stderr_log_path} . "/${service}_stderr.log"
52 if $self->{stderr_log_path};
54 $self->{min_spare_children} ||= 0;
55 $self->{max_backlog_queue} ||= 1000;
57 $self->{max_spare_children} = $self->{min_spare_children} + 1 if
58 $self->{max_spare_children} and
59 $self->{max_spare_children} <= $self->{min_spare_children};
64 # ----------------------------------------------------------------
65 # Disconnects from routers and waits for child processes to exit.
66 # ----------------------------------------------------------------
72 $logger->info("server: shutting down and cleaning up...");
75 $self->unregister_routers;
78 # graceful shutdown waits for all active
79 # children to complete their in-process tasks.
81 while (@{$self->{active_list}}) {
82 $logger->info("server: graceful shutdown with ".
83 @{$self->{active_list}}." active children...");
85 # block until a child is becomes available
86 $self->check_status(1);
88 $logger->info("server: all clear for graceful shutdown");
91 # don't get sidetracked by signals while we're cleaning up.
92 # it could result in unexpected behavior with list traversal
93 $SIG{CHLD} = 'IGNORE';
95 # terminate the child processes
96 $self->kill_child($_) for
97 (@{$self->{idle_list}}, @{$self->{active_list}});
99 $self->{osrf_handle}->disconnect;
101 # clean up our dead children
102 $self->reap_children(1);
104 exit(0) unless $no_exit;
107 # ----------------------------------------------------------------
108 # SIGHUP handler. Kill all idle children. Copy list of active
109 # children into sighup_pending list for later cleanup.
110 # ----------------------------------------------------------------
113 $logger->info("server: caught SIGHUP; reloading children");
115 # reload the opensrf config
116 # note: calling ::Config->load() results in ever-growing
117 # package names, which eventually causes an exception
118 OpenSRF::Utils::Config->current->_load(
120 config_file => OpenSRF::Utils::Config->current->FILE
123 # force-reload the logger config
124 OpenSRF::Utils::Logger::set_config(1);
126 # copy active list into pending list for later cleanup
127 $self->{sighup_pending} = [ @{$self->{active_list}} ];
129 # idle_list will be modified as children are reaped.
130 my @idle = @{$self->{idle_list}};
132 # idle children are the reaper's plaything
133 $self->kill_child($_) for @idle;
136 # ----------------------------------------------------------------
137 # Waits on the jabber socket for inbound data from the router.
138 # Each new message is passed off to a child process for handling.
139 # At regular intervals, wake up for min/max spare child maintenance
140 # ----------------------------------------------------------------
144 $logger->set_service($self->{service});
146 $SIG{$_} = sub { $self->cleanup; } for (qw/INT QUIT/);
147 $SIG{TERM} = sub { $self->cleanup(0, 1); };
148 $SIG{CHLD} = sub { $self->reap_children(); };
149 $SIG{HUP} = sub { $self->handle_sighup(); };
150 $SIG{USR1} = sub { $self->unregister_routers; };
151 $SIG{USR2} = sub { $self->register_routers; };
153 $self->spawn_children;
154 $self->build_osrf_handle;
155 $self->register_routers;
158 my @max_children_msg_queue;
162 my $from_network = 0;
165 $self->{child_died} = 0;
167 my $msg = shift(@max_children_msg_queue);
169 # no pending message, so wait for the next one forever
170 $from_network = $wait_time = -1 if (!$msg);
171 $msg ||= $self->{osrf_handle}->process($wait_time);
173 !$from_network and $chatty and $logger->debug("server: attempting to process previously queued message");
174 $from_network and $chatty and $logger->internal("server: no queued messages, processing due to network or signal");
176 # we woke up for any reason, reset the wait time to allow
177 # for idle maintenance as necessary
182 if ($msg->type and $msg->type eq 'error') {
183 $logger->info("server: Listener received an XMPP error ".
184 "message. Likely a bounced message. sender=".$msg->from);
186 } elsif(my $child = pop(@{$self->{idle_list}})) {
188 # we have an idle child to handle the request
189 $chatty and $logger->internal("server: passing request to idle child $child");
190 push(@{$self->{active_list}}, $child);
191 $self->write_child($child, $msg);
193 } elsif($self->{num_children} < $self->{max_children}) {
195 # spawning a child to handle the request
196 $chatty and $logger->internal("server: spawning child to handle request");
197 $self->write_child($self->spawn_child(1), $msg);
200 $logger->warn("server: no children available, waiting... consider increasing " .
201 "max_children for this application higher than $self->{max_children} ".
202 "in the OpenSRF configuration if this message occurs frequently");
205 $chatty and $logger->debug("server: queuing new message");
206 push @max_children_msg_queue, $msg;
208 $chatty and $logger->debug("server: re-queuing old message");
209 unshift @max_children_msg_queue, $msg;
212 $logger->warn("server: backlog queue size is now ". scalar(@max_children_msg_queue));
214 if (@max_children_msg_queue < $self->{max_backlog_queue}) {
215 # We still have room on the queue. Set the wait time to
216 # 1s, waiting for a drone to be freed up and reprocess
217 # this (and any other) queued messages.
219 if (!$from_network) {
220 # if we got here, we had retrieved a message from the queue
221 # but couldn't process it... but also hadn't fetched any
222 # additional messages from the network. Doing so now,
223 # as otherwise only one message will ever get queued
224 $msg = $self->{osrf_handle}->process($wait_time);
226 $chatty and $logger->debug("server: queuing new message after a re-queue");
227 push @max_children_msg_queue, $msg;
232 if (!$from_network) {
233 # The queue is full, and we just requeued a message. We'll
234 # now see if there is a request available from the network;
235 # if so, we'll see if a child is available again or else
237 $msg = $self->{osrf_handle}->process($wait_time);
239 $self->check_status();
240 if (@{$self->{idle_list}}) {
241 # child now available, so we'll go ahead and queue it
242 $chatty and $logger->debug("server: queuing new message after a re-queue with a full queue");
243 push @max_children_msg_queue, $msg;
245 # ok, need to drop this one
246 my $resp = OpenSRF::DomainObject::oilsMessage->new();
247 $resp->type('STATUS');
249 OpenSRF::DomainObject::oilsMethodException->new(
250 status => "Service unavailable: no available children and backlog queue at limit",
251 statusCode => STATUS_SERVICEUNAVAILABLE
254 $resp->threadTrace(1);
256 $logger->set_osrf_xid($msg->osrf_xid);
257 $self->{osrf_handle}->send(
259 osrf_xid => $msg->osrf_xid, # Note that this is ignored, which
260 # is why we called $logger->set_osrf_xid above.
261 # We probably don't want that to be necessary
262 # if osrf_xid is explicitly set here, but that'll
263 # be a FIXME for later
264 thread => $msg->thread,
265 body => OpenSRF::Utils::JSON->perl2JSON([ $resp ])
267 $logger->warn("Backlog queue full for $self->{service}; forced to drop message " .
268 $msg->thread . " from " . $msg->from);
277 # don't perform idle maint immediately when woken by SIGCHLD
278 unless($self->{child_died}) {
280 # when we hit equilibrium, there's no need for regular
281 # maintenance, so set wait_time to 'forever'
283 !$self->perform_idle_maintenance and # no maintenance performed this time
284 @{$self->{active_list}} == 0; # no active children
290 # ----------------------------------------------------------------
291 # Launch a new spare child or kill an extra spare child. To
292 # prevent large-scale spawning or die-offs, spawn or kill only
293 # 1 process per idle maintenance loop.
294 # Returns true if any idle maintenance occurred, 0 otherwise
295 # ----------------------------------------------------------------
296 sub perform_idle_maintenance {
299 $chatty and $logger->internal(sub{return
301 "server: %d idle, %d active, %d min_spare, %d max_spare in idle maintenance",
302 scalar(@{$self->{idle_list}}),
303 scalar(@{$self->{active_list}}),
304 $self->{min_spare_children},
305 $self->{max_spare_children}
309 # spawn 1 spare child per maintenance loop if necessary
310 if( $self->{min_spare_children} and
311 $self->{num_children} < $self->{max_children} and
312 scalar(@{$self->{idle_list}}) < $self->{min_spare_children} ) {
314 $chatty and $logger->internal("server: spawning spare child");
318 # kill 1 excess spare child per maintenance loop if necessary
319 } elsif($self->{max_spare_children} and
320 $self->{num_children} > $self->{min_children} and
321 scalar(@{$self->{idle_list}}) > $self->{max_spare_children} ) {
323 $chatty and $logger->internal("server: killing spare child");
333 my $child = shift || pop(@{$self->{idle_list}}) or return;
334 $chatty and $logger->internal("server: killing child $child");
335 kill('TERM', $child->{pid});
338 # ----------------------------------------------------------------
339 # Jabber connection inbound message arrive on.
340 # ----------------------------------------------------------------
341 sub build_osrf_handle {
344 my $conf = OpenSRF::Utils::Config->current;
345 my $username = $conf->bootstrap->username;
346 my $password = $conf->bootstrap->passwd;
347 my $domain = $conf->bootstrap->domain;
348 my $port = $conf->bootstrap->port;
349 my $resource = $self->{service} . '_listener_' . $conf->env->hostname;
351 $logger->debug("server: inbound connecting as $username\@$domain/$resource on port $port");
353 $self->{osrf_handle} =
354 OpenSRF::Transport::SlimJabber::Client->new(
355 username => $username,
356 resource => $resource,
357 password => $password,
362 $self->{osrf_handle}->initialize;
366 # ----------------------------------------------------------------
367 # Sends request data to a child process
368 # ----------------------------------------------------------------
370 my($self, $child, $msg) = @_;
371 my $xml = encode_utf8(decode_utf8($msg->to_xml));
373 # tell the child how much data to expect, minus the header
375 {use bytes; $write_size = length($xml)}
376 $write_size = sprintf("%*s", WRITE_PIPE_DATA_SIZE, $write_size);
380 $self->{sig_pipe} = 0;
381 local $SIG{'PIPE'} = sub { $self->{sig_pipe} = 1; };
383 # In rare cases a child can die between creation and first
384 # write, typically a result of a jabber connect error. Before
385 # sending data to each child, confirm it's still alive. If it's
386 # not, log the error and drop the message to prevent the parent
387 # process from dying.
388 # When a child dies, all of its attributes are deleted,
389 # so the lack of a pid means the child is dead.
390 if (!$child->{pid}) {
391 $logger->error("server: child is dead in write_child(). ".
392 "unable to send message: $xml");
393 return; # avoid syswrite crash
396 # send message to child data pipe
397 syswrite($child->{pipe_to_child}, $write_size . $xml);
399 last unless $self->{sig_pipe};
400 $logger->error("server: got SIGPIPE writing to $child, retrying...");
401 usleep(50000); # 50 msec
404 $logger->error("server: unable to send request message to child $child") if $self->{sig_pipe};
407 # ----------------------------------------------------------------
408 # Checks to see if any child process has reported its availability
409 # In blocking mode, blocks until a child has reported.
410 # ----------------------------------------------------------------
412 my($self, $block) = @_;
414 return unless @{$self->{active_list}};
420 # if can_read or sysread is interrupted while bloking, go back and
421 # wait again until we have at least 1 free child
423 # refresh the read_set handles in case we lost a child in the previous iteration
424 my $read_set = IO::Select->new;
425 $read_set->add($_->{pipe_to_child}) for @{$self->{active_list}};
427 if(my @handles = $read_set->can_read(($block) ? undef : 0)) {
429 for my $pipe (@handles) {
430 sysread($pipe, $pid, STATUS_PIPE_DATA_SIZE) or next;
431 push(@pids, int($pid));
435 last unless $block and !@pids;
440 $chatty and $logger->internal(sub{return "server: ".scalar(@pids)." children reporting for duty: (@pids)" });
445 # move the children from the active list to the idle list
446 for my $proc (@{$self->{active_list}}) {
447 if(grep { $_ == $proc->{pid} } @pids) {
448 push(@{$self->{idle_list}}, $proc);
450 push(@new_actives, $proc);
454 $self->{active_list} = [@new_actives];
456 $chatty and $logger->internal(sub{return sprintf(
457 "server: %d idle and %d active children after status update",
458 scalar(@{$self->{idle_list}}), scalar(@{$self->{active_list}})) });
460 # some children just went from active to idle. let's see
461 # if any of them need to be killed from a previous sighup.
463 for my $child (@{$self->{sighup_pending}}) {
464 if (grep {$_ == $child->{pid}} @pids) {
466 $chatty and $logger->internal(
467 "server: killing previously-active ".
468 "child after receiving SIGHUP: $child");
470 # remove the pending child
471 $self->{sighup_pending} = [
472 grep {$_->{pid} != $child->{pid}}
473 @{$self->{sighup_pending}}
476 # kill the pending child
477 $self->kill_child($child)
482 # ----------------------------------------------------------------
483 # Cleans up any child processes that have exited.
484 # In shutdown mode, block until all children have washed ashore
485 # ----------------------------------------------------------------
487 my($self, $shutdown) = @_;
488 $self->{child_died} = 1;
492 my $pid = waitpid(-1, ($shutdown) ? 0 : WNOHANG);
495 $chatty and $logger->internal("server: reaping child $pid");
497 my $child = $self->{pid_map}->{$pid};
499 close($child->{pipe_to_parent});
500 close($child->{pipe_to_child});
502 $self->{active_list} = [ grep { $_->{pid} != $pid } @{$self->{active_list}} ];
503 $self->{idle_list} = [ grep { $_->{pid} != $pid } @{$self->{idle_list}} ];
505 $self->{num_children}--;
506 delete $self->{pid_map}->{$pid};
507 delete $child->{$_} for keys %$child; # destroy with a vengeance
510 $self->spawn_children unless $shutdown;
512 $chatty and $logger->internal(sub{return sprintf(
513 "server: %d idle and %d active children after reap_children",
514 scalar(@{$self->{idle_list}}), scalar(@{$self->{active_list}})) });
517 # ----------------------------------------------------------------
518 # Spawn up to max_children processes
519 # ----------------------------------------------------------------
522 $self->spawn_child while $self->{num_children} < $self->{min_children};
525 # ----------------------------------------------------------------
526 # Spawns a new child. If $active is set, the child goes directly
527 # into the active_list.
528 # ----------------------------------------------------------------
530 my($self, $active) = @_;
532 my $child = OpenSRF::Server::Child->new($self);
534 # socket for sending message data to the child
536 $child->{pipe_to_child},
537 $child->{pipe_to_parent},
538 AF_UNIX, SOCK_STREAM, PF_UNSPEC)) {
539 $logger->error("server: error creating data socketpair: $!");
543 $child->{pipe_to_child}->autoflush(1);
544 $child->{pipe_to_parent}->autoflush(1);
546 $child->{pid} = fork();
548 if($child->{pid}) { # parent process
549 $self->{num_children}++;
550 $self->{pid_map}->{$child->{pid}} = $child;
553 push(@{$self->{active_list}}, $child);
555 push(@{$self->{idle_list}}, $child);
558 $chatty and $logger->internal(sub{return "server: server spawned child $child with ".$self->{num_children}." total children" });
562 } else { # child process
564 # recover default handling for any signal whose handler
565 # may have been adopted from the parent process.
566 $SIG{$_} = 'DEFAULT' for qw/TERM INT QUIT HUP CHLD USR1 USR2/;
568 if($self->{stderr_log}) {
570 $chatty and $logger->internal(sub{return "server: redirecting STDERR to " . $self->{stderr_log} });
573 unless( open(STDERR, '>>' . $self->{stderr_log}) ) {
574 $logger->error("server: unable to open STDERR log file: " . $self->{stderr_log} . " : $@");
575 open STDERR, '>/dev/null'; # send it back to /dev/null
583 OpenSRF::Transport::PeerHandle->retrieve->disconnect;
585 $logger->error("server: child process died: $@") if $@;
590 # ----------------------------------------------------------------
591 # Sends the register command to the configured routers
592 # ----------------------------------------------------------------
593 sub register_routers {
596 my $conf = OpenSRF::Utils::Config->current;
597 my $routers = $conf->bootstrap->routers;
598 my $router_name = $conf->bootstrap->router_name;
601 for my $router (@$routers) {
604 if( !$router->{services} ||
605 !$router->{services}->{service} ||
607 ref($router->{services}->{service}) eq 'ARRAY' and
608 grep { $_ eq $self->{service} } @{$router->{services}->{service}}
609 ) || $router->{services}->{service} eq $self->{service}) {
611 my $name = $router->{name};
612 my $domain = $router->{domain};
613 push(@targets, "$name\@$domain/router");
617 push(@targets, "$router_name\@$router/router");
622 $logger->info("server: registering with router $_");
623 $self->{osrf_handle}->send(
625 body => 'registering',
626 router_command => 'register',
627 router_class => $self->{service}
631 $self->{routers} = \@targets;
634 # ----------------------------------------------------------------
635 # Sends the unregister command to any routers we have registered
637 # ----------------------------------------------------------------
638 sub unregister_routers {
640 return unless $self->{osrf_handle}->tcp_connected;
642 for my $router (@{$self->{routers}}) {
643 $logger->info("server: disconnecting from router $router");
644 $self->{osrf_handle}->send(
646 body => "unregistering",
647 router_command => "unregister",
648 router_class => $self->{service}
654 package OpenSRF::Server::Child;
657 use OpenSRF::Transport;
658 use OpenSRF::Application;
659 use OpenSRF::Transport::PeerHandle;
660 use OpenSRF::Transport::SlimJabber::XMPPMessage;
661 use OpenSRF::Utils::Logger qw($logger);
662 use OpenSRF::DomainObject::oilsResponse qw/:status/;
663 use Fcntl qw(F_GETFL F_SETFL O_NONBLOCK);
664 use Time::HiRes qw(time usleep);
665 use POSIX qw/:sys_wait_h :errno_h/;
667 use overload '""' => sub { return '[' . shift()->{pid} . ']'; };
670 my($class, $parent) = @_;
671 my $self = bless({}, $class);
672 $self->{pid} = 0; # my process ID
673 $self->{parent} = $parent; # Controller parent process
674 $self->{num_requests} = 0; # total serviced requests
675 $self->{sig_pipe} = 0; # true if last syswrite failed
681 my $flags = fcntl($fh, F_GETFL, 0);
682 fcntl($fh, F_SETFL, $flags | O_NONBLOCK);
687 my $flags = fcntl($fh, F_GETFL, 0);
688 $flags &= ~O_NONBLOCK;
689 fcntl($fh, F_SETFL, $flags);
692 # ----------------------------------------------------------------
693 # Connects to Jabber and runs the application child_init
694 # ----------------------------------------------------------------
697 my $service = $self->{parent}->{service};
698 $0 = "OpenSRF Drone [$service]";
699 OpenSRF::Transport::PeerHandle->construct($service);
700 OpenSRF::Application->application_implementation->child_init
701 if (OpenSRF::Application->application_implementation->can('child_init'));
704 # ----------------------------------------------------------------
705 # Waits for messages from the parent process, handles the message,
706 # then goes into the keepalive loop if this is a stateful session.
707 # When max_requests is hit, the process exits.
708 # ----------------------------------------------------------------
711 my $network = OpenSRF::Transport::PeerHandle->retrieve;
713 # main child run loop. Ends when this child hits max requests.
716 my $data = $self->wait_for_request or next;
718 # Update process name to show activity
722 # Discard extraneous data from the jabber socket
723 if(!$network->flush_socket()) {
724 $logger->error("server: network disconnected! child dropping request and exiting: $data");
728 my $session = OpenSRF::Transport->handler(
729 $self->{parent}->{service},
730 OpenSRF::Transport::SlimJabber::XMPPMessage->new(xml => $data)
733 my $recycle = $self->keepalive_loop($session);
735 last if ++$self->{num_requests} == $self->{parent}->{max_requests};
738 $chatty && $logger->internal(
739 "server: child exiting early on force_recycle");
743 # Tell the parent process we are available to process requests
746 # Repair process name
750 $chatty and $logger->internal("server: child process shutting down after reaching max_requests");
752 OpenSRF::Application->application_implementation->child_exit
753 if (OpenSRF::Application->application_implementation->can('child_exit'));
756 # ----------------------------------------------------------------
757 # waits for a request data on the parent pipe and returns it.
758 # ----------------------------------------------------------------
759 sub wait_for_request {
762 my $data = ''; # final request data
763 my $buf_size = 4096; # default linux pipe_buf (atomic window, not total size)
764 my $read_pipe = $self->{pipe_to_parent};
765 my $bytes_needed; # size of the data we are about to receive
766 my $bytes_recvd; # number of bytes read so far
767 my $first_read = 1; # true for first loop iteration
772 # wait for some data to start arriving
773 my $read_set = IO::Select->new;
774 $read_set->add($read_pipe);
777 # if can_read is interrupted while blocking,
778 # go back and wait again until it succeeds.
779 last if $read_set->can_read;
782 # parent started writing, let's start reading
783 $self->set_nonblock($read_pipe);
786 # read all of the available data
789 my $nbytes = sysread($self->{pipe_to_parent}, $buf, $buf_size);
791 unless(defined $nbytes) {
793 $logger->error("server: error reading data from parent: $!. ".
794 "bytes_needed=$bytes_needed; bytes_recvd=$bytes_recvd; data=$data");
800 last if $nbytes <= 0; # no more data available for reading
802 $bytes_recvd += $nbytes;
806 $self->set_block($self->{pipe_to_parent});
807 return undef if $read_error;
809 # extract the data size and remove the header from the final data
811 my $wps_size = OpenSRF::Server::WRITE_PIPE_DATA_SIZE;
812 $bytes_needed = int(substr($data, 0, $wps_size)) + $wps_size;
813 $data = substr($data, $wps_size);
818 if ($bytes_recvd == $bytes_needed) {
819 # we've read all the data. Nothing left to do
823 $logger->info("server: child process read all available pipe data. ".
824 "waiting for more data from parent. bytes_needed=$bytes_needed; bytes_recvd=$bytes_recvd");
831 # ----------------------------------------------------------------
832 # If this is a stateful opensrf session, wait up to $keepalive
833 # seconds for subsequent requests from the client
834 # ----------------------------------------------------------------
836 my($self, $session) = @_;
837 my $keepalive = $self->{parent}->{keepalive};
839 while($session->state and $session->state == $session->CONNECTED) {
841 unless( $session->queue_wait($keepalive) ) {
843 # client failed to disconnect before timeout
844 $logger->info("server: no request was received in $keepalive seconds, exiting stateful session");
846 my $res = OpenSRF::DomainObject::oilsConnectStatus->new(
847 status => "Disconnected on timeout",
848 statusCode => STATUS_TIMEOUT
851 $session->status($res);
852 $session->state($session->DISCONNECTED);
857 $chatty and $logger->internal("server: child done with request(s)");
859 # Capture the recycle option value before it's clobbered.
860 # The option may be set at any point along the life of the
861 # session. Once set, it remains set unless
862 # $session->force_recycle(0) is explicitly called.
863 my $recycle = $session->force_recycle;
869 # ----------------------------------------------------------------
870 # Report our availability to our parent process
871 # ----------------------------------------------------------------
877 $self->{sig_pipe} = 0;
878 local $SIG{'PIPE'} = sub { $self->{sig_pipe} = 1; };
881 $self->{pipe_to_parent},
882 sprintf("%*s", OpenSRF::Server::STATUS_PIPE_DATA_SIZE, $self->{pid})
885 last unless $self->{sig_pipe};
886 $logger->error("server: $self got SIGPIPE writing status to parent, retrying...");
887 usleep(50000); # 50 msec
890 $logger->error("server: $self unable to send status to parent") if $self->{sig_pipe};