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 osrfLogInfo( OSRF_LOG_MARK, "Looking for idle child" );
668 // Grab the prefork_child at the head of the idle list
669 cur_child = forker->idle_list;
670 forker->idle_list = cur_child->next;
671 cur_child->next = NULL;
673 osrfLogInternal( OSRF_LOG_MARK,
674 "Searching for available child. cur_child->pid = %d", cur_child->pid );
675 osrfLogInternal( OSRF_LOG_MARK, "Current num children %d",
676 forker->current_num_children );
678 osrfLogDebug( OSRF_LOG_MARK, "forker sending data to %d", cur_child->pid );
679 osrfLogInternal( OSRF_LOG_MARK, "Writing to child fd %d",
680 cur_child->write_data_fd );
682 int written = write(cur_child->write_data_fd, msg_data, strlen(msg_data) + 1);
684 // This child appears to be dead or unusable. Discard it.
685 osrfLogWarning( OSRF_LOG_MARK, "Write returned error %d: %s",
686 errno, strerror( errno ) );
687 kill( cur_child->pid, SIGKILL );
688 del_prefork_child( forker, cur_child->pid );
692 add_prefork_child( forker, cur_child ); // Add it to active list
697 /* if none available, add a new child if we can */
699 osrfLogDebug( OSRF_LOG_MARK, "Not enough children, attempting to add...");
701 if( forker->current_num_children < forker->max_children ) {
702 osrfLogDebug( OSRF_LOG_MARK, "Launching new child with current_num = %d",
703 forker->current_num_children );
705 launch_child( forker ); // Put a new child into the idle list
706 if( forker->idle_list ) {
708 // Take the new child from the idle list
709 prefork_child* new_child = forker->idle_list;
710 forker->idle_list = new_child->next;
711 new_child->next = NULL;
713 osrfLogDebug( OSRF_LOG_MARK, "Writing to new child fd %d : pid %d",
714 new_child->write_data_fd, new_child->pid );
717 new_child->write_data_fd, msg_data, strlen(msg_data) + 1);
719 // This child appears to be dead or unusable. Discard it.
720 osrfLogWarning( OSRF_LOG_MARK, "Write returned error %d: %s",
721 errno, strerror( errno ) );
722 kill( cur_child->pid, SIGKILL );
723 del_prefork_child( forker, cur_child->pid );
725 add_prefork_child( forker, new_child );
734 osrfLogWarning( OSRF_LOG_MARK, "No children available, waiting...");
735 check_children( forker, 1 );
736 // Tell the loop not to call check_children again, since we're calling it now
741 reap_children(forker);
743 } // end while( ! honored )
745 message_free( cur_msg );
747 } /* end top level listen loop */
751 /* XXX Add a flag which tells select() to wait forever on children
752 in the best case, this will be faster than calling usleep(x), and
753 in the worst case it won't be slower and will do less logging...
756 @brief See if any children have become available.
757 @param forker Pointer to the prefork_simple that owns the children.
758 @param forever Boolean: true if we should wait indefinitely.
761 Call select() for all the children in the active list. Read each active file
762 descriptor and move the corresponding child to the idle list.
764 If @a forever is true, wait indefinitely for input. Otherwise return immediately if
765 there are no active file descriptors.
767 static void check_children( prefork_simple* forker, int forever ) {
770 reap_children( forker );
772 if( NULL == forker->first_child ) {
773 // If forever is true, then we're here because we've run out of idle
774 // processes, so there should be some active ones around.
775 // If forever is false, then the children may all be idle, and that's okay.
777 osrfLogError( OSRF_LOG_MARK, "No active child processes to check" );
787 // Prepare to select() on pipes from all the active children
788 prefork_child* cur_child = forker->first_child;
790 if( cur_child->read_status_fd > max_fd )
791 max_fd = cur_child->read_status_fd;
792 FD_SET( cur_child->read_status_fd, &read_set );
793 cur_child = cur_child->next;
794 } while( cur_child != forker->first_child );
796 FD_CLR(0,&read_set); /* just to be sure */
799 osrfLogWarning(OSRF_LOG_MARK,
800 "We have no children available - waiting for one to show up...");
802 if( (select_ret=select( max_fd + 1, &read_set, NULL, NULL, NULL)) == -1 ) {
803 osrfLogWarning( OSRF_LOG_MARK, "Select returned error %d on check_children: %s",
804 errno, strerror( errno ) );
806 osrfLogInfo(OSRF_LOG_MARK,
807 "select() completed after waiting on children to become available");
815 if( (select_ret=select( max_fd + 1, &read_set, NULL, NULL, &tv)) == -1 ) {
816 osrfLogWarning( OSRF_LOG_MARK, "Select returned error %d on check_children: %s",
817 errno, strerror( errno ) );
821 if( select_ret == 0 )
824 // Check each child in the active list.
825 // If it has responded, move it to the idle list.
826 cur_child = forker->first_child;
827 prefork_child* next_child = NULL;
830 next_child = cur_child->next;
831 if( FD_ISSET( cur_child->read_status_fd, &read_set ) ) {
832 osrfLogDebug( OSRF_LOG_MARK,
833 "Server received status from a child %d", cur_child->pid );
837 /* now suck off the data */
839 if( (n=read(cur_child->read_status_fd, buf, sizeof(buf) - 1)) < 0 ) {
840 osrfLogWarning( OSRF_LOG_MARK,
841 "Read error after select in child status read with errno %d: %s",
842 errno, strerror( errno ) );
846 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;
859 // Add it to the idle list
860 cur_child->prev = NULL;
861 cur_child->next = forker->idle_list;
862 forker->idle_list = cur_child;
864 cur_child = next_child;
865 } while( forker->first_child && forker->first_child != next_child );
869 @brief Service up a set maximum number of requests; then shut down.
870 @param child Pointer to the prefork_child representing the child process.
872 Called only by child process.
874 Enter a loop, for up to max_requests iterations. On each iteration:
875 - Wait indefinitely for a request from the parent.
876 - Service the request.
877 - Increment a counter. If the limit hasn't been reached, notify the parent that you
878 are available for another request.
880 After exiting the loop, shut down and terminate the process.
882 static void prefork_child_wait( prefork_child* child ) {
885 growing_buffer* gbuf = buffer_init( READ_BUFSIZE );
886 char buf[READ_BUFSIZE];
888 for( i = 0; i < child->max_requests; i++ ) {
891 int gotdata = 0; // boolean; set to true if we get data
892 clr_fl(child->read_data_fd, O_NONBLOCK );
894 while( (n=read(child->read_data_fd, buf, READ_BUFSIZE-1)) > 0 ) {
896 osrfLogDebug(OSRF_LOG_MARK, "Prefork child read %d bytes of data", n);
898 set_fl(child->read_data_fd, O_NONBLOCK );
901 buffer_add( gbuf, buf );
904 if( errno == EAGAIN ) n = 0;
906 if( errno == EPIPE ) {
907 osrfLogDebug(OSRF_LOG_MARK, "C child attempted read on broken pipe, exiting...");
912 osrfLogWarning( OSRF_LOG_MARK,
913 "Prefork child read returned error with errno %d", errno );
916 } else if( gotdata ) {
917 osrfLogDebug(OSRF_LOG_MARK, "Prefork child got a request.. processing..");
918 prefork_child_process_request(child, gbuf->buf);
919 buffer_reset( gbuf );
922 if( i < child->max_requests - 1 )
923 write( child->write_status_fd, "available" /*less than 64 bytes*/, 9 );
928 osrfLogDebug( OSRF_LOG_MARK, "Child with max-requests=%d, num-served=%d exiting...[%ld]",
929 child->max_requests, i, (long) getpid() );
931 osrf_prefork_child_exit(child);
935 @brief Add a prefork_child to the end of the active list.
936 @param forker Pointer to the prefork_simple that owns the list.
937 @param child Pointer to the prefork_child to be added.
939 static void add_prefork_child( prefork_simple* forker, prefork_child* child ) {
941 if( forker->first_child == NULL ) {
942 // Simplest case: list is initially empty.
943 forker->first_child = child;
947 // Find the last node in the circular list.
948 prefork_child* last_child = forker->first_child->prev;
950 // Insert the new child between the last and first children.
951 last_child->next = child;
952 child->prev = last_child;
953 child->next = forker->first_child;
954 forker->first_child->prev = child;
959 @brief Remove a prefork_child, representing a terminated child, from the list it's on.
960 @param forker Pointer to the prefork_simple that owns the child.
961 @param pid Process ID of the child to be removed.
963 Look for the node in the active list, and, failing that, in the idle list. If you
964 find it, close its file descriptors and put it in the free list for potential reuse.
966 static void old_del_prefork_child( prefork_simple* forker, pid_t pid ) {
968 if( forker->first_child == NULL )
969 return; // Empty list; bail out.
971 osrfLogDebug( OSRF_LOG_MARK, "Deleting Child: %d", pid );
973 // Find the node in question
974 prefork_child* cur_child = forker->first_child; /* current pointer */
975 while( cur_child->pid != pid && cur_child->next != forker->first_child )
976 cur_child = cur_child->next;
978 if( cur_child->pid == pid ) {
979 // We found the right node. Remove it from the list.
980 if( cur_child->next == cur_child )
981 forker->first_child = NULL; // only child in the list
983 if( forker->first_child == cur_child )
984 forker->first_child = cur_child->next; // Reseat forker->first_child
986 // Stitch the nodes on either side together
987 cur_child->prev->next = cur_child->next;
988 cur_child->next->prev = cur_child->prev;
992 prefork_child_free( forker, cur_child );
995 // Maybe it's in the idle list. This can happen if, for example,
996 // a child is killed by a signal while it's between requests.
998 prefork_child* prev = NULL;
999 cur_child = forker->idle_list;
1000 while( cur_child && cur_child->pid != pid ) {
1002 cur_child = cur_child->next;
1006 // Detach from the list
1008 prev->next = cur_child->next;
1010 forker->idle_list = cur_child->next;
1013 prefork_child_free( forker, cur_child );
1014 } // else we can't find it, so do nothing.
1018 static void del_prefork_child( prefork_simple* forker, pid_t pid ) {
1020 osrfLogDebug( OSRF_LOG_MARK, "Deleting Child: %d", pid );
1022 prefork_child* cur_child = NULL;
1024 // Look first in the active list
1025 if( forker->first_child ) {
1026 cur_child = forker->first_child; /* current pointer */
1027 while( cur_child->pid != pid && cur_child->next != forker->first_child )
1028 cur_child = cur_child->next;
1030 if( cur_child->pid == pid ) {
1031 // We found the right node. Remove it from the list.
1032 if( cur_child->next == cur_child )
1033 forker->first_child = NULL; // only child in the list
1035 if( forker->first_child == cur_child )
1036 forker->first_child = cur_child->next; // Reseat forker->first_child
1038 // Stitch the adjacent nodes together
1039 cur_child->prev->next = cur_child->next;
1040 cur_child->next->prev = cur_child->prev;
1043 cur_child = NULL; // Didn't find it in the active list
1047 // Maybe it's in the idle list. This can happen if, for example,
1048 // a child is killed by a signal while it's between requests.
1050 prefork_child* prev = NULL;
1051 cur_child = forker->idle_list;
1052 while( cur_child && cur_child->pid != pid ) {
1054 cur_child = cur_child->next;
1058 // Detach from the list
1060 prev->next = cur_child->next;
1062 forker->idle_list = cur_child->next;
1063 } // else we can't find it
1066 // If we found the node, destroy it.
1068 prefork_child_free( forker, cur_child );
1072 @brief Create and initialize a prefork_child.
1073 @param forker Pointer to the prefork_simple that will own the prefork_child.
1074 @param read_data_fd Used by child to read request from parent.
1075 @param write_data_fd Used by parent to write request to child.
1076 @param read_status_fd Used by parent to read status from child.
1077 @param write_status_fd Used by child to write status to parent.
1078 @return Pointer to the newly created prefork_child.
1080 The calling code is responsible for freeing the prefork_child by calling
1081 prefork_child_free().
1083 static prefork_child* prefork_child_init( prefork_simple* forker,
1084 int read_data_fd, int write_data_fd,
1085 int read_status_fd, int write_status_fd ) {
1087 // Allocate a prefork_child -- from the free list if possible, or from
1088 // the heap if necessary. The free list is a non-circular, singly-linked list.
1089 prefork_child* child;
1090 if( forker->free_list ) {
1091 child = forker->free_list;
1092 forker->free_list = child->next;
1094 child = safe_malloc(sizeof(prefork_child));
1097 child->read_data_fd = read_data_fd;
1098 child->write_data_fd = write_data_fd;
1099 child->read_status_fd = read_status_fd;
1100 child->write_status_fd = write_status_fd;
1101 child->max_requests = forker->max_requests;
1102 child->appname = forker->appname; // We don't make a separate copy
1103 child->keepalive = forker->keepalive;
1111 @brief Terminate all child processes and clear out a prefork_simple.
1112 @param prefork Pointer to the prefork_simple to be cleared out.
1114 We do not deallocate the prefork_simple itself, just its contents.
1116 static void prefork_clear( prefork_simple* prefork ) {
1118 // Kill all the active children, and move their prefork_child nodes to the free list.
1119 while( prefork->first_child ) {
1120 kill( prefork->first_child->pid, SIGKILL );
1121 del_prefork_child( prefork, prefork->first_child->pid );
1124 // Kill all the idle prefork children, close their file
1125 // descriptors, and move them to the free list.
1126 prefork_child* child = prefork->idle_list;
1127 prefork->idle_list = NULL;
1129 prefork_child* temp = child->next;
1130 kill( child->pid, SIGKILL );
1131 prefork_child_free( prefork, child );
1134 //prefork->current_num_children = 0;
1136 // Physically free the free list of prefork_children.
1137 child = prefork->free_list;
1138 prefork->free_list = NULL;
1140 prefork_child* temp = child->next;
1145 // Close the Jabber connection
1146 client_free( prefork->connection );
1147 prefork->connection = NULL;
1149 // After giving the child processes a second to terminate, wait on them so that they
1150 // don't become zombies. We don't wait indefinitely, so it's possible that some
1151 // children will survive a bit longer.
1153 while( (waitpid(-1, NULL, WNOHANG)) > 0) {
1154 --prefork->current_num_children;
1157 free(prefork->appname);
1158 prefork->appname = NULL;
1162 @brief Destroy and deallocate a prefork_child.
1163 @param forker Pointer to the prefork_simple that owns the prefork_child.
1164 @param child Pointer to the prefork_child to be destroyed.
1166 static void prefork_child_free( prefork_simple* forker, prefork_child* child ) {
1167 close( child->read_data_fd );
1168 close( child->write_data_fd );
1169 close( child->read_status_fd );
1170 close( child->write_status_fd );
1172 // Stick the prefork_child in a free list for potential reuse. This is a
1173 // non-circular, singly linked list.
1175 child->next = forker->free_list;
1176 forker->free_list = child;