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>
36 #include "opensrf/utils.h"
37 #include "opensrf/log.h"
38 #include "opensrf/transport_client.h"
39 #include "opensrf/osrf_stack.h"
40 #include "opensrf/osrf_settings.h"
41 #include "opensrf/osrf_application.h"
43 #define READ_BUFSIZE 1024
44 #define ABS_MAX_CHILDREN 256
47 int max_requests; /**< How many requests a child processes before terminating. */
48 int min_children; /**< Minimum number of children to maintain. */
49 int max_children; /**< Maximum number of children to maintain. */
50 int fd; /**< Unused. */
51 int data_to_child; /**< Unused. */
52 int data_to_parent; /**< Unused. */
53 int current_num_children; /**< How many children are currently on the list. */
54 int keepalive; /**< Keepalive time for stateful sessions. */
55 char* appname; /**< Name of the application. */
56 /** Points to a circular linked list of children. */
57 struct prefork_child_struct* first_child;
58 /** List of of child processes that aren't doing anything at the moment and are
59 therefore available to service a new request. */
60 struct prefork_child_struct* idle_list;
61 /** List of allocated but unused prefork_children, available for reuse. Each one is just
62 raw memory, apart from the "next" pointer used to stitch them together. In particular,
63 there is no child process for them, and the file descriptors are not open. */
64 struct prefork_child_struct* free_list;
65 transport_client* connection; /**< Connection to Jabber. */
68 struct prefork_child_struct {
69 pid_t pid; /**< Process ID of the child. */
70 int read_data_fd; /**< Child uses to read request. */
71 int write_data_fd; /**< Parent uses to write request. */
72 int read_status_fd; /**< Parent reads to see if child is available. */
73 int write_status_fd; /**< Child uses to notify parent when it's available again. */
74 int max_requests; /**< How many requests a child can process before terminating. */
75 const char* appname; /**< Name of the application. */
76 int keepalive; /**< Keepalive time for stateful sessions. */
77 struct prefork_child_struct* next; /**< Linkage pointer for linked list. */
78 struct prefork_child_struct* prev; /**< Linkage pointer for linked list. */
81 typedef struct prefork_child_struct prefork_child;
83 /** Boolean. Set to true by a signal handler when it traps SIGCHLD. */
84 static volatile sig_atomic_t child_dead;
86 static int prefork_simple_init( prefork_simple* prefork, transport_client* client,
87 int max_requests, int min_children, int max_children );
88 static prefork_child* launch_child( prefork_simple* forker );
89 static void prefork_launch_children( prefork_simple* forker );
90 static void prefork_run(prefork_simple* forker);
91 static void add_prefork_child( prefork_simple* forker, prefork_child* child );
93 static void del_prefork_child( prefork_simple* forker, pid_t pid );
94 static void check_children( prefork_simple* forker, int forever );
95 static void prefork_child_process_request(prefork_child*, char* data);
96 static int prefork_child_init_hook(prefork_child*);
97 static prefork_child* prefork_child_init( prefork_simple* forker,
98 int read_data_fd, int write_data_fd,
99 int read_status_fd, int write_status_fd );
101 /* listens on the 'data_to_child' fd and wait for incoming data */
102 static void prefork_child_wait( prefork_child* child );
103 static void prefork_clear( prefork_simple* );
104 static void prefork_child_free( prefork_simple* forker, prefork_child* );
105 static void osrf_prefork_register_routers( const char* appname );
106 static void osrf_prefork_child_exit( prefork_child* );
108 static void sigchld_handler( int sig );
111 @brief Spawn and manage a collection of drone processes for servicing requests.
112 @param appname Name of the application.
113 @return 0 if successful, or -1 if error.
115 int osrf_prefork_run( const char* appname ) {
118 osrfLogError( OSRF_LOG_MARK, "osrf_prefork_run requires an appname to run!");
122 set_proc_title( "OpenSRF Listener [%s]", appname );
129 osrfLogInfo( OSRF_LOG_MARK, "Loading config in osrf_forker for app %s", appname);
131 char* max_req = osrf_settings_host_value("/apps/%s/unix_config/max_requests", appname);
132 char* min_children = osrf_settings_host_value("/apps/%s/unix_config/min_children", appname);
133 char* max_children = osrf_settings_host_value("/apps/%s/unix_config/max_children", appname);
134 char* keepalive = osrf_settings_host_value("/apps/%s/keepalive", appname);
136 if(!keepalive) osrfLogWarning( OSRF_LOG_MARK, "Keepalive is not defined, assuming %d", kalive);
137 else kalive = atoi(keepalive);
139 if(!max_req) osrfLogWarning( OSRF_LOG_MARK, "Max requests not defined, assuming %d", maxr);
140 else maxr = atoi(max_req);
142 if(!min_children) osrfLogWarning( OSRF_LOG_MARK,
143 "Min children not defined, assuming %d", minc);
144 else minc = atoi(min_children);
146 if(!max_children) osrfLogWarning( OSRF_LOG_MARK,
147 "Max children not defined, assuming %d", maxc);
148 else maxc = atoi(max_children);
154 /* --------------------------------------------------- */
156 char* resc = va_list_to_string("%s_listener", appname);
158 // Make sure that we haven't already booted
159 if(!osrfSystemBootstrapClientResc( NULL, NULL, resc )) {
160 osrfLogError( OSRF_LOG_MARK, "Unable to bootstrap client for osrf_prefork_run()");
167 prefork_simple forker;
169 if( prefork_simple_init( &forker, osrfSystemGetTransportClient(), maxr, minc, maxc ) ) {
170 osrfLogError( OSRF_LOG_MARK,
171 "osrf_prefork_run() failed to create prefork_simple object" );
175 // Finish initializing the prefork_simple.
176 forker.appname = strdup(appname);
177 forker.keepalive = kalive;
179 // Spawn the children; put them in the idle list.
180 prefork_launch_children( &forker );
182 // Tell the router that you're open for business.
183 osrf_prefork_register_routers(appname);
185 // Sit back and let the requests roll in
186 osrfLogInfo( OSRF_LOG_MARK, "Launching osrf_forker for app %s", appname);
187 prefork_run( &forker );
189 osrfLogWarning( OSRF_LOG_MARK, "prefork_run() returned - how??");
190 prefork_clear( &forker );
195 @brief Register the application with a specified router.
196 @param appname Name of the application.
197 @param routerName Name of the router.
198 @param routerDomain Domain of the router.
200 Tell the router that you're open for business so that it can route requests to you.
202 Called only by the parent process.
204 static void osrf_prefork_send_router_registration(
205 const char* appname, const char* routerName, const char* routerDomain ) {
206 // Get a pointer to the global transport_client
207 transport_client* client = osrfSystemGetTransportClient();
209 // Construct the Jabber address of the router
210 char* jid = va_list_to_string( "%s@%s/router", routerName, routerDomain );
211 osrfLogInfo( OSRF_LOG_MARK, "%s registering with router %s", appname, jid );
213 // Create the registration message, and send it
214 transport_message* msg = message_init( "registering", NULL, NULL, jid, NULL );
215 message_set_router_info( msg, NULL, NULL, appname, "register", 0 );
216 client_send_message( client, msg );
223 /* parses a single "complex" router configuration chunk */
224 // Called only by the parent process
225 static void osrf_prefork_parse_router_chunk(const char* appname, const jsonObject* routerChunk) {
227 const char* routerName = jsonObjectGetString(jsonObjectGetKeyConst(routerChunk, "name"));
228 const char* domain = jsonObjectGetString(jsonObjectGetKeyConst(routerChunk, "domain"));
229 const jsonObject* services = jsonObjectGetKeyConst(routerChunk, "services");
230 osrfLogDebug(OSRF_LOG_MARK, "found router config with domain %s and name %s",
233 if( services && services->type == JSON_HASH ) {
234 osrfLogDebug(OSRF_LOG_MARK, "investigating router information...");
235 const jsonObject* service_obj = jsonObjectGetKeyConst(services, "service");
237 ; // do nothing (shouldn't happen)
238 else if( JSON_ARRAY == service_obj->type ) {
240 for(j = 0; j < service_obj->size; j++ ) {
241 const char* service = jsonObjectGetString(jsonObjectGetIndex(service_obj, j));
242 if( service && !strcmp( appname, service ))
243 osrf_prefork_send_router_registration(appname, routerName, domain);
246 else if( JSON_STRING == service_obj->type ) {
247 if( !strcmp(appname, jsonObjectGetString( service_obj )) )
248 osrf_prefork_send_router_registration(appname, routerName, domain);
251 osrf_prefork_send_router_registration(appname, routerName, domain);
256 @brief Register the application with one or more routers, according to the configuration.
257 @param appname Name of the application.
259 Called only by the parent process.
261 static void osrf_prefork_register_routers( const char* appname ) {
263 jsonObject* routerInfo = osrfConfigGetValueObject(NULL, "/routers/router");
266 for(i = 0; i < routerInfo->size; i++) {
267 const jsonObject* routerChunk = jsonObjectGetIndex(routerInfo, i);
269 if(routerChunk->type == JSON_STRING) {
270 /* this accomodates simple router configs */
271 char* routerName = osrfConfigGetValue( NULL, "/router_name" );
272 char* domain = osrfConfigGetValue(NULL, "/routers/router");
273 osrfLogDebug(OSRF_LOG_MARK, "found simple router settings with router name %s",
275 osrf_prefork_send_router_registration(appname, routerName, domain);
280 osrf_prefork_parse_router_chunk(appname, routerChunk);
284 jsonObjectFree( routerInfo );
288 @brief Initialize a child process.
289 @param child Pointer to the prefork_child representing the new child process.
290 @return Zero if successful, or -1 if not.
292 Called only by child processes. Actions:
293 - Connect to one or more cache servers
294 - Reconfigure logger, if necessary
295 - Discard parent's Jabber connection and open a new one
296 - Dynamically call an application-specific initialization routine
297 - Change the command line as reported by ps
299 static int prefork_child_init_hook(prefork_child* child) {
301 if(!child) return -1;
302 osrfLogDebug( OSRF_LOG_MARK, "Child init hook for child %d", child->pid);
304 // Connect to cache server(s).
305 osrfSystemInitCache();
306 char* resc = va_list_to_string("%s_drone", child->appname);
308 // If we're a source-client, tell the logger now that we're a new process.
309 char* isclient = osrfConfigGetValue(NULL, "/client");
310 if( isclient && !strcasecmp(isclient,"true") )
311 osrfLogSetIsClient(1);
314 // Remove traces of our parent's socket connection so we can have our own.
315 osrfSystemIgnoreTransportClient();
318 if(!osrfSystemBootstrapClientResc( NULL, NULL, resc )) {
319 osrfLogError( OSRF_LOG_MARK, "Unable to bootstrap client for osrf_prefork_run()");
326 // Dynamically call the application-specific initialization function
327 // from a previously loaded shared library.
328 if( ! osrfAppRunChildInit(child->appname) ) {
329 osrfLogDebug(OSRF_LOG_MARK, "Prefork child_init succeeded\n");
331 osrfLogError(OSRF_LOG_MARK, "Prefork child_init failed\n");
335 // Change the command line as reported by ps
336 set_proc_title( "OpenSRF Drone [%s]", child->appname );
340 // Called only by a child process
341 static void prefork_child_process_request(prefork_child* child, char* data) {
344 transport_client* client = osrfSystemGetTransportClient();
346 if(!client_connected(client)) {
347 osrfSystemIgnoreTransportClient();
348 osrfLogWarning(OSRF_LOG_MARK, "Reconnecting child to opensrf after disconnect...");
349 if(!osrf_system_bootstrap_client(NULL, NULL)) {
350 osrfLogError( OSRF_LOG_MARK,
351 "Unable to bootstrap client in prefork_child_process_request()");
353 osrf_prefork_child_exit(child);
357 /* construct the message from the xml */
358 transport_message* msg = new_message_from_xml( data );
360 osrfAppSession* session = osrf_stack_transport_handler(msg, child->appname);
363 if( session->stateless && session->state != OSRF_SESSION_CONNECTED ) {
364 osrfAppSessionFree( session );
368 osrfLogDebug( OSRF_LOG_MARK, "Entering keepalive loop for session %s", session->session_id );
369 int keepalive = child->keepalive;
377 osrfLogDebug(OSRF_LOG_MARK,
378 "osrf_prefork calling queue_wait [%d] in keepalive loop", keepalive);
380 retval = osrf_app_session_queue_wait(session, keepalive, &recvd);
383 osrfLogDebug(OSRF_LOG_MARK, "Data received == %d", recvd);
386 osrfLogError(OSRF_LOG_MARK, "queue-wait returned non-success %d", retval);
390 /* see if the client disconnected from us */
391 if(session->state != OSRF_SESSION_CONNECTED)
394 /* if no data was reveived within the timeout interval */
395 if( !recvd && (end - start) >= keepalive ) {
396 osrfLogInfo(OSRF_LOG_MARK,
397 "No request was received in %d seconds, exiting stateful session", keepalive);
398 osrfAppSessionStatus(
402 0, "Disconnected on timeout" );
408 osrfLogDebug( OSRF_LOG_MARK, "Exiting keepalive loop for session %s", session->session_id );
409 osrfAppSessionFree( session );
414 @brief Partially initialize a prefork_simple provided by the caller.
415 @param prefork Pointer to a a raw prefork_simple to be initialized.
416 @param client Pointer to a transport_client (connection to Jabber).
417 @param max_requests The maximum number of requests that a child process may service
419 @param min_children Minimum number of child processes to maintain.
420 @param max_children Maximum number of child processes to maintain.
421 @return 0 if successful, or 1 if not (due to invalid parameters).
423 static int prefork_simple_init( prefork_simple* prefork, transport_client* client,
424 int max_requests, int min_children, int max_children ) {
426 if( min_children > max_children ) {
427 osrfLogError( OSRF_LOG_MARK, "min_children (%d) is greater "
428 "than max_children (%d)", min_children, max_children );
432 if( max_children > ABS_MAX_CHILDREN ) {
433 osrfLogError( OSRF_LOG_MARK, "max_children (%d) is greater than ABS_MAX_CHILDREN (%d)",
434 max_children, ABS_MAX_CHILDREN );
438 osrfLogInfo(OSRF_LOG_MARK, "Prefork launching child with max_request=%d,"
439 "min_children=%d, max_children=%d", max_requests, min_children, max_children );
441 /* flesh out the struct */
442 prefork->max_requests = max_requests;
443 prefork->min_children = min_children;
444 prefork->max_children = max_children;
446 prefork->data_to_child = 0;
447 prefork->data_to_parent = 0;
448 prefork->current_num_children = 0;
449 prefork->keepalive = 0;
450 prefork->appname = NULL;
451 prefork->first_child = NULL;
452 prefork->idle_list = NULL;
453 prefork->free_list = NULL;
454 prefork->connection = client;
460 @brief Spawn a new child process and put it in the idle list.
461 @param forker Pointer to the prefork_simple that will own the process.
462 @return Pointer to the new prefork_child, or not at all.
464 Spawn a new child process. Create a prefork_child for it and put it in the idle list.
466 After forking, the parent returns a pointer to the new prefork_child. The child
467 services its quota of requests and then terminates without returning.
469 static prefork_child* launch_child( prefork_simple* forker ) {
475 // Set up the data and status pipes
476 if( pipe(data_fd) < 0 ) { /* build the data pipe*/
477 osrfLogError( OSRF_LOG_MARK, "Pipe making error" );
481 if( pipe(status_fd) < 0 ) {/* build the status pipe */
482 osrfLogError( OSRF_LOG_MARK, "Pipe making error" );
488 osrfLogInternal( OSRF_LOG_MARK, "Pipes: %d %d %d %d",
489 data_fd[0], data_fd[1], status_fd[0], status_fd[1] );
491 // Create and initialize a prefork_child for the new process
492 prefork_child* child = prefork_child_init( forker, data_fd[0],
493 data_fd[1], status_fd[0], status_fd[1] );
495 if( (pid=fork()) < 0 ) {
496 osrfLogError( OSRF_LOG_MARK, "Forking Error" );
497 prefork_child_free( forker, child );
501 // Add the new child to the head of the idle list
502 child->next = forker->idle_list;
503 forker->idle_list = child;
505 if( pid > 0 ) { /* parent */
507 signal(SIGCHLD, sigchld_handler);
508 (forker->current_num_children)++;
511 osrfLogDebug( OSRF_LOG_MARK, "Parent launched %d", pid );
512 /* *no* child pipe FD's can be closed or the parent will re-use fd's that
513 the children are currently using */
519 osrfLogInternal( OSRF_LOG_MARK,
520 "I am new child with read_data_fd = %d and write_status_fd = %d",
521 child->read_data_fd, child->write_status_fd );
523 child->pid = getpid();
524 close( child->write_data_fd );
525 close( child->read_status_fd );
528 if( prefork_child_init_hook(child) == -1 ) {
529 osrfLogError(OSRF_LOG_MARK,
530 "Forker child going away because we could not connect to OpenSRF...");
531 osrf_prefork_child_exit(child);
534 prefork_child_wait( child ); // Should exit without returning
535 osrf_prefork_child_exit( child ); // Just to be sure
536 return NULL; // Unreachable, but it keeps the compiler happy
541 @brief Terminate a child process.
542 @param child Pointer to the prefork_child representing the child process.
544 Called only by child processes. Dynamically call an application-specific shutdown
545 function from a previously loaded shared library; then exit.
547 static void osrf_prefork_child_exit(prefork_child* child) {
548 osrfAppRunExitCode();
553 @brief Launch all the child processes, putting them in the idle list.
554 @param forker Pointer to the prefork_simple that will own the children.
556 Called only by the parent process (in order to become a parent).
558 static void prefork_launch_children( prefork_simple* forker ) {
561 while( c++ < forker->min_children )
562 launch_child( forker );
566 @brief Signal handler for SIGCHLD: note that a child process has terminated.
567 @param sig The value of the trapped signal; always SIGCHLD.
569 Set a boolean to be checked later.
571 static void sigchld_handler( int sig ) {
572 signal(SIGCHLD, sigchld_handler);
577 @brief Replenish the collection of child processes, after one has terminated.
578 @param forker Pointer to the prefork_simple that manages the child processes.
580 The parent calls this function when it notices (via a signal handler) that
581 a child process has died.
583 Wait on the dead children so that they won't be zombies. Spawn new ones as needed
584 to maintain at least a minimum number.
586 void reap_children( prefork_simple* forker ) {
590 // Reset our boolean so that we can detect any further terminations.
593 // Bury the children so that they won't be zombies. WNOHANG means that waitpid() returns
594 // immediately if there are no waitable children, instead of waiting for more to die.
595 // Ignore the return code of the child. We don't do an autopsy.
596 while( (child_pid = waitpid(-1, NULL, WNOHANG)) > 0) {
597 --forker->current_num_children;
598 del_prefork_child( forker, child_pid );
601 // Spawn more children as needed.
602 while( forker->current_num_children < forker->min_children )
603 launch_child( forker );
607 @brief Read transport_messages and dispatch them to child processes for servicing.
608 @param forker Pointer to the prefork_simple that manages the child processes.
610 This is the main loop of the parent process, and once entered, does not exit.
612 For each usable transport_message received: look for an idle child to service it. If
613 no idle children are available, either spawn a new one or, if we've already spawned the
614 maximum number of children, wait for one to become available. Once a child is available
615 by whatever means, write an XML version of the input message, to a pipe designated for
618 static void prefork_run( prefork_simple* forker ) {
620 if( NULL == forker->idle_list )
621 return; // No available children, and we haven't even started yet
623 transport_message* cur_msg = NULL;
627 if( forker->first_child == NULL && forker->idle_list == NULL ) {/* no more children */
628 osrfLogWarning( OSRF_LOG_MARK, "No more children..." );
632 // Wait indefinitely for an input message
633 osrfLogDebug( OSRF_LOG_MARK, "Forker going into wait for data...");
634 cur_msg = client_recv( forker->connection, -1 );
636 if( cur_msg == NULL )
637 continue; // Error? Interrupted by a signal? Try again...
639 message_prepare_xml( cur_msg );
640 const char* msg_data = cur_msg->msg_xml;
641 if( ! msg_data || ! *msg_data ) {
642 osrfLogWarning( OSRF_LOG_MARK, "Received % message from %s, thread %",
643 (msg_data ? "empty" : "NULL"), cur_msg->sender, cur_msg->thread );
644 message_free( cur_msg );
645 continue; // Message not usable; go on to the next one.
648 int honored = 0; /* will be set to true when we service the request */
654 check_children( forker, 0 );
657 osrfLogDebug( OSRF_LOG_MARK, "Server received inbound data" );
659 prefork_child* cur_child = NULL;
661 // Look for an available child in the idle list. Since the idle list operates
662 // as a stack, the child we get is the one that was most recently active, or
663 // most recently spawned. That means it's the one most likely still to be in
664 // physical memory, and the one least likely to have to be swapped in.
665 while( forker->idle_list ) {
667 // Grab the prefork_child at the head of the idle list
668 cur_child = forker->idle_list;
669 forker->idle_list = cur_child->next;
670 cur_child->next = NULL;
672 osrfLogInternal( OSRF_LOG_MARK,
673 "Searching for available child. cur_child->pid = %d", cur_child->pid );
674 osrfLogInternal( OSRF_LOG_MARK, "Current num children %d",
675 forker->current_num_children );
677 osrfLogDebug( OSRF_LOG_MARK, "forker sending data to %d", cur_child->pid );
678 osrfLogInternal( OSRF_LOG_MARK, "Writing to child fd %d",
679 cur_child->write_data_fd );
681 int written = write(cur_child->write_data_fd, msg_data, strlen(msg_data) + 1);
683 // This child appears to be dead or unusable. Discard it.
684 osrfLogWarning( OSRF_LOG_MARK, "Write returned error %d: %s",
685 errno, strerror( errno ) );
686 kill( cur_child->pid, SIGKILL );
687 del_prefork_child( forker, cur_child->pid );
691 add_prefork_child( forker, cur_child ); // Add it to active list
696 /* if none available, add a new child if we can */
698 osrfLogDebug( OSRF_LOG_MARK, "Not enough children, attempting to add...");
700 if( forker->current_num_children < forker->max_children ) {
701 osrfLogDebug( OSRF_LOG_MARK, "Launching new child with current_num = %d",
702 forker->current_num_children );
704 launch_child( forker ); // Put a new child into the idle list
705 if( forker->idle_list ) {
707 // Take the new child from the idle list
708 prefork_child* new_child = forker->idle_list;
709 forker->idle_list = new_child->next;
710 new_child->next = NULL;
712 osrfLogDebug( OSRF_LOG_MARK, "Writing to new child fd %d : pid %d",
713 new_child->write_data_fd, new_child->pid );
716 new_child->write_data_fd, msg_data, strlen(msg_data) + 1);
718 // This child appears to be dead or unusable. Discard it.
719 osrfLogWarning( OSRF_LOG_MARK, "Write returned error %d: %s",
720 errno, strerror( errno ) );
721 kill( cur_child->pid, SIGKILL );
722 del_prefork_child( forker, cur_child->pid );
724 add_prefork_child( forker, new_child );
733 osrfLogWarning( OSRF_LOG_MARK, "No children available, waiting...");
734 check_children( forker, 1 );
735 // Tell the loop not to call check_children again, since we're calling it now
740 reap_children(forker);
742 } // end while( ! honored )
744 message_free( cur_msg );
746 } /* end top level listen loop */
750 /* XXX Add a flag which tells select() to wait forever on children
751 in the best case, this will be faster than calling usleep(x), and
752 in the worst case it won't be slower and will do less logging...
755 @brief See if any children have become available.
756 @param forker Pointer to the prefork_simple that owns the children.
757 @param forever Boolean: true if we should wait indefinitely.
760 Call select() for all the children in the active list. Read each active file
761 descriptor and move the corresponding child to the idle list.
763 If @a forever is true, wait indefinitely for input. Otherwise return immediately if
764 there are no active file descriptors.
766 static void check_children( prefork_simple* forker, int forever ) {
769 reap_children(forker);
771 if( NULL == forker->first_child ) {
772 // If forever is true, then we're here because we've run out of idle
773 // processes, so there should be some active ones around.
774 // If forever is false, then the children may all be idle, and that's okay.
776 osrfLogError( OSRF_LOG_MARK, "No active child processes to check" );
787 reap_children( forker );
789 // Prepare to select() on pipes from all the active children
790 prefork_child* cur_child = forker->first_child;
792 if( cur_child->read_status_fd > max_fd )
793 max_fd = cur_child->read_status_fd;
794 FD_SET( cur_child->read_status_fd, &read_set );
795 cur_child = cur_child->next;
796 } while( cur_child != forker->first_child );
798 FD_CLR(0,&read_set); /* just to be sure */
801 osrfLogWarning(OSRF_LOG_MARK,
802 "We have no children available - waiting for one to show up...");
804 if( (select_ret=select( max_fd + 1 , &read_set, NULL, NULL, NULL)) == -1 ) {
805 osrfLogWarning( OSRF_LOG_MARK, "Select returned error %d on check_children: %s",
806 errno, strerror( errno ) );
808 osrfLogInfo(OSRF_LOG_MARK,
809 "select() completed after waiting on children to become available");
817 if( (select_ret=select( max_fd + 1 , &read_set, NULL, NULL, &tv)) == -1 ) {
818 osrfLogWarning( OSRF_LOG_MARK, "Select returned error %d on check_children: %s",
819 errno, strerror( errno ) );
823 if( select_ret == 0 )
826 /* see if any children have told us they're done */
827 cur_child = forker->first_child;
830 for( j = 0; j!= forker->current_num_children && num_handled < select_ret ; j++ ) {
832 if( FD_ISSET( cur_child->read_status_fd, &read_set ) ) {
833 osrfLogDebug( OSRF_LOG_MARK,
834 "Server received status from a child %d", cur_child->pid );
838 /* now suck off the data */
840 if( (n=read(cur_child->read_status_fd, buf, sizeof(buf) - 1)) < 0 ) {
841 osrfLogWarning( OSRF_LOG_MARK,
842 "Read error after select in child status read with errno %d: %s",
843 errno, strerror( errno ) );
847 osrfLogDebug( OSRF_LOG_MARK, "Read %d bytes from status buffer: %s", n, buf );
849 // Remove the child from the active list
850 if( forker->first_child == cur_child ) {
851 if( cur_child->next == cur_child )
852 forker->first_child = NULL; // only child in the active list
854 forker->first_child = cur_child->next;
856 cur_child->next->prev = cur_child->prev;
857 cur_child->prev->next = cur_child->next;
860 // Add it to the idle list
861 cur_child->prev = NULL;
862 cur_child->next = forker->idle_list;
863 forker->idle_list = cur_child;
865 cur_child = cur_child->next;
870 @brief Service up a set maximum number of requests; then shut down.
871 @param child Pointer to the prefork_child representing the child process.
873 Called only by child process.
875 Enter a loop, for up to max_requests iterations. On each iteration:
876 - Wait indefinitely for a request from the parent.
877 - Service the request.
878 - Increment a counter. If the limit hasn't been reached, notify the parent that you
879 are available for another request.
881 After exiting the loop, shut down and terminate the process.
883 static void prefork_child_wait( prefork_child* child ) {
886 growing_buffer* gbuf = buffer_init( READ_BUFSIZE );
887 char buf[READ_BUFSIZE];
889 for( i = 0; i < child->max_requests; i++ ) {
892 int gotdata = 0; // boolean; set to true if we get data
893 clr_fl(child->read_data_fd, O_NONBLOCK );
895 while( (n=read(child->read_data_fd, buf, READ_BUFSIZE-1)) > 0 ) {
897 osrfLogDebug(OSRF_LOG_MARK, "Prefork child read %d bytes of data", n);
899 set_fl(child->read_data_fd, O_NONBLOCK );
902 buffer_add( gbuf, buf );
905 if( errno == EAGAIN ) n = 0;
907 if( errno == EPIPE ) {
908 osrfLogDebug(OSRF_LOG_MARK, "C child attempted read on broken pipe, exiting...");
913 osrfLogWarning( OSRF_LOG_MARK,
914 "Prefork child read returned error with errno %d", errno );
917 } else if( gotdata ) {
918 osrfLogDebug(OSRF_LOG_MARK, "Prefork child got a request.. processing..");
919 prefork_child_process_request(child, gbuf->buf);
920 buffer_reset( gbuf );
923 if( i < child->max_requests - 1 )
924 write( child->write_status_fd, "available" /*less than 64 bytes*/, 9 );
929 osrfLogDebug( OSRF_LOG_MARK, "Child with max-requests=%d, num-served=%d exiting...[%ld]",
930 child->max_requests, i, (long) getpid() );
932 osrf_prefork_child_exit(child);
936 @brief Add a prefork_child to the end of the active list.
937 @param forker Pointer to the prefork_simple that owns the list.
938 @param child Pointer to the prefork_child to be added.
940 static void add_prefork_child( prefork_simple* forker, prefork_child* child ) {
942 if( forker->first_child == NULL ) {
943 // Simplest case: list is initially empty.
944 forker->first_child = child;
948 // Find the last node in the circular list.
949 prefork_child* last_child = forker->first_child->prev;
951 // Insert the new child between the last and first children.
952 last_child->next = child;
953 child->prev = last_child;
954 child->next = forker->first_child;
955 forker->first_child->prev = child;
960 @brief Remove a prefork_child, representing a terminated child, from the active list.
961 @param forker Pointer to the prefork_simple that owns the child.
962 @param pid Process ID of the child to be removed.
964 Remove the node from the active list, close its file descriptors, and put it in the
965 free list for potential reuse.
967 static void del_prefork_child( prefork_simple* forker, pid_t pid ) {
969 if( forker->first_child == NULL )
970 return; // Empty list; bail out.
972 osrfLogDebug( OSRF_LOG_MARK, "Deleting Child: %d", pid );
974 // Find the node in question
975 prefork_child* cur_child = forker->first_child; /* current pointer */
976 while( cur_child->pid != pid && cur_child->next != forker->first_child )
977 cur_child = cur_child->next;
979 if( cur_child->pid == pid ) {
980 // We found the right node. Remove it from the list.
981 if( cur_child->next == cur_child )
982 forker->first_child = NULL; // only child in the list
984 if( forker->first_child == cur_child )
985 forker->first_child = cur_child->next; // Reseat forker->first_child
987 // Stitch the nodes on either side together
988 cur_child->prev->next = cur_child->next;
989 cur_child->next->prev = cur_child->prev;
991 //--forker->current_num_children;
994 prefork_child_free( forker, cur_child );
997 // Maybe it's in the idle list. This can happen if, for example,
998 // a child is killed by a signal while it's between requests.
1000 prefork_child* prev = NULL;
1001 cur_child = forker->idle_list;
1002 while( cur_child && cur_child->pid != pid ) {
1004 cur_child = cur_child->next;
1008 // Detach from the list
1010 prev->next = cur_child->next;
1012 forker->idle_list = cur_child->next;
1014 //--forker->current_num_children;
1017 prefork_child_free( forker, cur_child );
1018 } // else we can't find it, so do nothing.
1023 @brief Create and initialize a prefork_child.
1024 @param forker Pointer to the prefork_simple that will own the prefork_child.
1025 @param read_data_fd Used by child to read request from parent.
1026 @param write_data_fd Used by parent to write request to child.
1027 @param read_status_fd Used by parent to read status from child.
1028 @param write_status_fd Used by child to write status to parent.
1029 @return Pointer to the newly created prefork_child.
1031 The calling code is responsible for freeing the prefork_child by calling
1032 prefork_child_free().
1034 static prefork_child* prefork_child_init( prefork_simple* forker,
1035 int read_data_fd, int write_data_fd,
1036 int read_status_fd, int write_status_fd ) {
1038 // Allocate a prefork_child -- from the free list if possible, or from
1039 // the heap if necessary. The free list is a non-circular, singly-linked list.
1040 prefork_child* child;
1041 if( forker->free_list ) {
1042 child = forker->free_list;
1043 forker->free_list = child->next;
1045 child = safe_malloc(sizeof(prefork_child));
1048 child->read_data_fd = read_data_fd;
1049 child->write_data_fd = write_data_fd;
1050 child->read_status_fd = read_status_fd;
1051 child->write_status_fd = write_status_fd;
1052 child->max_requests = forker->max_requests;
1053 child->appname = forker->appname; // We don't make a separate copy
1054 child->keepalive = forker->keepalive;
1062 @brief Terminate all child processes and clear out a prefork_simple.
1063 @param prefork Pointer to the prefork_simple to be cleared out.
1065 We do not deallocate the prefork_simple itself, just its contents.
1067 static void prefork_clear( prefork_simple* prefork ) {
1069 // Kill all the active children, and move their prefork_child nodes to the free list.
1070 while( prefork->first_child ) {
1071 kill( prefork->first_child->pid, SIGKILL );
1072 del_prefork_child( prefork, prefork->first_child->pid );
1075 // Kill all the idle prefork children, close their file
1076 // descriptors, and move them to the free list.
1077 prefork_child* child = prefork->idle_list;
1078 prefork->idle_list = NULL;
1080 prefork_child* temp = child->next;
1081 kill( child->pid, SIGKILL );
1082 prefork_child_free( prefork, child );
1085 //prefork->current_num_children = 0;
1087 // Physically free the free list of prefork_children.
1088 child = prefork->free_list;
1089 prefork->free_list = NULL;
1091 prefork_child* temp = child->next;
1096 // Close the Jabber connection
1097 client_free( prefork->connection );
1098 prefork->connection = NULL;
1100 // After giving the child processes a second to terminate, wait on them so that they
1101 // don't become zombies. We don't wait indefinitely, so it's possible that some
1102 // children will survive a bit longer.
1104 while( (waitpid(-1, NULL, WNOHANG)) > 0) {
1105 --prefork->current_num_children;
1108 free(prefork->appname);
1109 prefork->appname = NULL;
1113 @brief Destroy and deallocate a prefork_child.
1114 @param forker Pointer to the prefork_simple that owns the prefork_child.
1115 @param child Pointer to the prefork_child to be destroyed.
1117 static void prefork_child_free( prefork_simple* forker, prefork_child* child ) {
1118 close( child->read_data_fd );
1119 close( child->write_data_fd );
1120 close( child->read_status_fd );
1121 close( child->write_status_fd );
1123 // Stick the prefork_child in a free list for potential reuse. This is a
1124 // non-circular, singly linked list.
1126 child->next = forker->free_list;
1127 forker->free_list = child;