mpm_winnt.c revision 820e91baab4f9a45001d668698d2fae3501cb4b0
* Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. /* scoreboard.c does the heavy lifting; all we do is create the child * score by moving a handle down the pipe into the child's stdin. /* my_generation is returned to the scoreboard code */ /* Definitions of WINNT MPM specific config globals */ /* set by child_main to STACK_SIZE_PARAM_IS_A_RESERVATION for NT >= 5.1 (XP/2003) */ /* used by parent to signal the child to start and exit */ * perhaps it should be private. /* Only one of these, the pipe from our parent, meant only for * one child worker's consumption (not to be inherited!) * XXX: decorate this name for the trunk branch, was left simplified * only to make the 2.2 patch trivial to read. /* Stub functions until this MPM supports the connection status API */ "Number of threads each child creates" ),
"Maximum worker threads in a server for this run of Apache"),
* Signalling Apache on NT. * Under Unix, Apache can be told to shutdown or restart by sending various * signals (HUP, USR, TERM). On NT we don't have easy access to signals, so * we use "events" instead. The parent apache process goes into a loop * where it waits forever for a set of events. Two of those events are * (where PID is the PID of the apache parent process). When one of these * is signalled, the Apache parent performs the appropriate action. The events * can become signalled through internal Apache methods (e.g. if the child * finds a fatal error and needs to kill its parent), via the service * control manager (the control thread will signal the shutdown event when * requested to stop the Apache service), from the -k Apache command line, * or from any external program which finds the Apache PID from the * The signal_parent() function, below, is used to signal one of these events. * It can be called by any child or parent process, since it does not * rely on global variables. * On entry, type gives the event to signal. 0 means shutdown, 1 means * Initialise the signal names, in the global variables signal_name_prefix, * signal_restart_name and signal_shutdown_name. #
define MAX_SIGNAL_NAME 30 /* Long enough for apPID_shutdown, where PID is an int */ /* This MPM supports only graceful restarts right now */ /* This MPM supports only graceful restarts right now */ /* Um, problem, can't signal the parent, which means we can't * signal ourselves to die. Ignore for now... /* Same problem as above */ * Passed the following handles [in sync with send_handles_to_child()] * ready event [signal the parent immediately, then close] * exit event [save to poll later] * start mutex [signal from the parent to begin accept()] * scoreboard shm handle [to recreate the ap_scoreboard] /* *** We now do this was back in winnt_rewrite_args * pipe = GetStdHandle(STD_INPUT_HANDLE); "Child %d: Unable to retrieve the ready event from the parent",
my_pid);
"Child %d: Unable to retrieve the exit event from the parent",
my_pid);
"Child %d: Unable to retrieve the start_mutex from the parent",
my_pid);
"Child %d: Unable to access the start_mutex from the parent",
my_pid);
"Child %d: Unable to retrieve the scoreboard from the parent",
my_pid);
"Child %d: Unable to access the scoreboard from the parent",
my_pid);
"Child %d: Unable to reopen the scoreboard from the parent",
my_pid);
/* We must 'initialize' the scoreboard to relink all the * process-local pointer arrays into the shared memory block. "Child %d: Retrieved our scoreboard from the parent.",
my_pid);
"Parent: Unable to duplicate the ready event handle for the child");
"Parent: Unable to send the exit event handle to the child");
"Parent: Unable to duplicate the exit event handle for the child");
"Parent: Unable to send the exit event handle to the child");
"Parent: Unable to retrieve the start mutex for the child");
"Parent: Unable to duplicate the start mutex to the child");
"Parent: Unable to send the start mutex to the child");
"Parent: Unable to retrieve the scoreboard handle for the child");
"Parent: Unable to duplicate the scoreboard handle to the child");
"Parent: Unable to send the scoreboard handle to the child");
"Parent: Sent the scoreboard to the child");
* 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. /* Set up a default listener if necessary */ /* Open the pipe to the parent process to receive the inherited socket * data. The sockets have been set to listening in the parent process. * *** We now do this was back in winnt_rewrite_args * pipe = GetStdHandle(STD_INPUT_HANDLE); "setup_inherited_listeners: Unable to read socket data from parent");
"Child %d: setup_inherited_listeners(), WSASocket failed to open the inherited socket.",
my_pid);
"set_listeners_noninheritable: SetHandleInformation failed.");
"Child %d: retrieved %d listeners from parent",
my_pid,
lcnt);
/* 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. "Parent: Duplicating socket %d and sending it to child process %d",
"Parent: WSADuplicateSocket failed for socket %d. Check the FAQ.",
lr->
sd );
"Parent: Unable to write duplicated socket %d to the child.",
lr->
sd );
/* These NEVER change for the lifetime of this parent /* Build the command line. Should look something like this: * First, get the path to the executable... "Parent: Failed to get the current path");
/* Build the args array, only once since it won't change * for the lifetime of this parent process. "Parent: Failed to get full path of %s",
/* Create a pipe to send handles to the child */ "Parent: Unable to create child stdin pipe.");
/* Create the child_ready_event */ "Parent: Could not create ready event for child process");
/* Create the child_exit_event */ "Parent: Could not create exit event for child process");
/* Build the env array */ "Parent: Failed to create the child process.");
* 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. * 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. * Outch... that isn't a ready signal. It's dead, Jim! * 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. /*********************************************************************** * master_main() runs in the parent process. It creates the child * process which handles HTTP requests then waits on one of three * 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) * 3. The child process calling ap_signal_parent(SIGNAL_PARENT_RESTART) * as a result of hitting MaxRequestsPerChild. * 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) * The child process handle will be signaled if the child process * exits for any reason. In a normal running server, the signaling * 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 * master_main uses the child_exit_event to signal the child process **********************************************************************/ /* Create a single child process */ "master_main: create child process failed. Exiting.");
/* Update the scoreboard. Note that there is only a single active /* Wait for shutdown or restart events or for child death */ /* Something serious is wrong */ "master_main: WaitForMultipeObjects WAIT_FAILED -- doing server shutdown");
/* Hey, this cannot happen */ "master_main: WaitForMultipeObjects with INFINITE wait exited with WAIT_TIMEOUT");
/* shutdown_event signalled */ "Parent: Received shutdown signal -- Shutting down the server.");
"ResetEvent(shutdown_event)");
/* Received a restart event. Prepare the restart_event to be reused * then signal the child process to exit. "Parent: Received restart signal -- Restarting the server.");
"Parent: ResetEvent(restart_event) failed.");
"Parent: SetEvent for child process %d failed.",
/* Don't wait to verify that the child process really exits, * just move on with the restart. /* The child process exited prematurely due to a fatal error. */ /* HUH? We did exit, didn't we? */ "Parent: child process exited with status %u -- Aborting.",
exitcode);
"Parent: child process exited with status %u -- Restarting.",
exitcode);
int timeout =
30000;
/* Timeout is milliseconds */ /* 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. /* Signal the child processes to exit */ "Parent: Child process exited successfully.");
return 0;
/* Tell the caller we do not want to restart */ 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... /* 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. /* Handle the following SCM aspects in this phase: * -k runservice [transition in service context only] * -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. /* We wish this was *always* a reservation, but sadly it wasn't so and * we couldn't break a hard limit prior to NT Kernel 5.1 /* AP_PARENT_PID is only valid in the child */ /* Prevent holding open the (nonexistant) console */ /* The parent gave us stdin, we need to remember this * handle, and no longer inherit it at our children * (we can't slurp it up now, we just aren't ready yet). * The original handle is closed below, at apr_file_dup2() /* The parent gave us stdout of the NUL device, * and expects us to suck up stdin of all of our * shared handles and data from the parent. * Don't infect child processes with our stdin * handle, use another handle to NUL! /* This child needs the existing stderr opened for logging, /* 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. /* This is the parent, we have a long way to go :-) */ /* This behavior is voided by setting real_exit_code to 0 */ /* 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 "Failed to get the full path of %s",
process->
argv[0]);
/* WARNING: There is an implict assumption here that the * executable resides in ServerRoot or ServerRoot\bin /* 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. /* Shortcuts; include the -w option to hold the window open on error. * This must not be toggled once we reset ap_real_exit_code to 0! /* Fall through so the Apache main() handles the 'E' arg */ /* back up to capture the bad argument */ /* Track the number of args actually entered by the user */ /* Provide a default 'run' -k arg to simplify signal_arg tests */ /* 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 * 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. /* 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. /* Open a null handle to soak stdout in this process. * Windows service processes are missing any file handle * usable for stdin/out/err. This was the cause of later * trouble with invocations of apr_file_open_stdout() /* Get the default for any -k option, except run */ /* Attempt to Uninstall, or stop, before * we can read the arguments or .conf files "Using ConfigArgs of the installed service " "No installed ConfigArgs for the service " /* Track the args actually entered by the user. * These will be used for the -k install parameters, as well as * for the -k start service override arguments. /* Now, do service install or reconfigure then proceed to * post_config to test the installed configuration. /* Reconfigure the service */ fprintf(
stderr,
"Errors reported here must be corrected before the " "service can be started.\n");
/* Install the service */ fprintf(
stderr,
"Errors reported here must be corrected before the " "service can be started.\n");
/* Handle the following SCM aspects in this phase: * -k runservice [WinNT errors logged from rewrite_args] /* Initialize shared static objects. * TODO: Put config related statics into an sconf structure. /* XXX: presume proper privilages; one nice thing would be * a loud emit if running as "LocalSystem"/"SYSTEM" to indicate * they should change to a user with write access to logs/ alone. "%s: Unable to start the service manager.",
/* Open a null handle to soak stdout in this process. * We need to emulate apr_proc_detach, unix performs this * same check in the pre_config hook (although it is * arguably premature). Services already fixed this. /* We want this only in the parent and only the first time around */ "WARNING: ThreadLimit of %d exceeds compile-time " " %d threads, decreasing to %d.",
"ThreadLimit of %d exceeds compile-time limit " "of %d, decreasing to match",
"WARNING: ThreadLimit of %d not allowed, " "ThreadLimit of %d not allowed, increasing to 1",
/* You cannot change ThreadLimit across a restart; ignore /* Don't need a startup console version here */ "changing ThreadLimit to %d from original value " "of %d not allowed during restart",
"WARNING: ThreadsPerChild of %d exceeds ThreadLimit " " %d threads, decreasing to %d.",
" To increase, please see the ThreadLimit " "ThreadsPerChild of %d exceeds ThreadLimit " "of %d, decreasing to match",
"WARNING: ThreadsPerChild of %d not allowed, " "ThreadsPerChild of %d not allowed, increasing to 1",
/* Handle the following SCM aspects in this phase: * -k install (catch and exit as install was handled in rewrite_args) * -k config (catch and exit as config was handled in rewrite_args) * -k runservice [Win95, only once - after we parsed the config] * because all of these signals are useful _only_ if there * is a valid conf\httpd.conf environment to start. * We reached this phase by avoiding errors that would cause * these options to fail unexpectedly in another process. /* Service install happens in the rewrite_args hooks. If we * made it this far, the server configuration is clean and the * service will successfully start. /* Service reconfiguration happens in the rewrite_args hooks. If we * made it this far, the server configuration is clean and the * service will successfully start. /* Close the listening sockets. */ /* This code should be run once in the parent and not run /* Create shutdown event, apPID_shutdown, where PID is the parent * Apache process ID. Shutdown is signaled by 'apache -k shutdown'. /* Create restart event, apPID_restart, where PID is the parent * Apache process ID. Restart is signaled by 'apache -k restart'. /* Create the start mutex, as an unnamed object for security. * Ths start mutex is used during a restart to prevent more than * one child process from entering the accept loop at once. "%s: Unable to create the start_mutex.",
/* Always reset our console handler to be the first, even on a restart * because some modules (e.g. mod_perl) might have set a console * handler to terminate the process. else /* parent_pid != my_pid */ /* This really should be a post_config hook, but the error log is already * redirected by that point, so we need to do this in the open_logs phase. /* Initialize shared static objects. /* We cannot initialize our listeners if we are restarting * (the parent process already has glomed on to them) * nor should we do so for service reconfiguration * (since the service may already be running.) NULL,
"no listening sockets available, shutting down");
/* This is a child process, not in single process mode */ /* Set up events and the scoreboard */ /* Set up the listeners */ /* Done reading from the parent, close that channel */ /* Single process mode - this lock doesn't even need to exist */ "%s child %d: Unable to init the start_mutex.",
/* Borrow the shutdown_even as our _child_ loop exit event */ static int restart = 0;
/* Default is "not a restart" */ /* ### If non-graceful restarts are ever introduced - we need to rerun * the pre_mpm hook on subsequent non-graceful restarts. But Win32 * has only graceful style restarts - and we need this hook to act * the same on Win32 as on Unix. /* Set up the scoreboard. */ /* The child process or in one_process (debug) mode "Child %d: Child process is running",
my_pid);
"Child %d: Child process is exiting",
my_pid);
/* A real-honest to goodness parent */ "%s configured -- resuming normal operations",
/* Shutting down. Clean up... */ /* Our open_logs hook function must run before the core's, or stderr * will be redirected to a file, and the messages won't print to the NULL,
/* create per-directory config structure */ NULL,
/* merge per-directory config structures */ NULL,
/* create per-server config structure */ NULL,
/* merge per-server config structures */