3 @brief Spawn and manage a collection of child process to service requests.
5 Spawn a collection of child processes, replacing them as needed. Forward requests to them
6 and let the children do the work.
8 Each child processes some maximum number of requests before it terminates itself. When a
9 child dies, either deliberately or otherwise, we can spawn another one to replace it,
10 keeping the number of children within a predefined range.
12 Use a doubly-linked circular list to keep track of the children to whom we have forwarded
13 a request, and who are still working on them. Use a separate linear linked list to keep
14 track of children that are currently idle. Move them back and forth as needed.
16 For each child, set up two pipes:
17 - One for the parent to send requests to the child.
18 - One for the child to notify the parent that it is available for another request.
20 The message sent to the child represents an XML stanza as received from Jabber.
22 When the child finishes processing the request, it writes the string "available" back
23 to the parent. Then the parent knows that it can send that child another request.
27 #include <sys/types.h>
33 #include <sys/select.h>
37 #include "opensrf/utils.h"
38 #include "opensrf/log.h"
39 #include "opensrf/transport_client.h"
40 #include "opensrf/osrf_stack.h"
41 #include "opensrf/osrf_settings.h"
42 #include "opensrf/osrf_application.h"
44 #define READ_BUFSIZE 1024
45 #define ABS_MAX_CHILDREN 256
48 int max_requests; /**< How many requests a child processes before terminating. */
49 int min_children; /**< Minimum number of children to maintain. */
50 int max_children; /**< Maximum number of children to maintain. */
51 int fd; /**< Unused. */
52 int data_to_child; /**< Unused. */
53 int data_to_parent; /**< Unused. */
54 int current_num_children; /**< How many children are currently on the list. */
55 int keepalive; /**< Keepalive time for stateful sessions. */
56 char* appname; /**< Name of the application. */
57 /** Points to a circular linked list of children. */
58 struct prefork_child_struct* first_child;
59 /** List of of child processes that aren't doing anything at the moment and are
60 therefore available to service a new request. */
61 struct prefork_child_struct* idle_list;
62 /** List of allocated but unused prefork_children, available for reuse. Each one is just
63 raw memory, apart from the "next" pointer used to stitch them together. In particular,
64 there is no child process for them, and the file descriptors are not open. */
65 struct prefork_child_struct* free_list;
66 transport_client* connection; /**< Connection to Jabber. */
69 struct prefork_child_struct {
70 pid_t pid; /**< Process ID of the child. */
71 int read_data_fd; /**< Child uses to read request. */
72 int write_data_fd; /**< Parent uses to write request. */
73 int read_status_fd; /**< Parent reads to see if child is available. */
74 int write_status_fd; /**< Child uses to notify parent when it's available again. */
75 int max_requests; /**< How many requests a child can process before terminating. */
76 const char* appname; /**< Name of the application. */
77 int keepalive; /**< Keepalive time for stateful sessions. */
78 struct prefork_child_struct* next; /**< Linkage pointer for linked list. */
79 struct prefork_child_struct* prev; /**< Linkage pointer for linked list. */
82 typedef struct prefork_child_struct prefork_child;
84 /** Boolean. Set to true by a signal handler when it traps SIGCHLD. */
85 static volatile sig_atomic_t child_dead;
87 static int prefork_simple_init( prefork_simple* prefork, transport_client* client,
88 int max_requests, int min_children, int max_children );
89 static prefork_child* launch_child( prefork_simple* forker );
90 static void prefork_launch_children( prefork_simple* forker );
91 static void prefork_run(prefork_simple* forker);
92 static void add_prefork_child( prefork_simple* forker, prefork_child* child );
94 static void del_prefork_child( prefork_simple* forker, pid_t pid );
95 static void check_children( prefork_simple* forker, int forever );
96 static void prefork_child_process_request(prefork_child*, char* data);
97 static int prefork_child_init_hook(prefork_child*);
98 static prefork_child* prefork_child_init( prefork_simple* forker,
99 int read_data_fd, int write_data_fd,
100 int read_status_fd, int write_status_fd );
102 /* listens on the 'data_to_child' fd and wait for incoming data */
103 static void prefork_child_wait( prefork_child* child );
104 static void prefork_clear( prefork_simple* );
105 static void prefork_child_free( prefork_simple* forker, prefork_child* );
106 static void osrf_prefork_register_routers( const char* appname );
107 static void osrf_prefork_child_exit( prefork_child* );
109 static void sigchld_handler( int sig );
112 @brief Spawn and manage a collection of drone processes for servicing requests.
113 @param appname Name of the application.
114 @return 0 if successful, or -1 if error.
116 int osrf_prefork_run( const char* appname ) {
119 osrfLogError( OSRF_LOG_MARK, "osrf_prefork_run requires an appname to run!");
123 set_proc_title( "OpenSRF Listener [%s]", appname );
130 osrfLogInfo( OSRF_LOG_MARK, "Loading config in osrf_forker for app %s", appname);
132 char* max_req = osrf_settings_host_value("/apps/%s/unix_config/max_requests", appname);
133 char* min_children = osrf_settings_host_value("/apps/%s/unix_config/min_children", appname);
134 char* max_children = osrf_settings_host_value("/apps/%s/unix_config/max_children", appname);
135 char* keepalive = osrf_settings_host_value("/apps/%s/keepalive", appname);
137 if(!keepalive) osrfLogWarning( OSRF_LOG_MARK, "Keepalive is not defined, assuming %d", kalive);
138 else kalive = atoi(keepalive);
140 if(!max_req) osrfLogWarning( OSRF_LOG_MARK, "Max requests not defined, assuming %d", maxr);
141 else maxr = atoi(max_req);
143 if(!min_children) osrfLogWarning( OSRF_LOG_MARK,
144 "Min children not defined, assuming %d", minc);
145 else minc = atoi(min_children);
147 if(!max_children) osrfLogWarning( OSRF_LOG_MARK,
148 "Max children not defined, assuming %d", maxc);
149 else maxc = atoi(max_children);
155 /* --------------------------------------------------- */
157 char* resc = va_list_to_string("%s_listener", appname);
159 // Make sure that we haven't already booted
160 if(!osrfSystemBootstrapClientResc( NULL, NULL, resc )) {
161 osrfLogError( OSRF_LOG_MARK, "Unable to bootstrap client for osrf_prefork_run()");
168 prefork_simple forker;
170 if( prefork_simple_init( &forker, osrfSystemGetTransportClient(), maxr, minc, maxc ) ) {
171 osrfLogError( OSRF_LOG_MARK,
172 "osrf_prefork_run() failed to create prefork_simple object" );
176 // Finish initializing the prefork_simple.
177 forker.appname = strdup(appname);
178 forker.keepalive = kalive;
180 // Spawn the children; put them in the idle list.
181 prefork_launch_children( &forker );
183 // Tell the router that you're open for business.
184 osrf_prefork_register_routers(appname);
186 // Sit back and let the requests roll in
187 osrfLogInfo( OSRF_LOG_MARK, "Launching osrf_forker for app %s", appname);
188 prefork_run( &forker );
190 osrfLogWarning( OSRF_LOG_MARK, "prefork_run() returned - how??");
191 prefork_clear( &forker );
196 @brief Register the application with a specified router.
197 @param appname Name of the application.
198 @param routerName Name of the router.
199 @param routerDomain Domain of the router.
201 Tell the router that you're open for business so that it can route requests to you.
203 static void osrf_prefork_send_router_registration(
204 const char* appname, const char* routerName, const char* routerDomain ) {
205 // Get a pointer to the global transport_client
206 transport_client* client = osrfSystemGetTransportClient();
208 // Construct the Jabber address of the router
209 char* jid = va_list_to_string( "%s@%s/router", routerName, routerDomain );
210 osrfLogInfo( OSRF_LOG_MARK, "%s registering with router %s", appname, jid );
212 // Create the registration message, and send it
213 transport_message* msg = message_init( "registering", NULL, NULL, jid, NULL );
214 message_set_router_info( msg, NULL, NULL, appname, "register", 0 );
215 client_send_message( client, msg );
222 /** parses a single "complex" router configuration chunk */
223 static void osrf_prefork_parse_router_chunk(const char* appname, jsonObject* routerChunk) {
225 const char* routerName = jsonObjectGetString(jsonObjectGetKeyConst(routerChunk, "name"));
226 const char* domain = jsonObjectGetString(jsonObjectGetKeyConst(routerChunk, "domain"));
227 const jsonObject* services = jsonObjectGetKeyConst(routerChunk, "services");
228 osrfLogDebug(OSRF_LOG_MARK, "found router config with domain %s and name %s",
231 if( services && services->type == JSON_HASH ) {
232 osrfLogDebug(OSRF_LOG_MARK, "investigating router information...");
233 const jsonObject* service_obj = jsonObjectGetKeyConst(services, "service");
235 ; // do nothing (shouldn't happen)
236 else if( JSON_ARRAY == service_obj->type ) {
238 for(j = 0; j < service_obj->size; j++ ) {
239 const char* service = jsonObjectGetString(jsonObjectGetIndex(service_obj, j));
240 if( service && !strcmp( appname, service ))
241 osrf_prefork_send_router_registration(appname, routerName, domain);
244 else if( JSON_STRING == service_obj->type ) {
245 if( !strcmp(appname, jsonObjectGetString( service_obj )) )
246 osrf_prefork_send_router_registration(appname, routerName, domain);
249 osrf_prefork_send_router_registration(appname, routerName, domain);
253 static void osrf_prefork_register_routers( const char* appname ) {
255 jsonObject* routerInfo = osrfConfigGetValueObject(NULL, "/routers/router");
258 for(i = 0; i < routerInfo->size; i++) {
259 jsonObject* routerChunk = jsonObjectGetIndex(routerInfo, i);
261 if(routerChunk->type == JSON_STRING) {
262 /* this accomodates simple router configs */
263 char* routerName = osrfConfigGetValue( NULL, "/router_name" );
264 char* domain = osrfConfigGetValue(NULL, "/routers/router");
265 osrfLogDebug(OSRF_LOG_MARK, "found simple router settings with router name %s",
267 osrf_prefork_send_router_registration(appname, routerName, domain);
270 osrf_prefork_parse_router_chunk(appname, routerChunk);
275 static int prefork_child_init_hook(prefork_child* child) {
277 if(!child) return -1;
278 osrfLogDebug( OSRF_LOG_MARK, "Child init hook for child %d", child->pid);
280 osrfSystemInitCache();
281 char* resc = va_list_to_string("%s_drone", child->appname);
283 /* if we're a source-client, tell the logger now that we're a new process*/
284 char* isclient = osrfConfigGetValue(NULL, "/client");
285 if( isclient && !strcasecmp(isclient,"true") )
286 osrfLogSetIsClient(1);
289 /* we want to remove traces of our parent's socket connection
290 * so we can have our own */
291 osrfSystemIgnoreTransportClient();
293 if(!osrfSystemBootstrapClientResc( NULL, NULL, resc )) {
294 osrfLogError( OSRF_LOG_MARK, "Unable to bootstrap client for osrf_prefork_run()");
301 if( ! osrfAppRunChildInit(child->appname) ) {
302 osrfLogDebug(OSRF_LOG_MARK, "Prefork child_init succeeded\n");
304 osrfLogError(OSRF_LOG_MARK, "Prefork child_init failed\n");
308 set_proc_title( "OpenSRF Drone [%s]", child->appname );
312 static void prefork_child_process_request(prefork_child* child, char* data) {
315 transport_client* client = osrfSystemGetTransportClient();
317 if(!client_connected(client)) {
318 osrfSystemIgnoreTransportClient();
319 osrfLogWarning(OSRF_LOG_MARK, "Reconnecting child to opensrf after disconnect...");
320 if(!osrf_system_bootstrap_client(NULL, NULL)) {
321 osrfLogError( OSRF_LOG_MARK,
322 "Unable to bootstrap client in prefork_child_process_request()");
324 osrf_prefork_child_exit(child);
328 /* construct the message from the xml */
329 transport_message* msg = new_message_from_xml( data );
331 osrfAppSession* session = osrf_stack_transport_handler(msg, child->appname);
334 if( session->stateless && session->state != OSRF_SESSION_CONNECTED ) {
335 osrfAppSessionFree( session );
339 osrfLogDebug( OSRF_LOG_MARK, "Entering keepalive loop for session %s", session->session_id );
340 int keepalive = child->keepalive;
348 osrfLogDebug(OSRF_LOG_MARK,
349 "osrf_prefork calling queue_wait [%d] in keepalive loop", keepalive);
351 retval = osrf_app_session_queue_wait(session, keepalive, &recvd);
354 osrfLogDebug(OSRF_LOG_MARK, "Data received == %d", recvd);
357 osrfLogError(OSRF_LOG_MARK, "queue-wait returned non-success %d", retval);
361 /* see if the client disconnected from us */
362 if(session->state != OSRF_SESSION_CONNECTED)
365 /* if no data was reveived within the timeout interval */
366 if( !recvd && (end - start) >= keepalive ) {
367 osrfLogInfo(OSRF_LOG_MARK,
368 "No request was received in %d seconds, exiting stateful session", keepalive);
369 osrfAppSessionStatus(
373 0, "Disconnected on timeout" );
379 osrfLogDebug( OSRF_LOG_MARK, "Exiting keepalive loop for session %s", session->session_id );
380 osrfAppSessionFree( session );
385 @brief Partially initialize a prefork_simple provided by the caller.
386 @param prefork Pointer to a a raw prefork_simple to be initialized.
387 @param client Pointer to a transport_client (connection to Jabber).
388 @param max_requests The maximum number of requests that a child process may service
390 @param min_children Minimum number of child processes to maintain.
391 @param max_children Maximum number of child processes to maintain.
392 @return 0 if successful, or 1 if not (due to invalid parameters).
394 static int prefork_simple_init( prefork_simple* prefork, transport_client* client,
395 int max_requests, int min_children, int max_children ) {
397 if( min_children > max_children ) {
398 osrfLogError( OSRF_LOG_MARK, "min_children (%d) is greater "
399 "than max_children (%d)", min_children, max_children );
403 if( max_children > ABS_MAX_CHILDREN ) {
404 osrfLogError( OSRF_LOG_MARK, "max_children (%d) is greater than ABS_MAX_CHILDREN (%d)",
405 max_children, ABS_MAX_CHILDREN );
409 osrfLogInfo(OSRF_LOG_MARK, "Prefork launching child with max_request=%d,"
410 "min_children=%d, max_children=%d", max_requests, min_children, max_children );
412 /* flesh out the struct */
413 prefork->max_requests = max_requests;
414 prefork->min_children = min_children;
415 prefork->max_children = max_children;
417 prefork->data_to_child = 0;
418 prefork->data_to_parent = 0;
419 prefork->current_num_children = 0;
420 prefork->keepalive = 0;
421 prefork->appname = NULL;
422 prefork->first_child = NULL;
423 prefork->idle_list = NULL;
424 prefork->free_list = NULL;
425 prefork->connection = client;
431 @brief Spawn a new child process.
432 @param forker Pointer to the prefork_simple that will own the process.
433 @return Pointer to the new prefork_child, or not at all.
435 Spawn a new child process. Create a prefork_child for it and put it in the idle list.
437 After forking, the parent returns a pointer to the new prefork_child. The child
438 services its quota of requests and then terminates without returning.
440 static prefork_child* launch_child( prefork_simple* forker ) {
446 // Set up the data and status pipes
447 if( pipe(data_fd) < 0 ) { /* build the data pipe*/
448 osrfLogError( OSRF_LOG_MARK, "Pipe making error" );
452 if( pipe(status_fd) < 0 ) {/* build the status pipe */
453 osrfLogError( OSRF_LOG_MARK, "Pipe making error" );
459 osrfLogInternal( OSRF_LOG_MARK, "Pipes: %d %d %d %d",
460 data_fd[0], data_fd[1], status_fd[0], status_fd[1] );
462 // Create and initialize a prefork_child for the new process
463 prefork_child* child = prefork_child_init( forker, data_fd[0],
464 data_fd[1], status_fd[0], status_fd[1] );
466 if( (pid=fork()) < 0 ) {
467 osrfLogError( OSRF_LOG_MARK, "Forking Error" );
468 prefork_child_free( forker, child );
472 // Add the new child to the head of the idle list
473 child->next = forker->idle_list;
474 forker->idle_list = child;
476 if( pid > 0 ) { /* parent */
478 signal(SIGCHLD, sigchld_handler);
479 (forker->current_num_children)++;
482 osrfLogDebug( OSRF_LOG_MARK, "Parent launched %d", pid );
483 /* *no* child pipe FD's can be closed or the parent will re-use fd's that
484 the children are currently using */
490 osrfLogInternal( OSRF_LOG_MARK,
491 "I am new child with read_data_fd = %d and write_status_fd = %d",
492 child->read_data_fd, child->write_status_fd );
494 child->pid = getpid();
495 close( child->write_data_fd );
496 close( child->read_status_fd );
499 if( prefork_child_init_hook(child) == -1 ) {
500 osrfLogError(OSRF_LOG_MARK,
501 "Forker child going away because we could not connect to OpenSRF...");
502 osrf_prefork_child_exit(child);
505 prefork_child_wait( child ); // Should exit without returning
506 osrf_prefork_child_exit( child ); // Just to be sure
507 return NULL; // Unreachable, but it keeps the compiler happy
511 static void osrf_prefork_child_exit(prefork_child* child) {
512 osrfAppRunExitCode();
516 static void prefork_launch_children( prefork_simple* forker ) {
519 while( c++ < forker->min_children )
520 launch_child( forker );
525 @brief Signal handler for SIGCHLD: note that a child process has terminated.
526 @param sig The value of the trapped signal; always SIGCHLD.
528 Set a boolean to be checked later.
530 static void sigchld_handler( int sig ) {
531 signal(SIGCHLD, sigchld_handler);
537 @brief Replenish the collection of child processes, after one has terminated.
538 @param forker Pointer to the prefork_simple that manages the child processes.
540 This function is called when we notice (via a signal handler) that a child
543 Spawn a new child process to replace each of the terminated ones.
545 void reap_children( prefork_simple* forker ) {
549 // Reset our boolean so that we can detect any further terminations.
552 // Bury the children so that they won't be zombies. WNOHANG means that waitpid() returns
553 // immediately if there are no waitable children, instead of waiting for more to die.
554 // Ignore the return code of the child. We don't do an autopsy.
555 while( (child_pid = waitpid(-1, NULL, WNOHANG)) > 0)
556 del_prefork_child( forker, child_pid );
558 /* Spawn more children as needed to maintain a minimum brood. */
559 while( forker->current_num_children < forker->min_children )
560 launch_child( forker );
564 @brief Read transport_messages and dispatch them to child processes for servicing.
565 @param forker Pointer to the prefork_simple that manages the child processes.
567 This is the main loop of the parent process, and once entered, does not exit.
569 For each usable transport_message received: look for an idle child to service it. If
570 no idle children are available, either spawn a new one or, if we've already spawned the
571 maximum number of children, wait for one to become available. Once a child is available
572 by whatever means, write an XML version of the input message, to a pipe designated for
575 static void prefork_run( prefork_simple* forker ) {
577 if( NULL == forker->idle_list )
578 return; // No available children, and we haven't even started yet
580 transport_message* cur_msg = NULL;
584 if( forker->first_child == NULL && forker->idle_list == NULL ) {/* no more children */
585 osrfLogWarning( OSRF_LOG_MARK, "No more children..." );
589 // Wait indefinitely for an input message
590 osrfLogDebug( OSRF_LOG_MARK, "Forker going into wait for data...");
591 cur_msg = client_recv( forker->connection, -1 );
593 if( cur_msg == NULL )
594 continue; // Error? Interrupted by a signal? Try again...
596 message_prepare_xml( cur_msg );
597 const char* msg_data = cur_msg->msg_xml;
598 if( ! msg_data || ! *msg_data ) {
599 osrfLogWarning( OSRF_LOG_MARK, "Received % message from %s, thread %",
600 (msg_data ? "empty" : "NULL"), cur_msg->sender, cur_msg->thread );
601 message_free( cur_msg );
602 continue; // Message not usable; go on to the next one.
605 int honored = 0; /* will be set to true when we service the request */
611 check_children( forker, 0 );
614 osrfLogDebug( OSRF_LOG_MARK, "Server received inbound data" );
616 prefork_child* cur_child = NULL;
618 // Look for an available child in the idle list. Since the idle list operates
619 // as a stack, the child we get is the one that was most recently active, or
620 // most recently spawned. That means it's the one most likely still to be in
621 // physical memory, and the one least likely to have to be swapped in.
622 while( forker->idle_list ) {
624 // Grab the prefork_child at the head of the idle list
625 cur_child = forker->idle_list;
626 forker->idle_list = cur_child->next;
628 osrfLogInternal( OSRF_LOG_MARK,
629 "Searching for available child. cur_child->pid = %d", cur_child->pid );
630 osrfLogInternal( OSRF_LOG_MARK, "Current num children %d",
631 forker->current_num_children );
633 osrfLogDebug( OSRF_LOG_MARK, "forker sending data to %d", cur_child->pid );
634 osrfLogInternal( OSRF_LOG_MARK, "Writing to child fd %d",
635 cur_child->write_data_fd );
637 int written = write(cur_child->write_data_fd, msg_data, strlen(msg_data) + 1);
639 // This child appears to be dead or unusable. Discard it.
640 osrfLogWarning( OSRF_LOG_MARK, "Write returned error %d: %s",
641 errno, strerror( errno ) );
642 kill( cur_child->pid, SIGKILL );
643 del_prefork_child( forker, cur_child->pid );
647 add_prefork_child( forker, cur_child ); // Add it to active list
652 /* if none available, add a new child if we can */
654 osrfLogDebug( OSRF_LOG_MARK, "Not enough children, attempting to add...");
656 if( forker->current_num_children < forker->max_children ) {
657 osrfLogDebug( OSRF_LOG_MARK, "Launching new child with current_num = %d",
658 forker->current_num_children );
660 prefork_child* new_child = launch_child( forker );
663 osrfLogDebug( OSRF_LOG_MARK, "Writing to new child fd %d : pid %d",
664 new_child->write_data_fd, new_child->pid );
666 if(write(new_child->write_data_fd, msg_data, strlen(msg_data) + 1) >= 0 ) {
667 forker->first_child = new_child->next;
668 add_prefork_child( forker, new_child );
671 // This child appears to be dead or unusable. Discard it.
672 osrfLogWarning( OSRF_LOG_MARK, "Write returned error %d: %s",
673 errno, strerror( errno ) );
674 kill( cur_child->pid, SIGKILL );
675 osrfLogWarning( OSRF_LOG_MARK, "Write returned error %d: %s",
676 errno, strerror( errno ) );
677 del_prefork_child( forker, cur_child->pid );
685 osrfLogWarning( OSRF_LOG_MARK, "No children available, waiting...");
686 check_children( forker, 1 );
687 // Tell the loop not to call check_children again, since we're calling it now
692 reap_children(forker);
694 } // end while( ! honored )
696 message_free( cur_msg );
698 } /* end top level listen loop */
703 /* XXX Add a flag which tells select() to wait forever on children
704 in the best case, this will be faster than calling usleep(x), and
705 in the worst case it won't be slower and will do less logging...
707 static void check_children( prefork_simple* forker, int forever ) {
710 reap_children(forker);
712 if( NULL == forker->first_child ) {
713 // If forever is true, then we're here because we've run out of idle
714 // processes, so there should be some active ones around. Otherwise
715 // the children may all be idle, and that's okay.
717 osrfLogError( OSRF_LOG_MARK, "No active child processes to check" );
728 reap_children( forker );
730 // Prepare to select() on pipes from all the active children
731 prefork_child* cur_child = forker->first_child;
733 if( cur_child->read_status_fd > max_fd )
734 max_fd = cur_child->read_status_fd;
735 FD_SET( cur_child->read_status_fd, &read_set );
736 cur_child = cur_child->next;
737 } while( cur_child != forker->first_child );
739 FD_CLR(0,&read_set); /* just to be sure */
742 osrfLogWarning(OSRF_LOG_MARK,
743 "We have no children available - waiting for one to show up...");
745 if( (select_ret=select( max_fd + 1 , &read_set, NULL, NULL, NULL)) == -1 ) {
746 osrfLogWarning( OSRF_LOG_MARK, "Select returned error %d on check_children: %s",
747 errno, strerror( errno ) );
749 osrfLogInfo(OSRF_LOG_MARK,
750 "select() completed after waiting on children to become available");
758 if( (select_ret=select( max_fd + 1 , &read_set, NULL, NULL, &tv)) == -1 ) {
759 osrfLogWarning( OSRF_LOG_MARK, "Select returned error %d on check_children: %s",
760 errno, strerror( errno ) );
764 if( select_ret == 0 )
767 /* see if any children have told us they're done */
768 cur_child = forker->first_child;
771 for( j = 0; j!= forker->current_num_children && num_handled < select_ret ; j++ ) {
773 if( FD_ISSET( cur_child->read_status_fd, &read_set ) ) {
774 osrfLogDebug( OSRF_LOG_MARK,
775 "Server received status from a child %d", cur_child->pid );
779 /* now suck off the data */
781 if( (n=read(cur_child->read_status_fd, buf, sizeof(buf) - 1)) < 0 ) {
782 osrfLogWarning( OSRF_LOG_MARK,
783 "Read error after select in child status read with errno %d: %s",
784 errno, strerror( errno ) );
788 osrfLogDebug( OSRF_LOG_MARK, "Read %d bytes from status buffer: %s", n, buf );
790 // Remove the child from the active list
791 if( forker->first_child == cur_child ) {
792 if( cur_child->next == cur_child )
793 forker->first_child = NULL; // only child in the active list
795 forker->first_child = cur_child->next;
797 cur_child->next->prev = cur_child->prev;
798 cur_child->prev->next = cur_child->next;
801 // Add it to the idle list
802 cur_child->prev = NULL;
803 cur_child->next = forker->idle_list;
804 forker->idle_list = cur_child;
806 cur_child = cur_child->next;
811 static void prefork_child_wait( prefork_child* child ) {
814 growing_buffer* gbuf = buffer_init( READ_BUFSIZE );
815 char buf[READ_BUFSIZE];
817 for( i = 0; i < child->max_requests; i++ ) {
820 int gotdata = 0; // boolean; set to true if we get data
821 clr_fl(child->read_data_fd, O_NONBLOCK );
823 while( (n=read(child->read_data_fd, buf, READ_BUFSIZE-1)) > 0 ) {
825 osrfLogDebug(OSRF_LOG_MARK, "Prefork child read %d bytes of data", n);
827 set_fl(child->read_data_fd, O_NONBLOCK );
830 buffer_add( gbuf, buf );
833 if( errno == EAGAIN ) n = 0;
835 if( errno == EPIPE ) {
836 osrfLogDebug(OSRF_LOG_MARK, "C child attempted read on broken pipe, exiting...");
841 osrfLogWarning( OSRF_LOG_MARK,
842 "Prefork child read returned error with errno %d", errno );
845 } else if( gotdata ) {
846 osrfLogDebug(OSRF_LOG_MARK, "Prefork child got a request.. processing..");
847 prefork_child_process_request(child, gbuf->buf);
848 buffer_reset( gbuf );
851 if( i < child->max_requests - 1 )
852 write( child->write_status_fd, "available" /*less than 64 bytes*/, 9 );
857 osrfLogDebug( OSRF_LOG_MARK, "Child with max-requests=%d, num-served=%d exiting...[%ld]",
858 child->max_requests, i, (long) getpid() );
860 osrf_prefork_child_exit(child);
864 @brief Add a prefork_child to the end of the active list.
865 @param forker Pointer to the prefork_simple that owns the list.
866 @param child Pointer to the prefork_child to be added.
868 static void add_prefork_child( prefork_simple* forker, prefork_child* child ) {
870 if( forker->first_child == NULL ) {
871 // Simplest case: list is initially empty.
872 forker->first_child = child;
876 // Find the last node in the circular list.
877 prefork_child* last_child = forker->first_child->prev;
879 // Insert the new child between the last and first children.
880 last_child->next = child;
881 child->prev = last_child;
882 child->next = forker->first_child;
883 forker->first_child->prev = child;
888 @brief Remove a prefork_child, representing a terminated child, from the active list.
889 @param forker Pointer to the prefork_simple that owns the child.
890 @param pid Process ID of the child to be removed.
892 Remove the node from the active list, close its file descriptors, and put it in the
893 free list for potential reuse.
895 static void del_prefork_child( prefork_simple* forker, pid_t pid ) {
897 if( forker->first_child == NULL )
898 return; // Empty list; bail out.
900 osrfLogDebug( OSRF_LOG_MARK, "Deleting Child: %d", pid );
902 // Find the node in question
903 prefork_child* cur_child = forker->first_child; /* current pointer */
904 while( cur_child->pid != pid && cur_child->next != forker->first_child )
905 cur_child = cur_child->next;
907 if( cur_child->pid == pid ) {
908 // We found the right node. Remove it from the list.
909 if( cur_child->next == cur_child )
910 forker->first_child = NULL; // only child in the list
912 if( forker->first_child == cur_child )
913 forker->first_child = cur_child->next; // Reseat forker->first_child
915 // Stitch the nodes on either side together
916 cur_child->prev->next = cur_child->next;
917 cur_child->next->prev = cur_child->prev;
919 --forker->current_num_children;
922 prefork_child_free( forker, cur_child );
925 // Maybe it's in the idle list. This can happen if, for example,
926 // a child is killed by a signal while it's between requests.
928 prefork_child* prev = NULL;
929 cur_child = forker->idle_list;
930 while( cur_child && cur_child->pid != pid ) {
932 cur_child = cur_child->next;
936 // Detach from the list
938 prev->next = cur_child->next;
940 forker->idle_list = cur_child->next;
942 --forker->current_num_children;
945 prefork_child_free( forker, cur_child );
946 } // else we can't find it, so do nothing.
951 @brief Create and initialize a prefork_child.
952 @param forker Pointer to the prefork_simple that will own the prefork_child.
953 @param read_data_fd Used by child to read request from parent.
954 @param write_data_fd Used by parent to write request to child.
955 @param read_status_fd Used by parent to read status from child.
956 @param write_status_fd Used by child to write status to parent.
957 @return Pointer to the newly created prefork_child.
959 The calling code is responsible for freeing the prefork_child by calling
960 prefork_child_free().
962 static prefork_child* prefork_child_init( prefork_simple* forker,
963 int read_data_fd, int write_data_fd,
964 int read_status_fd, int write_status_fd ) {
966 // Allocate a prefork_child -- from the free list if possible, or from
967 // the heap if necessary. The free list is a non-circular, singly-linked list.
968 prefork_child* child;
969 if( forker->free_list ) {
970 child = forker->free_list;
971 forker->free_list = child->next;
973 child = safe_malloc(sizeof(prefork_child));
976 child->read_data_fd = read_data_fd;
977 child->write_data_fd = write_data_fd;
978 child->read_status_fd = read_status_fd;
979 child->write_status_fd = write_status_fd;
980 child->max_requests = forker->max_requests;
981 child->appname = forker->appname; // We don't make a separate copy
982 child->keepalive = forker->keepalive;
990 @brief Terminate all child processes and clear out a prefork_simple.
991 @param prefork Pointer to the prefork_simple to be cleared out.
993 We do not deallocate the prefork_simple itself, just its contents.
995 static void prefork_clear( prefork_simple* prefork ) {
997 // Kill all the active children, and move their prefork_child nodes to the free list.
998 while( prefork->first_child ) {
999 kill( prefork->first_child->pid, SIGKILL );
1000 del_prefork_child( prefork, prefork->first_child->pid );
1003 // Kill all the idle prefork children, close their file
1004 // descriptors, and move them to the free list.
1005 prefork_child* child = prefork->idle_list;
1006 prefork->idle_list = NULL;
1008 prefork_child* temp = child->next;
1009 kill( child->pid, SIGKILL );
1010 prefork_child_free( prefork, child );
1013 prefork->current_num_children = 0;
1015 // Physically free the free list of prefork_children.
1016 child = prefork->free_list;
1017 prefork->free_list = NULL;
1019 prefork_child* temp = child->next;
1024 // Close the Jabber connection
1025 client_free( prefork->connection );
1026 prefork->connection = NULL;
1028 // After giving the child processes a second to terminate, wait on them so that they
1029 // don't become zombies. We don't wait indefinitely, so it's possible that some
1030 // children will survive a bit longer.
1032 while( (waitpid(-1, NULL, WNOHANG)) > 0) {
1033 ; // Another one died...go around again
1036 free(prefork->appname);
1037 prefork->appname = NULL;
1041 @brief Destroy and deallocate a prefork_child.
1042 @param forker Pointer to the prefork_simple that owns the prefork_child.
1043 @param child Pointer to the prefork_child to be destroyed.
1045 static void prefork_child_free( prefork_simple* forker, prefork_child* child ) {
1046 close( child->read_data_fd );
1047 close( child->write_data_fd );
1048 close( child->read_status_fd );
1049 close( child->write_status_fd );
1051 // Stick the prefork_child in a free list for potential reuse. This is a
1052 // non-circular, singly linked list.
1054 child->next = forker->free_list;
1055 forker->free_list = child;