📄 mpm_winnt.c
字号:
EVENT_MODIFY_STATE | SYNCHRONIZE, FALSE, 0)) { ap_log_error(APLOG_MARK, APLOG_CRIT, apr_get_os_error(), ap_server_conf, "Parent: Unable to duplicate the ready event handle for the child"); return -1; } if ((rv = apr_file_write_full(child_in, &hDup, sizeof(hDup), &BytesWritten)) != APR_SUCCESS) { ap_log_error(APLOG_MARK, APLOG_CRIT, rv, ap_server_conf, "Parent: Unable to send the exit event handle to the child"); return -1; } if (!DuplicateHandle(hCurrentProcess, child_exit_event, hProcess, &hDup, EVENT_MODIFY_STATE | SYNCHRONIZE, FALSE, 0)) { ap_log_error(APLOG_MARK, APLOG_CRIT, apr_get_os_error(), ap_server_conf, "Parent: Unable to duplicate the exit event handle for the child"); return -1; } if ((rv = apr_file_write_full(child_in, &hDup, sizeof(hDup), &BytesWritten)) != APR_SUCCESS) { ap_log_error(APLOG_MARK, APLOG_CRIT, rv, ap_server_conf, "Parent: Unable to send the exit event handle to the child"); return -1; } if ((rv = apr_os_proc_mutex_get(&os_start, child_start_mutex)) != APR_SUCCESS) { ap_log_error(APLOG_MARK, APLOG_CRIT, rv, ap_server_conf, "Parent: Unable to retrieve the start mutex for the child"); return -1; } if (!DuplicateHandle(hCurrentProcess, os_start, hProcess, &hDup, SYNCHRONIZE, FALSE, 0)) { ap_log_error(APLOG_MARK, APLOG_CRIT, apr_get_os_error(), ap_server_conf, "Parent: Unable to duplicate the start mutex to the child"); return -1; } if ((rv = apr_file_write_full(child_in, &hDup, sizeof(hDup), &BytesWritten)) != APR_SUCCESS) { ap_log_error(APLOG_MARK, APLOG_CRIT, rv, ap_server_conf, "Parent: Unable to send the start mutex to the child"); return -1; } if ((rv = apr_os_shm_get(&hScore, scoreboard_shm)) != APR_SUCCESS) { ap_log_error(APLOG_MARK, APLOG_CRIT, rv, ap_server_conf, "Parent: Unable to retrieve the scoreboard handle for the child"); return -1; } if (!DuplicateHandle(hCurrentProcess, hScore, hProcess, &hDup, FILE_MAP_READ | FILE_MAP_WRITE, FALSE, 0)) { ap_log_error(APLOG_MARK, APLOG_CRIT, apr_get_os_error(), ap_server_conf, "Parent: Unable to duplicate the scoreboard handle to the child"); return -1; } if ((rv = apr_file_write_full(child_in, &hDup, sizeof(hDup), &BytesWritten)) != APR_SUCCESS) { ap_log_error(APLOG_MARK, APLOG_CRIT, rv, ap_server_conf, "Parent: Unable to send the scoreboard handle to the child"); return -1; } ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf, "Parent: Sent the scoreboard to the child"); return 0;}/* * get_listeners_from_parent() * The listen sockets are opened in the parent. This function, which runs * exclusively in the child process, receives them from the parent and * makes them availeble in the child. */void get_listeners_from_parent(server_rec *s){ WSAPROTOCOL_INFO WSAProtocolInfo; HANDLE pipe; ap_listen_rec *lr; DWORD BytesRead; int lcnt = 0; SOCKET nsd; /* Set up a default listener if necessary */ if (ap_listeners == NULL) { ap_listen_rec *lr; lr = apr_palloc(s->process->pool, sizeof(ap_listen_rec)); lr->sd = NULL; lr->next = ap_listeners; ap_listeners = lr; } /* Open the pipe to the parent process to receive the inherited socket * data. The sockets have been set to listening in the parent process. */ pipe = GetStdHandle(STD_INPUT_HANDLE); for (lr = ap_listeners; lr; lr = lr->next, ++lcnt) { if (!ReadFile(pipe, &WSAProtocolInfo, sizeof(WSAPROTOCOL_INFO), &BytesRead, (LPOVERLAPPED) NULL)) { ap_log_error(APLOG_MARK, APLOG_CRIT, apr_get_os_error(), ap_server_conf, "setup_inherited_listeners: Unable to read socket data from parent"); exit(APEXIT_CHILDINIT); } nsd = WSASocket(FROM_PROTOCOL_INFO, FROM_PROTOCOL_INFO, FROM_PROTOCOL_INFO, &WSAProtocolInfo, 0, 0); if (nsd == INVALID_SOCKET) { ap_log_error(APLOG_MARK, APLOG_CRIT, apr_get_netos_error(), ap_server_conf, "Child %d: setup_inherited_listeners(), WSASocket failed to open the inherited socket.", my_pid); exit(APEXIT_CHILDINIT); } if (osver.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) { HANDLE hProcess = GetCurrentProcess(); HANDLE dup; if (DuplicateHandle(hProcess, (HANDLE) nsd, hProcess, &dup, 0, FALSE, DUPLICATE_SAME_ACCESS)) { closesocket(nsd); nsd = (SOCKET) dup; } } else { /* A different approach. Many users report errors such as * (32538)An operation was attempted on something that is not * a socket. : Parent: WSADuplicateSocket failed... * * This appears that the duplicated handle is no longer recognized * as a socket handle. SetHandleInformation should overcome that * problem by not altering the handle identifier. But this won't * work on 9x - it's unsupported. */ if (!SetHandleInformation((HANDLE)nsd, HANDLE_FLAG_INHERIT, 0)) { ap_log_error(APLOG_MARK, APLOG_ERR, apr_get_os_error(), ap_server_conf, "set_listeners_noninheritable: SetHandleInformation failed."); } } apr_os_sock_put(&lr->sd, &nsd, s->process->pool); } ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf, "Child %d: retrieved %d listeners from parent", my_pid, lcnt);}static int send_listeners_to_child(apr_pool_t *p, DWORD dwProcessId, apr_file_t *child_in){ apr_status_t rv; int lcnt = 0; ap_listen_rec *lr; LPWSAPROTOCOL_INFO lpWSAProtocolInfo; DWORD BytesWritten; /* Run the chain of open sockets. For each socket, duplicate it * for the target process then send the WSAPROTOCOL_INFO * (returned by dup socket) to the child. */ for (lr = ap_listeners; lr; lr = lr->next, ++lcnt) { int nsd; lpWSAProtocolInfo = apr_pcalloc(p, sizeof(WSAPROTOCOL_INFO)); apr_os_sock_get(&nsd,lr->sd); ap_log_error(APLOG_MARK, APLOG_INFO, APR_SUCCESS, ap_server_conf, "Parent: Duplicating socket %d and sending it to child process %d", nsd, dwProcessId); if (WSADuplicateSocket(nsd, dwProcessId, lpWSAProtocolInfo) == SOCKET_ERROR) { ap_log_error(APLOG_MARK, APLOG_CRIT, apr_get_netos_error(), ap_server_conf, "Parent: WSADuplicateSocket failed for socket %d. Check the FAQ.", lr->sd ); return -1; } if ((rv = apr_file_write_full(child_in, lpWSAProtocolInfo, sizeof(WSAPROTOCOL_INFO), &BytesWritten)) != APR_SUCCESS) { ap_log_error(APLOG_MARK, APLOG_CRIT, rv, ap_server_conf, "Parent: Unable to write duplicated socket %d to the child.", lr->sd ); return -1; } } ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf, "Parent: Sent %d listeners to child %d", lcnt, dwProcessId); return 0;}enum waitlist_e { waitlist_ready = 0, waitlist_term = 1};static int create_process(apr_pool_t *p, HANDLE *child_proc, HANDLE *child_exit_event, DWORD *child_pid){ /* These NEVER change for the lifetime of this parent */ static char **args = NULL; static char **env = NULL; static char pidbuf[28]; apr_status_t rv; apr_pool_t *ptemp; apr_procattr_t *attr; apr_file_t *child_out; apr_file_t *child_err; apr_proc_t new_child; HANDLE hExitEvent; HANDLE waitlist[2]; /* see waitlist_e */ char *cmd; char *cwd; apr_pool_sub_make(&ptemp, p, NULL); /* Build the command line. Should look something like this: * C:/apache/bin/apache.exe -f ap_server_confname * First, get the path to the executable... */ apr_procattr_create(&attr, ptemp); apr_procattr_cmdtype_set(attr, APR_PROGRAM); apr_procattr_detach_set(attr, 1); if (((rv = apr_filepath_get(&cwd, 0, ptemp)) != APR_SUCCESS) || ((rv = apr_procattr_dir_set(attr, cwd)) != APR_SUCCESS)) { ap_log_error(APLOG_MARK, APLOG_CRIT, rv, ap_server_conf, "Parent: Failed to get the current path"); } if (!args) { /* Build the args array, only once since it won't change * for the lifetime of this parent process. */ if ((rv = ap_os_proc_filepath(&cmd, ptemp)) != APR_SUCCESS) { ap_log_error(APLOG_MARK, APLOG_CRIT, ERROR_BAD_PATHNAME, ap_server_conf, "Parent: Failed to get full path of %s", ap_server_conf->process->argv[0]); apr_pool_destroy(ptemp); return -1; } args = malloc((ap_server_conf->process->argc + 1) * sizeof (char*)); memcpy(args + 1, ap_server_conf->process->argv + 1, (ap_server_conf->process->argc - 1) * sizeof (char*)); args[0] = malloc(strlen(cmd) + 1); strcpy(args[0], cmd); args[ap_server_conf->process->argc] = NULL; } else { cmd = args[0]; } /* Create a pipe to send handles to the child */ if ((rv = apr_procattr_io_set(attr, APR_FULL_BLOCK, APR_NO_PIPE, APR_NO_PIPE)) != APR_SUCCESS) { ap_log_error(APLOG_MARK, APLOG_CRIT, rv, ap_server_conf, "Parent: Unable to create child stdin pipe.\n"); apr_pool_destroy(ptemp); return -1; } /* Open a null handle to soak info from the child */ if (((rv = apr_file_open(&child_out, "NUL", APR_READ | APR_WRITE, APR_OS_DEFAULT, ptemp)) != APR_SUCCESS) || ((rv = apr_procattr_child_out_set(attr, child_out, NULL)) != APR_SUCCESS)) { ap_log_error(APLOG_MARK, APLOG_CRIT, rv, ap_server_conf, "Parent: Unable to connect child stdout to NUL.\n"); apr_pool_destroy(ptemp); return -1; } /* Connect the child's initial stderr to our main server error log * or share our own stderr handle. */ if (ap_server_conf->error_log) { child_err = ap_server_conf->error_log; } else { rv = apr_file_open_stderr(&child_err, ptemp); } if (rv == APR_SUCCESS) { if ((rv = apr_procattr_child_err_set(attr, child_err, NULL)) != APR_SUCCESS) { ap_log_error(APLOG_MARK, APLOG_CRIT, rv, ap_server_conf, "Parent: Unable to connect child stderr.\n"); apr_pool_destroy(ptemp); return -1; } } /* Create the child_ready_event */ waitlist[waitlist_ready] = CreateEvent(NULL, TRUE, FALSE, NULL); if (!waitlist[waitlist_ready]) { ap_log_error(APLOG_MARK, APLOG_CRIT, apr_get_os_error(), ap_server_conf, "Parent: Could not create ready event for child process"); apr_pool_destroy (ptemp); return -1; } /* Create the child_exit_event */ hExitEvent = CreateEvent(NULL, TRUE, FALSE, NULL); if (!hExitEvent) { ap_log_error(APLOG_MARK, APLOG_CRIT, apr_get_os_error(), ap_server_conf, "Parent: Could not create exit event for child process"); apr_pool_destroy(ptemp); CloseHandle(waitlist[waitlist_ready]); return -1; } if (!env) { /* Build the env array, only once since it won't change * for the lifetime of this parent process. */ int envc; for (envc = 0; _environ[envc]; ++envc) { ; } env = malloc((envc + 2) * sizeof (char*)); memcpy(env, _environ, envc * sizeof (char*)); apr_snprintf(pidbuf, sizeof(pidbuf), "AP_PARENT_PID=%i", parent_pid); env[envc] = pidbuf; env[envc + 1] = NULL; } rv = apr_proc_create(&new_child, cmd, args, env, attr, ptemp); if (rv != APR_SUCCESS) { ap_log_error(APLOG_MARK, APLOG_CRIT, rv, ap_server_conf, "Parent: Failed to create the child process."); apr_pool_destroy(ptemp); CloseHandle(hExitEvent); CloseHandle(waitlist[waitlist_ready]); CloseHandle(new_child.hproc); return -1; } ap_log_error(APLOG_MARK, APLOG_NOTICE, APR_SUCCESS, ap_server_conf, "Parent: Created child process %d", new_child.pid); if (send_handles_to_child(ptemp, waitlist[waitlist_ready], hExitEvent, start_mutex, ap_scoreboard_shm, new_child.hproc, new_child.in)) { /* * This error is fatal, mop up the child and move on * We toggle the child's exit event to cause this child * to quit even as it is attempting to start. */ SetEvent(hExitEvent); apr_pool_destroy(ptemp); CloseHandle(hExitEvent); CloseHandle(waitlist[waitlist_ready]); CloseHandle(new_child.hproc); return -1; } /* Important: * Give the child process a chance to run before dup'ing the sockets. * We have already set the listening sockets noninheritable, but if * WSADuplicateSocket runs before the child process initializes * the listeners will be inherited anyway. */ waitlist[waitlist_term] = new_child.hproc; rv = WaitForMultipleObjects(2, waitlist, FALSE, INFINITE); CloseHandle(waitlist[waitlist_ready]); if (rv != WAIT_OBJECT_0) { /* * Outch... that isn't a ready signal. It's dead, Jim! */ SetEvent(hExitEvent); apr_pool_destroy(ptemp); CloseHandle(hExitEvent); CloseHandle(new_child.hproc); return -1; } if (send_listeners_to_child(ptemp, new_child.pid, new_child.in)) { /* * This error is fatal, mop up the child and move on * We toggle the child's exit event to cause this child * to quit even as it is attempting to start. */ SetEvent(hExitEvent); apr_pool_destroy(ptemp); CloseHandle(hExitEvent); CloseHandle(new_child.hproc); return -1; } *child_exit_event = hExitEvent; *child_proc = new_child.hproc; *child_pid = new_child.pid; return 0;}/*********************************************************************** * master_main() * master_main() runs in the parent process. It creates the child * process which handles HTTP requests then waits on one of three * events: * * restart_event * ------------- * The restart event causes master_main to start a new child process and * tells the old child process to exit (by setting the child_exit_event). * The restart event is set as a result of one of the following: * 1. An apache -k restart command on the command line * 2. A command received from Windows service manager which gets * translated into an ap_signal_parent(SIGNAL_PARENT_RESTART) * call by code in service.c. * 3. The child process calling ap_signal_parent(SIGNAL_PARENT_RESTART) * as a result of hitting MaxRequestsPerChild. * * shutdown_event * -------------- * The shutdown event causes master_main to tell the child process to * exit and that the server is shutting down. The shutdown event is * set as a result of one of the following: * 1. An apache -k shutdown command on the command line * 2. A command received from Windows service manager which gets * translated into an ap_signal_parent(SIGNAL_PARENT_SHUTDOWN) * call by code in service.c. * * child process handle * -------------------- * The child process handle will be signaled if the child process * exits for any reason. In a normal running server, the signaling
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -