stoddard 99/07/23 08:31:26
Modified: src CHANGES src/main http_main.c Log: Complete apache -k restart work. Now, restarts are honored immediately without loosing connections in the listen queue. Revision Changes Path 1.1398 +10 -1 apache-1.3/src/CHANGES Index: CHANGES =================================================================== RCS file: /home/cvs/apache-1.3/src/CHANGES,v retrieving revision 1.1397 retrieving revision 1.1398 diff -u -r1.1397 -r1.1398 --- CHANGES 1999/07/19 10:15:53 1.1397 +++ CHANGES 1999/07/23 15:31:21 1.1398 @@ -1,5 +1,14 @@ Changes with Apache 1.3.7 + *) Win32: More apache -k restart work. Restarts are now honored + immediately and connections in the listen queue are -not- lost. + This is made possible by the use of the WSADuplicateSocket() + call. The listeners are opened in the parent, duplicated, then + the duplicates are passed to the child. The original listen sockets + are not closed by the parent across a restart, thus the listen queue + is preserved. + [Bill Stoddard <[EMAIL PROTECTED]>] + *) Fix handling of case when a client has sent "Expect: 100-continue" and we are going to respond with an error, but get stuck waiting to discard the body in the pointless hope of preserving the connection. @@ -70,7 +79,7 @@ could take hours. Now, a restart is honored almost immediately. All connections in Apache's queues are handled but connections in the stack's listen queue are discarded. Restart triggered by - MaxRequestPerChild is unchanged. + MaxRequestPerChild is unchanged. [Bill Stoddard <[EMAIL PROTECTED]>] *) Win32: Eliminated unnecessary call to wait_for_multiple_objects in 1.458 +200 -81 apache-1.3/src/main/http_main.c Index: http_main.c =================================================================== RCS file: /home/cvs/apache-1.3/src/main/http_main.c,v retrieving revision 1.457 retrieving revision 1.458 diff -u -r1.457 -r1.458 --- http_main.c 1999/07/21 12:23:19 1.457 +++ http_main.c 1999/07/23 15:31:25 1.458 @@ -5363,6 +5363,71 @@ APD2("signal prefix %s", signal_name_prefix); } +static void setup_inherited_listeners(pool *p) +{ + HANDLE pipe; + listen_rec *lr; + int fd; + WSAPROTOCOL_INFO WSAProtocolInfo; + DWORD BytesRead; + + /* 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); + + /* Setup the listeners */ + listenmaxfd = -1; + FD_ZERO(&listenfds); + lr = ap_listeners; + + FD_ZERO(&listenfds); + + for (;;) { + fd = find_listener(lr); + if (fd < 0) { + if (!ReadFile(pipe, + &WSAProtocolInfo, sizeof(WSAPROTOCOL_INFO), + &BytesRead, + (LPOVERLAPPED) NULL)){ + ap_log_error(APLOG_MARK, APLOG_WIN32ERROR|APLOG_CRIT, server_conf, + "setup_inherited_listeners: Unable to read socket data from parent"); + exit(1); + } + fd = WSASocket(FROM_PROTOCOL_INFO, + FROM_PROTOCOL_INFO, + FROM_PROTOCOL_INFO, + &WSAProtocolInfo, + 0, + 0); + if (fd == INVALID_SOCKET) { + ap_log_error(APLOG_MARK, APLOG_WIN32ERROR|APLOG_CRIT, server_conf, + "setup_inherited_listeners: WSASocket failed to get inherit the socket."); + exit(1); + } + APD2("setup_inherited_listeners: WSASocket() returned socket %d", fd); + } + else { + ap_note_cleanups_for_socket(p, fd); + } + if (fd >= 0) { + FD_SET(fd, &listenfds); + if (fd > listenmaxfd) + listenmaxfd = fd; + } + lr->fd = fd; + if (lr->next == NULL) + break; + lr = lr->next; + } + /* turn the list into a ring */ + lr->next = ap_listeners; + head_listener = ap_listeners; + close_unused_listeners(); + CloseHandle(pipe); + return; +} + /* * worker_main() is main loop for the child process. The loop in * this function becomes the controlling thread for the actually working @@ -5443,8 +5508,13 @@ exit(0); } /* start_mutex obtained, continue into the select() loop */ + if (one_process) { + setup_listeners(pconf); + } else { + /* Get listeners from the parent process */ + setup_inherited_listeners(pconf); + } - setup_listeners(pconf); if (listenmaxfd == -1) { /* Help, no sockets were made, better log something and exit */ ap_log_error(APLOG_MARK, APLOG_CRIT|APLOG_NOERRNO, NULL, @@ -5531,7 +5601,7 @@ if (errno != EINTR) { /* A "real" error occurred, log it and increment the count of * select errors. This count is used to ensure we don't go into - * a busy loop of continuour errors. + * a busy loop of continuous errors. */ ap_log_error(APLOG_MARK, APLOG_ERR, server_conf, "select: (listen)"); count_select_errors++; @@ -5655,69 +5725,6 @@ * returned the error will already have been logged by ap_log_error(). */ -int create_event_and_spawn(int argc, char **argv, event **ev, int *child_num, char *prefix) -{ - char buf[40], mod[200]; - int i, rv; - -#ifdef WIN32 -#define NUMCHILDARGS 4 -#else -#define NUMCHILDARGS 2 -#endif - char **pass_argv = (char **) alloca(sizeof(char *) * (argc + NUMCHILDARGS + 1)); - - /* We need an event to tell the child process to kill itself when - * the parent is doing a shutdown/restart. This will be named - * apPID_CN where PID is the parent Apache process PID and - * N is a unique child serial number. prefix contains - * the "apPID" part. The child will get the name of this - * event as its -Z command line argument. - */ - ap_snprintf(buf, sizeof(buf), "%s_C%d", prefix, ++(*child_num)); - _flushall(); - *ev = CreateEvent(NULL, TRUE, FALSE, buf); - if (!*ev) { - ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_WIN32ERROR, NULL, - "could not create event for child process"); - return -1; - } - APD2("create_event_and_spawn(): created process kill event %s", buf); - - pass_argv[0] = argv[0]; - pass_argv[1] = "-Z"; - pass_argv[2] = buf; -#ifdef WIN32 - pass_argv[3] = "-f"; - pass_argv[4] = ap_server_confname; -#endif - for (i = 1; i < argc; i++) { - pass_argv[i + NUMCHILDARGS] = argv[i]; - } - pass_argv[argc + NUMCHILDARGS] = NULL; - - rv = GetModuleFileName(NULL, mod, sizeof(mod)); - if (rv == sizeof(mod)) { - /* mod[] was not big enough for our pathname */ - ap_log_error(APLOG_MARK, APLOG_ERR, NULL, - "Internal error: path to Apache process too long"); - return -1; - } - if (rv == 0) { - ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_WIN32ERROR, NULL, - "GetModuleFileName() for current process"); - return -1; - } - rv = spawnv(_P_NOWAIT, mod, pass_argv); - if (rv == -1) { - ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_WIN32ERROR, NULL, - "spawn of child process %s failed", mod); - return -1; - } - - return rv; -} - /********************************************************************** * master_main - this is the parent (main) process. We create a * child process to do the work, then sit around waiting for either @@ -5748,23 +5755,133 @@ APD4("cleanup_processes: removed child in slot %d handle %d, max=%d", position, handle, *processes); } -static int create_process(HANDLE *handles, HANDLE *events, +static int create_process(pool *p, HANDLE *handles, HANDLE *events, int *processes, int *child_num, char *kill_event_name, int argc, char **argv) { - int i = *processes; + + int rv, i; HANDLE kill_event; - int child_handle; + char buf[1024]; + char exit_event_name[40]; /* apPID_C# */ + char *pCommand; + + STARTUPINFO si; /* Filled in prior to call to CreateProcess */ + PROCESS_INFORMATION pi; /* filled in on call to CreateProces */ + LPWSAPROTOCOL_INFO lpWSAProtocolInfo; + listen_rec *lr; + DWORD BytesWritten; + HANDLE hPipeRead = NULL; + HANDLE hPipeWrite = NULL; + SECURITY_ATTRIBUTES sa = {0}; + + sa.nLength = sizeof(sa); + sa.bInheritHandle = TRUE; + sa.lpSecurityDescriptor = NULL; + + /* Build the command line. Should look something like this: + * C:/apache/bin/apache.exe -Z exit_event -f ap_server_confname + * First, get the path to the executable... + */ + rv = GetModuleFileName(NULL, buf, sizeof(buf)); + if (rv == sizeof(buf)) { + ap_log_error(APLOG_MARK, APLOG_WIN32ERROR | APLOG_CRIT, server_conf, + "Parent: Path to Apache process too long"); + return -1; + } else if (rv == 0) { + ap_log_error(APLOG_MARK, APLOG_WIN32ERROR | APLOG_CRIT, server_conf, + "Parent: GetModuleFileName() returned NULL for current process."); + return -1; + } + + /* Create the exit event (apPID_C#). Parent signals this event to tell the + * child to exit + */ + ap_snprintf(exit_event_name, sizeof(exit_event_name), "%s_C%d", kill_event_name, ++(*child_num)); + kill_event = CreateEvent(NULL, TRUE, FALSE, exit_event_name); + if (!kill_event) { + ap_log_error(APLOG_MARK, APLOG_WIN32ERROR | APLOG_CRIT, server_conf, + "Parent: Could not create exit event for child process"); + return -1; + } + + pCommand = ap_psprintf(p, "%s -Z %s -f %s", buf, exit_event_name, ap_server_confname); - child_handle = create_event_and_spawn(argc, argv, &kill_event, child_num, kill_event_name); - if (child_handle <= 0) { - return -1; - } - handles[i] = (HANDLE)child_handle; - events[i] = kill_event; - (*processes)++; + for (i = 1; i < argc; i++) { + pCommand = ap_pstrcat(p, pCommand, " ", argv[i], NULL); + } + + /* Create a pipe to send socket info to the child */ + if (!CreatePipe(&hPipeRead, &hPipeWrite, &sa, 0)) { + ap_log_error(APLOG_MARK, APLOG_WIN32ERROR | APLOG_CRIT, server_conf, + "Parent: Unable to create pipe to child process.\n"); + return -1; + } + + /* Give the read in of teh pipe (hPipeRead) to the child as stdin. The + * parent will write the socket data to the child on this pipe. + */ + memset(&si, 0, sizeof(si)); + memset(&pi, 0, sizeof(pi)); + si.cb = sizeof(si); + si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES; + si.wShowWindow = SW_HIDE; + si.hStdInput = hPipeRead; + /* + si.hStdOutput = NULL; + si.hStdError = NULL; + */ + + if (!CreateProcess(NULL, pCommand, NULL, NULL, + TRUE, /* Inherit handles */ + 0, /* Creation flags */ + NULL, NULL, + &si, &pi)) { + ap_log_error(APLOG_MARK, APLOG_WIN32ERROR | APLOG_CRIT, server_conf, + "Parent: Not able to create the child process."); + /* + * We must close the handles to the new process and its main thread + * to prevent handle and memory leaks. + */ + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); + + return -1; + } + else { + /* Assume the child process lives. Update the process and event tables */ + handles[*processes] = pi.hProcess; + events[*processes] = kill_event; + (*processes)++; + + /* 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 */ + lr = ap_listeners; + while (lr != NULL) { + lpWSAProtocolInfo = ap_pcalloc(p, sizeof(WSAPROTOCOL_INFO)); + APD2("Parent: Duplicating socket %d and sending it to the child process.", lr->fd); + if (WSADuplicateSocket(lr->fd, + pi.dwProcessId, + lpWSAProtocolInfo) == SOCKET_ERROR) { + ap_log_error(APLOG_MARK, APLOG_WIN32ERROR | APLOG_CRIT, server_conf, + "Parent: WSADuplicateSocket failed for socket %d.", lr->fd ); + return -1; + } + + if (!WriteFile(hPipeWrite, lpWSAProtocolInfo, (DWORD) sizeof(WSAPROTOCOL_INFO), + &BytesWritten, + (LPOVERLAPPED) NULL)) { + ap_log_error(APLOG_MARK, APLOG_WIN32ERROR | APLOG_CRIT, server_conf, + "Parent: Unable to write duplicated socket %d to the child.", lr->fd ); + return -1; + } - APD4("create_process: created child in slot %d handle %d, max=%d", - (*processes)-1, handles[(*processes)-1], *processes); + lr = lr->next; + if (lr == ap_listeners) + break; + } + } + CloseHandle(hPipeWrite); return 0; } @@ -5872,10 +5989,12 @@ if (!is_graceful) { ap_restart_time = time(NULL); } + copy_listeners(pconf); ap_clear_pool(pconf); pparent = ap_make_sub_pool(pconf); server_conf = ap_read_config(pconf, pparent, ap_server_confname); + setup_listeners(pconf); ap_clear_pool(plog); ap_open_logs(server_conf, plog); ap_set_version(); @@ -5884,7 +6003,7 @@ service_set_status(SERVICE_START_PENDING); /* Create child processes */ while (processes_to_create--) { - if (create_process(process_handles, process_kill_events, + if (create_process(pconf, process_handles, process_kill_events, ¤t_live_processes, &child_num, signal_prefix_string, argc, argv) < 0) { ap_log_error(APLOG_MARK, APLOG_ERR, server_conf, "master_main: create child process failed. Exiting."); @@ -5902,14 +6021,14 @@ if (rv == WAIT_FAILED) { /* Something serious is wrong */ ap_log_error(APLOG_MARK,APLOG_CRIT|APLOG_WIN32ERROR, server_conf, - "WaitForMultipeObjects on process handles and apache-signal -- doing shutdown"); + "master_main: : WaitForMultipeObjects on process handles and apache-signal -- doing shutdown"); shutdown_pending = 1; break; } if (rv == WAIT_TIMEOUT) { /* Hey, this cannot happen */ ap_log_error(APLOG_MARK, APLOG_ERR, server_conf, - "WaitForMultipeObjects with INFINITE wait exited with WAIT_TIMEOUT"); + "master_main: WaitForMultipeObjects with INFINITE wait exited with WAIT_TIMEOUT"); shutdown_pending = 1; } @@ -5940,7 +6059,7 @@ "master_main: Restart event signaled. Doing a graceful restart."); if (ResetEvent(signal_restart_event) == 0) { ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_WIN32ERROR, server_conf, - "ResetEvent(signal_restart_event) failed."); + "master_main: ResetEvent(signal_restart_event) failed."); } /* Signal each child process to die */ for (i = 0; i < children_to_kill; i++) {