void ap_init_scoreboard(void *shared_score) { char *more_storage; int i; ap_calc_scoreboard_size(); ap_scoreboard_image = calloc(1, sizeof(scoreboard) + server_limit * sizeof(worker_score *)); more_storage = shared_score; ap_scoreboard_image->global = (global_score *)more_storage; more_storage += sizeof(global_score); ap_scoreboard_image->parent = (process_score *)more_storage; more_storage += sizeof(process_score) * server_limit; ap_scoreboard_image->servers = (worker_score **)((char*)ap_scoreboard_image + sizeof(scoreboard)); for (i = 0; i < server_limit; i++) { ap_scoreboard_image->servers[i] = (worker_score *)more_storage; more_storage += thread_limit * sizeof(worker_score); } if (lb_limit) { ap_scoreboard_image->balancers = (lb_score *)more_storage; more_storage += lb_limit * sizeof(lb_score); } ap_assert(more_storage == (char*)shared_score + scoreboard_size); ap_scoreboard_image->global->server_limit = server_limit; ap_scoreboard_image->global->thread_limit = thread_limit; ap_scoreboard_image->global->lb_limit = lb_limit; }
AP_DECLARE(void) ap_init_scoreboard(void *shared_score) { char *more_storage; int i; ap_calc_scoreboard_size(); ap_scoreboard_image = ap_calloc(1, sizeof(scoreboard) + server_limit * sizeof(worker_score *)); more_storage = shared_score; ap_scoreboard_image->global = (global_score *)more_storage; more_storage += sizeof(global_score); ap_scoreboard_image->parent = (process_score *)more_storage; more_storage += sizeof(process_score) * server_limit; ap_scoreboard_image->servers = (worker_score **)((char*)ap_scoreboard_image + sizeof(scoreboard)); for (i = 0; i < server_limit; i++) { ap_scoreboard_image->servers[i] = (worker_score *)more_storage; more_storage += thread_limit * sizeof(worker_score); } ap_assert(more_storage == (char*)shared_score + scoreboard_size); ap_scoreboard_image->global->server_limit = server_limit; ap_scoreboard_image->global->thread_limit = thread_limit; /* * ******* begin amiya 20140221 ******* */ //we initialize these in worker since ap_daemons_limit //is not defined until mpm_hook is run ap_scoreboard_image->global->running_maxclients = 0; ap_scoreboard_image->global->running_keepalivetimeout = 0; /* * ******* end amiya ******* */ }
/* Create or reinit an existing scoreboard. The MPM can control whether * the scoreboard is shared across multiple processes or not */ int ap_create_scoreboard(apr_pool_t *p, ap_scoreboard_e sb_type) { int i; #if APR_HAS_SHARED_MEMORY apr_status_t rv; #endif if (ap_scoreboard_image) { ap_scoreboard_image->global->restart_time = apr_time_now(); memset(ap_scoreboard_image->parent, 0, sizeof(process_score) * server_limit); for (i = 0; i < server_limit; i++) { memset(ap_scoreboard_image->servers[i], 0, sizeof(worker_score) * thread_limit); } /* Clean up the lb workers data */ if (lb_limit) { memset(ap_scoreboard_image->balancers, 0, sizeof(lb_score) * lb_limit); } return OK; } ap_calc_scoreboard_size(); #if APR_HAS_SHARED_MEMORY if (sb_type == SB_SHARED) { void *sb_shared; rv = open_scoreboard(p); if (rv || !(sb_shared = apr_shm_baseaddr_get(ap_scoreboard_shm))) { return HTTP_INTERNAL_SERVER_ERROR; } memset(sb_shared, 0, scoreboard_size); ap_init_scoreboard(sb_shared); } else #endif { /* A simple malloc will suffice */ void *sb_mem = calloc(1, scoreboard_size); if (sb_mem == NULL) { ap_log_error(APLOG_MARK, APLOG_CRIT, 0, NULL, "(%d)%s: cannot allocate scoreboard", errno, strerror(errno)); return HTTP_INTERNAL_SERVER_ERROR; } ap_init_scoreboard(sb_mem); } ap_scoreboard_image->global->sb_type = scoreboard_type = sb_type; ap_scoreboard_image->global->running_generation = 0; ap_scoreboard_image->global->restart_time = apr_time_now(); apr_pool_cleanup_register(p, NULL, ap_cleanup_scoreboard, apr_pool_cleanup_null); return OK; }
/* Create or reinit an existing scoreboard. The MPM can control whether * the scoreboard is shared across multiple processes or not */ int ap_create_scoreboard(apr_pool_t *p, ap_scoreboard_e sb_type) { int i; #if APR_HAS_SHARED_MEMORY apr_status_t rv; #endif pfn_ap_logio_get_last_bytes = APR_RETRIEVE_OPTIONAL_FN(ap_logio_get_last_bytes); if (ap_scoreboard_image) { ap_scoreboard_image->global->restart_time = apr_time_now(); memset(ap_scoreboard_image->parent, 0, sizeof(process_score) * server_limit); for (i = 0; i < server_limit; i++) { memset(ap_scoreboard_image->servers[i], 0, sizeof(worker_score) * thread_limit); } return OK; } ap_calc_scoreboard_size(); #if APR_HAS_SHARED_MEMORY if (sb_type == SB_SHARED) { void *sb_shared; rv = open_scoreboard(p); if (rv || !(sb_shared = apr_shm_baseaddr_get(ap_scoreboard_shm))) { return HTTP_INTERNAL_SERVER_ERROR; } memset(sb_shared, 0, scoreboard_size); ap_init_scoreboard(sb_shared); } else #endif { /* A simple malloc will suffice */ void *sb_mem = ap_calloc(1, scoreboard_size); ap_init_scoreboard(sb_mem); } scoreboard_type = sb_type; ap_scoreboard_image->global->running_generation = 0; ap_scoreboard_image->global->restart_time = apr_time_now(); apr_pool_cleanup_register(p, NULL, ap_cleanup_scoreboard, apr_pool_cleanup_null); return OK; }
void ap_mpm_child_main(apr_pool_t *pconf) { ap_listen_rec *lr = NULL; int requests_this_child = 0; int rv = 0; unsigned long ulTimes; int my_pid = getpid(); ULONG rc, c; HQUEUE workq; apr_pollset_t *pollset; int num_listeners; TID server_maint_tid; void *sb_mem; /* Stop Ctrl-C/Ctrl-Break signals going to child processes */ DosSetSignalExceptionFocus(0, &ulTimes); set_signals(); /* Create pool for child */ apr_pool_create(&pchild, pconf); ap_run_child_init(pchild, ap_server_conf); /* Create an event semaphore used to trigger other threads to shutdown */ rc = DosCreateEventSem(NULL, &shutdown_event, 0, FALSE); if (rc) { ap_log_error(APLOG_MARK, APLOG_ERR, APR_FROM_OS_ERROR(rc), ap_server_conf, "unable to create shutdown semaphore, exiting"); clean_child_exit(APEXIT_CHILDFATAL); } /* Gain access to the scoreboard. */ rc = DosGetNamedSharedMem(&sb_mem, ap_scoreboard_fname, PAG_READ|PAG_WRITE); if (rc) { ap_log_error(APLOG_MARK, APLOG_ERR, APR_FROM_OS_ERROR(rc), ap_server_conf, "scoreboard not readable in child, exiting"); clean_child_exit(APEXIT_CHILDFATAL); } ap_calc_scoreboard_size(); ap_init_scoreboard(sb_mem); /* Gain access to the accpet mutex */ rc = DosOpenMutexSem(NULL, &ap_mpm_accept_mutex); if (rc) { ap_log_error(APLOG_MARK, APLOG_ERR, APR_FROM_OS_ERROR(rc), ap_server_conf, "accept mutex couldn't be accessed in child, exiting"); clean_child_exit(APEXIT_CHILDFATAL); } /* Find our pid in the scoreboard so we know what slot our parent allocated us */ for (child_slot = 0; ap_scoreboard_image->parent[child_slot].pid != my_pid && child_slot < HARD_SERVER_LIMIT; child_slot++); if (child_slot == HARD_SERVER_LIMIT) { ap_log_error(APLOG_MARK, APLOG_ERR, 0, ap_server_conf, "child pid not found in scoreboard, exiting"); clean_child_exit(APEXIT_CHILDFATAL); } ap_my_generation = ap_scoreboard_image->parent[child_slot].generation; memset(ap_scoreboard_image->servers[child_slot], 0, sizeof(worker_score) * HARD_THREAD_LIMIT); /* Set up an OS/2 queue for passing connections & termination requests * to worker threads */ rc = DosCreateQueue(&workq, QUE_FIFO, apr_psprintf(pchild, "/queues/httpd/work.%d", my_pid)); if (rc) { ap_log_error(APLOG_MARK, APLOG_ERR, APR_FROM_OS_ERROR(rc), ap_server_conf, "unable to create work queue, exiting"); clean_child_exit(APEXIT_CHILDFATAL); } /* Create initial pool of worker threads */ for (c = 0; c < ap_min_spare_threads; c++) { // ap_scoreboard_image->servers[child_slot][c].tid = _beginthread(worker_main, NULL, 128*1024, (void *)c); } /* Start maintenance thread */ server_maint_tid = _beginthread(server_maintenance, NULL, 32768, NULL); /* Set up poll */ for (num_listeners = 0, lr = ap_listeners; lr; lr = lr->next) { num_listeners++; } apr_pollset_create(&pollset, num_listeners, pchild, 0); for (lr = ap_listeners; lr != NULL; lr = lr->next) { apr_pollfd_t pfd = { 0 }; pfd.desc_type = APR_POLL_SOCKET; pfd.desc.s = lr->sd; pfd.reqevents = APR_POLLIN; pfd.client_data = lr; apr_pollset_add(pollset, &pfd); } /* Main connection accept loop */ do { apr_pool_t *pconn; worker_args_t *worker_args; int last_poll_idx = 0; apr_pool_create(&pconn, pchild); worker_args = apr_palloc(pconn, sizeof(worker_args_t)); worker_args->pconn = pconn; if (num_listeners == 1) { rv = apr_socket_accept(&worker_args->conn_sd, ap_listeners->sd, pconn); } else { const apr_pollfd_t *poll_results; apr_int32_t num_poll_results; rc = DosRequestMutexSem(ap_mpm_accept_mutex, SEM_INDEFINITE_WAIT); if (shutdown_pending) { DosReleaseMutexSem(ap_mpm_accept_mutex); break; } rv = APR_FROM_OS_ERROR(rc); if (rv == APR_SUCCESS) { rv = apr_pollset_poll(pollset, -1, &num_poll_results, &poll_results); DosReleaseMutexSem(ap_mpm_accept_mutex); } if (rv == APR_SUCCESS) { if (last_poll_idx >= num_listeners) { last_poll_idx = 0; } lr = poll_results[last_poll_idx++].client_data; rv = apr_socket_accept(&worker_args->conn_sd, lr->sd, pconn); last_poll_idx++; } } if (rv != APR_SUCCESS) { if (!APR_STATUS_IS_EINTR(rv)) { ap_log_error(APLOG_MARK, APLOG_ERR, rv, ap_server_conf, "apr_socket_accept"); clean_child_exit(APEXIT_CHILDFATAL); } } else { DosWriteQueue(workq, WORKTYPE_CONN, sizeof(worker_args_t), worker_args, 0); requests_this_child++; } if (ap_max_requests_per_child != 0 && requests_this_child >= ap_max_requests_per_child) break; } while (!shutdown_pending && ap_my_generation == ap_scoreboard_image->global->running_generation); ap_scoreboard_image->parent[child_slot].quiescing = 1; DosPostEventSem(shutdown_event); DosWaitThread(&server_maint_tid, DCWW_WAIT); if (is_graceful) { char someleft; /* tell our worker threads to exit */ for (c=0; c<HARD_THREAD_LIMIT; c++) { if (ap_scoreboard_image->servers[child_slot][c].status != SERVER_DEAD) { DosWriteQueue(workq, WORKTYPE_EXIT, 0, NULL, 0); } } do { someleft = 0; for (c=0; c<HARD_THREAD_LIMIT; c++) { if (ap_scoreboard_image->servers[child_slot][c].status != SERVER_DEAD) { someleft = 1; DosSleep(1000); break; } } } while (someleft); } else { DosPurgeQueue(workq); for (c=0; c<HARD_THREAD_LIMIT; c++) { if (ap_scoreboard_image->servers[child_slot][c].status != SERVER_DEAD) { DosKillThread(ap_scoreboard_image->servers[child_slot][c].tid); } } } apr_pool_destroy(pchild); }
/* Main processing of the parent process * returns TRUE if restarting */ static char master_main() { server_rec *s = ap_server_conf; ap_listen_rec *lr; parent_info_t *parent_info; char *listener_shm_name; int listener_num, num_listeners, slot; ULONG rc; printf("%s \n", ap_get_server_version()); set_signals(); if (ap_setup_listeners(ap_server_conf) < 1) { ap_log_error(APLOG_MARK, APLOG_ALERT, 0, s, "no listening sockets available, shutting down"); return FALSE; } /* Allocate a shared memory block for the array of listeners */ for (num_listeners = 0, lr = ap_listeners; lr; lr = lr->next) { num_listeners++; } listener_shm_name = apr_psprintf(pconf, "/sharemem/httpd/parent_info.%d", getpid()); rc = DosAllocSharedMem((PPVOID)&parent_info, listener_shm_name, sizeof(parent_info_t) + num_listeners * sizeof(listen_socket_t), PAG_READ|PAG_WRITE|PAG_COMMIT); if (rc) { ap_log_error(APLOG_MARK, APLOG_ALERT, APR_FROM_OS_ERROR(rc), s, "failure allocating shared memory, shutting down"); return FALSE; } /* Store the listener sockets in the shared memory area for our children to see */ for (listener_num = 0, lr = ap_listeners; lr; lr = lr->next, listener_num++) { apr_os_sock_get(&parent_info->listeners[listener_num].listen_fd, lr->sd); } /* Create mutex to prevent multiple child processes from detecting * a connection with apr_poll() */ rc = DosCreateMutexSem(NULL, &ap_mpm_accept_mutex, DC_SEM_SHARED, FALSE); if (rc) { ap_log_error(APLOG_MARK, APLOG_ALERT, APR_FROM_OS_ERROR(rc), s, "failure creating accept mutex, shutting down"); return FALSE; } parent_info->accept_mutex = ap_mpm_accept_mutex; /* Allocate shared memory for scoreboard */ if (ap_scoreboard_image == NULL) { void *sb_mem; rc = DosAllocSharedMem(&sb_mem, ap_scoreboard_fname, ap_calc_scoreboard_size(), PAG_COMMIT|PAG_READ|PAG_WRITE); if (rc) { ap_log_error(APLOG_MARK, APLOG_ERR, APR_FROM_OS_ERROR(rc), ap_server_conf, "unable to allocate shared memory for scoreboard , exiting"); return FALSE; } ap_init_scoreboard(sb_mem); } ap_scoreboard_image->global->restart_time = apr_time_now(); ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf, "%s configured -- resuming normal operations", ap_get_server_version()); ap_log_error(APLOG_MARK, APLOG_INFO, 0, ap_server_conf, "Server built: %s", ap_get_server_built()); #ifdef AP_MPM_WANT_SET_ACCEPT_LOCK_MECH ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf, "AcceptMutex: %s (default: %s)", apr_proc_mutex_name(accept_mutex), apr_proc_mutex_defname()); #endif if (one_process) { ap_scoreboard_image->parent[0].pid = getpid(); ap_mpm_child_main(pconf); return FALSE; } while (!restart_pending && !shutdown_pending) { RESULTCODES proc_rc; PID child_pid; int active_children = 0; /* Count number of active children */ for (slot=0; slot < HARD_SERVER_LIMIT; slot++) { active_children += ap_scoreboard_image->parent[slot].pid != 0 && !ap_scoreboard_image->parent[slot].quiescing; } /* Spawn children if needed */ for (slot=0; slot < HARD_SERVER_LIMIT && active_children < ap_daemons_to_start; slot++) { if (ap_scoreboard_image->parent[slot].pid == 0) { spawn_child(slot); active_children++; } } rc = DosWaitChild(DCWA_PROCESSTREE, DCWW_NOWAIT, &proc_rc, &child_pid, 0); if (rc == 0) { /* A child has terminated, remove its scoreboard entry & terminate if necessary */ for (slot=0; ap_scoreboard_image->parent[slot].pid != child_pid && slot < HARD_SERVER_LIMIT; slot++); if (slot < HARD_SERVER_LIMIT) { ap_scoreboard_image->parent[slot].pid = 0; ap_scoreboard_image->parent[slot].quiescing = 0; if (proc_rc.codeTerminate == TC_EXIT) { /* Child terminated normally, check its exit code and * terminate server if child indicates a fatal error */ if (proc_rc.codeResult == APEXIT_CHILDFATAL) break; } } } else if (rc == ERROR_CHILD_NOT_COMPLETE) { /* No child exited, lets sleep for a while.... */ apr_sleep(SCOREBOARD_MAINTENANCE_INTERVAL); } } /* Signal children to shut down, either gracefully or immediately */ for (slot=0; slot<HARD_SERVER_LIMIT; slot++) { kill(ap_scoreboard_image->parent[slot].pid, is_graceful ? SIGHUP : SIGTERM); } DosFreeMem(parent_info); return restart_pending; }