📄 mpm_winnt.c
字号:
* of this event means that the child process has exited prematurely * due to a seg fault or other irrecoverable error. For server * robustness, master_main will restart the child process under this * condtion. * * master_main uses the child_exit_event to signal the child process * to exit. **********************************************************************/#define NUM_WAIT_HANDLES 3#define CHILD_HANDLE 0#define SHUTDOWN_HANDLE 1#define RESTART_HANDLE 2static int master_main(server_rec *s, HANDLE shutdown_event, HANDLE restart_event){ int rv, cld; int restart_pending; int shutdown_pending; HANDLE child_exit_event; HANDLE event_handles[NUM_WAIT_HANDLES]; DWORD child_pid; restart_pending = shutdown_pending = 0; event_handles[SHUTDOWN_HANDLE] = shutdown_event; event_handles[RESTART_HANDLE] = restart_event; /* Create a single child process */ rv = create_process(pconf, &event_handles[CHILD_HANDLE], &child_exit_event, &child_pid); if (rv < 0) { ap_log_error(APLOG_MARK, APLOG_CRIT, apr_get_os_error(), ap_server_conf, "master_main: create child process failed. Exiting."); shutdown_pending = 1; goto die_now; } if (!strcasecmp(signal_arg, "runservice")) { mpm_service_started(); } /* Update the scoreboard. Note that there is only a single active * child at once. */ ap_scoreboard_image->parent[0].quiescing = 0; ap_scoreboard_image->parent[0].pid = child_pid; /* Wait for shutdown or restart events or for child death */ winnt_mpm_state = AP_MPMQ_RUNNING; rv = WaitForMultipleObjects(NUM_WAIT_HANDLES, (HANDLE *) event_handles, FALSE, INFINITE); cld = rv - WAIT_OBJECT_0; if (rv == WAIT_FAILED) { /* Something serious is wrong */ ap_log_error(APLOG_MARK,APLOG_CRIT, apr_get_os_error(), ap_server_conf, "master_main: WaitForMultipeObjects WAIT_FAILED -- doing server shutdown"); shutdown_pending = 1; } else if (rv == WAIT_TIMEOUT) { /* Hey, this cannot happen */ ap_log_error(APLOG_MARK, APLOG_ERR, apr_get_os_error(), s, "master_main: WaitForMultipeObjects with INFINITE wait exited with WAIT_TIMEOUT"); shutdown_pending = 1; } else if (cld == SHUTDOWN_HANDLE) { /* shutdown_event signalled */ shutdown_pending = 1; ap_log_error(APLOG_MARK, APLOG_NOTICE, APR_SUCCESS, s, "Parent: Received shutdown signal -- Shutting down the server."); if (ResetEvent(shutdown_event) == 0) { ap_log_error(APLOG_MARK, APLOG_ERR, apr_get_os_error(), s, "ResetEvent(shutdown_event)"); } } else if (cld == RESTART_HANDLE) { /* Received a restart event. Prepare the restart_event to be reused * then signal the child process to exit. */ restart_pending = 1; ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, s, "Parent: Received restart signal -- Restarting the server."); if (ResetEvent(restart_event) == 0) { ap_log_error(APLOG_MARK, APLOG_ERR, apr_get_os_error(), s, "Parent: ResetEvent(restart_event) failed."); } if (SetEvent(child_exit_event) == 0) { ap_log_error(APLOG_MARK, APLOG_ERR, apr_get_os_error(), s, "Parent: SetEvent for child process %d failed.", event_handles[CHILD_HANDLE]); } /* Don't wait to verify that the child process really exits, * just move on with the restart. */ CloseHandle(event_handles[CHILD_HANDLE]); event_handles[CHILD_HANDLE] = NULL; } else { /* The child process exited prematurely due to a fatal error. */ DWORD exitcode; if (!GetExitCodeProcess(event_handles[CHILD_HANDLE], &exitcode)) { /* HUH? We did exit, didn't we? */ exitcode = APEXIT_CHILDFATAL; } if ( exitcode == APEXIT_CHILDFATAL || exitcode == APEXIT_CHILDINIT || exitcode == APEXIT_INIT) { ap_log_error(APLOG_MARK, APLOG_ERR, 0, ap_server_conf, "Parent: child process exited with status %u -- Aborting.", exitcode); } else { int i; restart_pending = 1; ap_log_error(APLOG_MARK, APLOG_NOTICE, APR_SUCCESS, ap_server_conf, "Parent: child process exited with status %u -- Restarting.", exitcode); for (i = 0; i < ap_threads_per_child; i++) { ap_update_child_status_from_indexes(0, i, SERVER_DEAD, NULL); } } CloseHandle(event_handles[CHILD_HANDLE]); event_handles[CHILD_HANDLE] = NULL; } if (restart_pending) { ++ap_my_generation; ap_scoreboard_image->global->running_generation = ap_my_generation; }die_now: if (shutdown_pending) { int timeout = 30000; /* Timeout is milliseconds */ winnt_mpm_state = AP_MPMQ_STOPPING; /* This shutdown is only marginally graceful. We will give the * child a bit of time to exit gracefully. If the time expires, * the child will be wacked. */ if (!strcasecmp(signal_arg, "runservice")) { mpm_service_stopping(); } /* Signal the child processes to exit */ if (SetEvent(child_exit_event) == 0) { ap_log_error(APLOG_MARK,APLOG_ERR, apr_get_os_error(), ap_server_conf, "Parent: SetEvent for child process %d failed", event_handles[CHILD_HANDLE]); } if (event_handles[CHILD_HANDLE]) { rv = WaitForSingleObject(event_handles[CHILD_HANDLE], timeout); if (rv == WAIT_OBJECT_0) { ap_log_error(APLOG_MARK,APLOG_NOTICE, APR_SUCCESS, ap_server_conf, "Parent: Child process exited successfully."); CloseHandle(event_handles[CHILD_HANDLE]); event_handles[CHILD_HANDLE] = NULL; } else { ap_log_error(APLOG_MARK,APLOG_NOTICE, APR_SUCCESS, ap_server_conf, "Parent: Forcing termination of child process %d ", event_handles[CHILD_HANDLE]); TerminateProcess(event_handles[CHILD_HANDLE], 1); CloseHandle(event_handles[CHILD_HANDLE]); event_handles[CHILD_HANDLE] = NULL; } } return 0; /* Tell the caller we do not want to restart */ } winnt_mpm_state = AP_MPMQ_STARTING; return 1; /* Tell the caller we want a restart */}/* service_nt_main_fn needs to append the StartService() args * outside of our call stack and thread as the service starts... */apr_array_header_t *mpm_new_argv;/* Remember service_to_start failures to log and fail in pre_config. * Remember inst_argc and inst_argv for installing or starting the * service after we preflight the config. */AP_DECLARE(apr_status_t) ap_mpm_query(int query_code, int *result){ switch(query_code){ case AP_MPMQ_MAX_DAEMON_USED: *result = MAXIMUM_WAIT_OBJECTS; return APR_SUCCESS; case AP_MPMQ_IS_THREADED: *result = AP_MPMQ_STATIC; return APR_SUCCESS; case AP_MPMQ_IS_FORKED: *result = AP_MPMQ_NOT_SUPPORTED; return APR_SUCCESS; case AP_MPMQ_HARD_LIMIT_DAEMONS: *result = HARD_SERVER_LIMIT; return APR_SUCCESS; case AP_MPMQ_HARD_LIMIT_THREADS: *result = thread_limit; return APR_SUCCESS; case AP_MPMQ_MAX_THREADS: *result = ap_threads_per_child; return APR_SUCCESS; case AP_MPMQ_MIN_SPARE_DAEMONS: *result = 0; return APR_SUCCESS; case AP_MPMQ_MIN_SPARE_THREADS: *result = 0; return APR_SUCCESS; case AP_MPMQ_MAX_SPARE_DAEMONS: *result = 0; return APR_SUCCESS; case AP_MPMQ_MAX_SPARE_THREADS: *result = 0; return APR_SUCCESS; case AP_MPMQ_MAX_REQUESTS_DAEMON: *result = ap_max_requests_per_child; return APR_SUCCESS; case AP_MPMQ_MAX_DAEMONS: *result = 0; return APR_SUCCESS; case AP_MPMQ_MPM_STATE: *result = winnt_mpm_state; return APR_SUCCESS; } return APR_ENOTIMPL;} #define SERVICE_UNSET (-1)static apr_status_t service_set = SERVICE_UNSET;static apr_status_t service_to_start_success;static int inst_argc;static const char * const *inst_argv;static char *service_name = NULL; void winnt_rewrite_args(process_rec *process) { /* Handle the following SCM aspects in this phase: * * -k runservice [transition for WinNT, nothing for Win9x] * -k (!)install [error out if name is not installed] * -k uninstall * -k stop * -k shutdown (same as -k stop). Maintained for backward compatability. * * We can't leave this phase until we know our identity * and modify the command arguments appropriately. * * We do not care if the .conf file exists or is parsable when * attempting to stop or uninstall a service. */ apr_status_t rv; char *def_server_root; char *binpath; char optbuf[3]; const char *optarg; int fixed_args; char *pid; apr_getopt_t *opt; int running_as_service = 1; int errout = 0; pconf = process->pconf; osver.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); GetVersionEx(&osver); /* AP_PARENT_PID is only valid in the child */ pid = getenv("AP_PARENT_PID"); if (pid) { /* This is the child */ my_pid = GetCurrentProcessId(); parent_pid = (DWORD) atol(pid); /* Prevent holding open the (nonexistant) console */ real_exit_code = 0; /* The parent is responsible for providing the * COMPLETE ARGUMENTS REQUIRED to the child. * * No further argument parsing is needed, but * for good measure we will provide a simple * signal string for later testing. */ signal_arg = "runchild"; return; } /* This is the parent, we have a long way to go :-) */ parent_pid = my_pid = GetCurrentProcessId(); /* This behavior is voided by setting real_exit_code to 0 */ atexit(hold_console_open_on_error); /* Rewrite process->argv[]; * * strip out -k signal into signal_arg * strip out -n servicename and set the names * add default -d serverroot from the path of this executable * * The end result will look like: * * The invocation command (%0) * The -d serverroot default from the running executable * The requested service's (-n) registry ConfigArgs * The WinNT SCM's StartService() args */ if ((rv = ap_os_proc_filepath(&binpath, process->pconf)) != APR_SUCCESS) { ap_log_error(APLOG_MARK,APLOG_CRIT, rv, NULL, "Failed to get the full path of %s", process->argv[0]); exit(APEXIT_INIT); } /* WARNING: There is an implict assumption here that the * executable resides in ServerRoot or ServerRoot\bin */ def_server_root = (char *) apr_filename_of_pathname(binpath); if (def_server_root > binpath) { *(def_server_root - 1) = '\0'; def_server_root = (char *) apr_filename_of_pathname(binpath); if (!strcasecmp(def_server_root, "bin")) *(def_server_root - 1) = '\0'; } apr_filepath_merge(&def_server_root, NULL, binpath, APR_FILEPATH_TRUENAME, process->pool); /* Use process->pool so that the rewritten argv * lasts for the lifetime of the server process, * because pconf will be destroyed after the * initial pre-flight of the config parser. */ mpm_new_argv = apr_array_make(process->pool, process->argc + 2, sizeof(const char *)); *(const char **)apr_array_push(mpm_new_argv) = process->argv[0]; *(const char **)apr_array_push(mpm_new_argv) = "-d"; *(const char **)apr_array_push(mpm_new_argv) = def_server_root; fixed_args = mpm_new_argv->nelts; optbuf[0] = '-'; optbuf[2] = '\0'; apr_getopt_init(&opt, process->pool, process->argc, (char**) process->argv); opt->errfn = NULL; while ((rv = apr_getopt(opt, "wn:k:" AP_SERVER_BASEARGS, optbuf + 1, &optarg)) == APR_SUCCESS) { switch (optbuf[1]) { /* Shortcuts; include the -w option to hold the window open on error. * This must not be toggled once we reset real_exit_code to 0! */ case 'w': if (real_exit_code) real_exit_code = 2; break; case 'n': service_set = mpm_service_set_name(process->pool, &service_name, optarg); break; case 'k': signal_arg = optarg; break; case 'E': errout = 1; /* Fall through so the Apache main() handles the 'E' arg */ default: *(const char **)apr_array_push(mpm_new_argv) = apr_pstrdup(process->pool, optbuf); if (optarg) { *(const char **)apr_array_push(mpm_new_argv) = optarg; } break; } } /* back up to capture the bad argument */ if (rv == APR_BADCH || rv == APR_BADARG) { opt->ind--; } while (opt->ind < opt->argc) { *(const char **)apr_array_push(mpm_new_argv) = apr_pstrdup(process->pool, opt->argv[opt->ind++]); } /* Track the number of args actually entered by the user */ inst_argc = mpm_new_argv->nelts - fixed_args; /* Provide a default 'run' -k arg to simplify signal_arg tests */ if (!signal_arg) { signal_arg = "run"; running_as_service = 0; } if (!strcasecmp(signal_arg, "runservice")) { /* Start the NT Service _NOW_ because the WinNT SCM is * expecting us to rapidly assume control of our own * process, the SCM will tell us our service name, and * may have extra StartService() command arguments to * add for us. * * The SCM will generally invoke the executable with * the c:\win\system32 default directory. This is very * lethal if folks use ServerRoot /foopath on windows * without a drive letter. Change to the default root * (path to apache root, above /bin) for safety. */ apr_filepath_set(def_server_root, process->pool); /* Any other process has a console, so we don't to begin * a Win9x service until the configuration is parsed and * any command line errors are reported. * * We hold the return value so that we can die in pre_config * after logging begins, and the failure can land in the log. */ if (osver.dwPlatformId == VER_PLATFORM_WIN32_NT) { if (!errout) { mpm_nt_eventlog_stderr_open(service_name, process->pool); } service_to_start_success = mpm_service_to_start(&service_name, process->pool); if (service_to_start_success == APR_SUCCESS) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -