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++) {