pcs 98/10/06 08:41:46
Modified: src/main http_main.c
Log:
WIN32: Add new options to let Apache signal itself to shutdown or
restart. The option is -k, used like this:
-k shutdown
-k restart
This lets people signal Apache on Win95 systems (where previously Apache
was controlled by doing a ^C to stop it running).
Reviewed by: Ken, Lars, Jim, Martin (all concept only)
Revision Changes Path
1.399 +242 -84 apache-1.3/src/main/http_main.c
Index: http_main.c
===================================================================
RCS file: /export/home/cvs/apache-1.3/src/main/http_main.c,v
retrieving revision 1.398
retrieving revision 1.399
diff -u -r1.398 -r1.399
--- http_main.c 1998/10/06 15:36:02 1.398
+++ http_main.c 1998/10/06 15:41:44 1.399
@@ -999,6 +999,10 @@
fprintf(stderr, " -l : list compiled-in modules\n");
fprintf(stderr, " -S : show parsed settings (currently
only vhost settings)\n");
fprintf(stderr, " -t : run syntax test for configuration
files only\n");
+#ifdef WIN32
+ fprintf(stderr, " -k shutdown : tell running Apache to
shutdown\n");
+ fprintf(stderr, " -k restart : tell running Apache to do a
graceful restart\n");
+#endif
exit(1);
}
@@ -2542,17 +2546,41 @@
#ifdef WIN32
/*
- * signal_parent() tells the parent process to wake up and do something.
- * Once woken it will look at shutdown_pending and restart_pending to decide
- * what to do. If neither variable is set, it will do a shutdown. This
function
- * if called by start_shutdown() or start_restart() in the parent's process
- * space, so that the variables get set. However it can also be called
- * by child processes to force the parent to exit in an emergency.
+ * 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
+ * called
+ *
+ * apPID_shutdown
+ * apPID_restart
+ *
+ * (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
+ * httpd.pid file.
+ *
+ * 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
+ * graceful restart.
*/
-static void signal_parent(void)
+static void signal_parent(int type)
{
HANDLE e;
+ char *signal_name;
+ extern char signal_shutdown_name[];
+ extern char signal_restart_name[];
+
/* after updating the shutdown_pending or restart flags, we need
* to wake up the parent process so it can see the changes. The
* parent will normally be waiting for either a child process
@@ -2564,21 +2592,28 @@
return;
}
- APD1("*** SIGNAL_PARENT SET ***");
+ switch(type) {
+ case 0: signal_name = signal_shutdown_name; break;
+ case 1: signal_name = signal_restart_name; break;
+ default: return;
+ }
- e = OpenEvent(EVENT_ALL_ACCESS, FALSE, "apache-signal");
+ APD2("signal_parent signalling event \"%s\"", signal_name);
+
+ e = OpenEvent(EVENT_ALL_ACCESS, FALSE, signal_name);
if (!e) {
- /* Um, problem, can't signal the main loop, which means we can't
+ /* Um, problem, can't signal the parent, which means we can't
* signal ourselves to die. Ignore for now...
*/
ap_log_error(APLOG_MARK, APLOG_EMERG|APLOG_WIN32ERROR, server_conf,
- "OpenEvent on apache-signal event");
+ "OpenEvent on %s event", signal_name);
return;
}
if (SetEvent(e) == 0) {
/* Same problem as above */
ap_log_error(APLOG_MARK, APLOG_EMERG|APLOG_WIN32ERROR, server_conf,
- "SetEvent on apache-signal event");
+ "SetEvent on %s event", signal_name);
+ CloseHandle(e);
return;
}
CloseHandle(e);
@@ -2586,24 +2621,19 @@
#endif
/*
- * start_shutdown() and start_restart(), below, are a first stab at
+ * ap_start_shutdown() and ap_start_restart(), below, are a first stab at
* functions to initiate shutdown or restart without relying on signals.
* Previously this was initiated in sig_term() and restart() signal
handlers,
* but we want to be able to start a shutdown/restart from other sources --
* e.g. on Win32, from the service manager. Now the service manager can
- * call start_shutdown() or start_restart() as appropiate.
- *
- * These should only be called from the parent process itself, since the
- * parent process will use the shutdown_pending and restart_pending variables
- * to determine whether to shutdown or restart. The child process should
- * call signal_parent() directly to tell the parent to die -- this will
- * cause neither of those variable to be set, which the parent will
- * assume means something serious is wrong (which it will be, for the
- * child to force an exit) and so do an exit anyway.
+ * call ap_start_shutdown() or ap_start_restart() as appropiate. Note that
+ * these functions can also be called by the child processes, since global
+ * variables are no longer used to pass on the required action to the parent.
*/
void ap_start_shutdown(void)
{
+#ifndef WIN32
if (shutdown_pending == 1) {
/* Um, is this _probably_ not an error, if the user has
* tried to do a shutdown twice quickly, so we won't
@@ -2612,24 +2642,23 @@
return;
}
shutdown_pending = 1;
-
-#ifdef WIN32
- signal_parent(); /* get the parent process to wake up */
+#else
+ signal_parent(0); /* get the parent process to wake up */
#endif
}
/* do a graceful restart if graceful == 1 */
void ap_start_restart(int graceful)
{
+#ifndef WIN32
if (restart_pending == 1) {
/* Probably not an error - don't bother reporting it */
return;
}
restart_pending = 1;
is_graceful = graceful;
-
-#ifdef WIN32
- signal_parent(); /* get the parent process to wake up */
+#else
+ signal_parent(1); /* get the parent process to wake up */
#endif /* WIN32 */
}
@@ -4633,11 +4662,13 @@
*
* Signalling between the parent and working process uses a Win32
* event. Each child has a unique name for the event, which is
- * passed to it with the -c argument when the child is spawned. The
+ * passed to it with the -Z argument when the child is spawned. The
* parent sets (signals) this event to tell the child to die.
* At present all children do a graceful die - they finish all
* current jobs _and_ empty the listen queue before they exit.
- * A non-graceful die would need a second event.
+ * A non-graceful die would need a second event. The -Z argument in
+ * the child is also used to create the shutdown and restart events,
+ * since the prefix (apPID) contains the parent process PID.
*
* The code below starts with functions at the lowest level -
* worker threads, and works up to the top level - the main()
@@ -5001,17 +5032,37 @@
event *exit_event;
mutex *start_mutex;
+#define MAX_SIGNAL_NAME 30 /* Long enough for apPID_shutdown, where PID is
an int */
+char signal_name_prefix[MAX_SIGNAL_NAME];
+char signal_restart_name[MAX_SIGNAL_NAME];
+char signal_shutdown_name[MAX_SIGNAL_NAME];
+
#define MAX_SELECT_ERRORS 100
+/*
+ * Initialise the signal names, in the global variables signal_name_prefix,
+ * signal_restart_name and signal_shutdown_name.
+ */
+
+void setup_signal_names(char *prefix)
+{
+ ap_snprintf(signal_name_prefix, sizeof(signal_name_prefix), prefix);
+ ap_snprintf(signal_shutdown_name, sizeof(signal_shutdown_name),
+ "%s_shutdown", signal_name_prefix);
+ ap_snprintf(signal_restart_name, sizeof(signal_restart_name),
+ "%s_restart", signal_name_prefix);
+
+ APD2("signal prefix %s", signal_name_prefix);
+}
+
+/*
+ * worker_main() is main loop for the child process. The loop in
+ * this function becomes the controlling thread for the actually working
+ * threads (which run in a loop in child_sub_main()).
+ */
+
void worker_main(void)
{
- /*
- * I am writing this stuff specifically for NT.
- * have pulled out a lot of the restart and
- * graceful restart stuff, because that is only
- * useful on Unix (not sure it even makes sense
- * in a multi-threaded env.
- */
int nthreads;
fd_set main_fds;
int srv;
@@ -5059,7 +5110,13 @@
reinit_scoreboard(pconf);
- //ap_acquire_mutex(start_mutex);
+ /*
+ * Wait until we have permission to start accepting connections.
+ * start_mutex is used to ensure that only one child ever
+ * goes into the listen/accept loop at once. Also wait on exit_event,
+ * in case we (this child) is told to die before we get a chance to
+ * serve any requests.
+ */
hObjects[0] = (HANDLE)start_mutex;
hObjects[1] = (HANDLE)exit_event;
rv = WaitForMultipleObjects(2, hObjects, FALSE, INFINITE);
@@ -5085,7 +5142,7 @@
ap_log_error(APLOG_MARK, APLOG_CRIT|APLOG_NOERRNO, NULL,
"No sockets were created for listening");
- signal_parent(); /* tell parent to die */
+ signal_parent(0); /* tell parent to die */
ap_destroy_pool(pchild);
cleanup_scoreboard();
@@ -5114,16 +5171,12 @@
/* spawn off the threads */
child_handles = (thread *) alloca(nthreads * sizeof(int));
- {
- int i;
-
- for (i = 0; i < nthreads; i++) {
- child_handles[i] = create_thread((void (*)(void *)) child_main,
(void *) i);
- }
- if (nthreads > max_daemons_limit) {
- max_daemons_limit = nthreads;
- }
+ for (i = 0; i < nthreads; i++) {
+ child_handles[i] = create_thread((void (*)(void *)) child_main, (void
*) i);
}
+ if (nthreads > max_daemons_limit) {
+ max_daemons_limit = nthreads;
+ }
while (1) {
#if SEVERELY_VERBOSE
@@ -5288,22 +5341,21 @@
clean_parent_exit(0);
} /* standalone_main */
-/* Spawn a child Apache process. The child process has the command
- * line arguments from argc and argv[], plus a -Z argument giving the
- * name of an event. The child should open and poll or wait on this
- * event. When it is signalled, the child should die. prefix is a
- * prefix string for the event name.
+/*
+ * Spawn a child Apache process. The child process has the command line
arguments from
+ * argc and argv[], plus a -Z argument giving the name of an event. The
child should
+ * open and poll or wait on this event. When it is signalled, the child
should die.
+ * prefix is a prefix string for the event name.
*
- * The child_num argument on entry contains a serial number for this
- * child (used to create a unique event name). On exit, this number
- * will have been incremented by one, ready for the next call.
+ * The child_num argument on entry contains a serial number for this child
(used to create
+ * a unique event name). On exit, this number will have been incremented by
one, ready
+ * for the next call.
*
* On exit, the value pointed to be *ev will contain the event created
* to signal the new child process.
*
- * The return value is the handle to the child process if successful,
- * else -1. If -1 is returned the error will already have been logged
- * by ap_log_error().
+ * The return value is the handle to the child process if successful, else
-1. If -1 is
+ * 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)
@@ -5311,8 +5363,15 @@
char buf[40], mod[200];
int i, rv;
char **pass_argv = (char **) alloca(sizeof(char *) * (argc + 3));
-
- ap_snprintf(buf, sizeof(buf), "%s_%d", prefix, ++(*child_num));
+
+ /* 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) {
@@ -5409,10 +5468,11 @@
int *child;
int child_num = 0;
int rv, cld;
- char buf[100];
+ char signal_prefix_string[100];
int i;
time_t tmstart;
- HANDLE signal_event; /* used to signal shutdown/restart to parent */
+ HANDLE signal_shutdown_event; /* used to signal shutdown to parent */
+ HANDLE signal_restart_event; /* used to signal a restart to parent */
HANDLE process_handles[MAX_PROCESSES];
HANDLE process_kill_events[MAX_PROCESSES];
int current_live_processes = 0; /* number of child process we know about
*/
@@ -5425,22 +5485,34 @@
is_graceful = 0;
++generation;
- signal_event = OpenEvent(EVENT_ALL_ACCESS, FALSE, "apache-signal");
- if (!signal_event) {
+ ap_snprintf(signal_prefix_string, sizeof(signal_prefix_string),
+ "ap%d", getpid());
+ setup_signal_names(signal_prefix_string);
+
+ signal_shutdown_event = CreateEvent(NULL, TRUE, FALSE,
signal_shutdown_name);
+ if (!signal_shutdown_event) {
+ ap_log_error(APLOG_MARK, APLOG_EMERG|APLOG_WIN32ERROR, server_conf,
+ "Cannot create shutdown event %s", signal_shutdown_name);
+ exit(1);
+ }
+ APD2("master_main: created event %s", signal_shutdown_name);
+ signal_restart_event = CreateEvent(NULL, TRUE, FALSE,
signal_restart_name);
+ if (!signal_restart_event) {
+ CloseHandle(signal_shutdown_event);
ap_log_error(APLOG_MARK, APLOG_EMERG|APLOG_WIN32ERROR, server_conf,
- "Cannot open apache-signal event");
+ "Cannot create restart event %s", signal_restart_name);
exit(1);
}
+ APD2("master_main: created event %s", signal_restart_name);
- sprintf(buf, "Apache%d", getpid());
- start_mutex = ap_create_mutex(buf);
+ start_mutex = ap_create_mutex(signal_prefix_string);
ev = (event **) alloca(sizeof(event *) * nchild);
child = (int *) alloca(sizeof(int) * (nchild+1));
while (processes_to_create--) {
service_set_status(SERVICE_START_PENDING);
if (create_process(process_handles, process_kill_events,
- ¤t_live_processes, &child_num, buf, argc, argv) < 0) {
+ ¤t_live_processes, &child_num, signal_prefix_string, argc,
argv) < 0) {
goto die_now;
}
}
@@ -5461,8 +5533,6 @@
ap_set_version();
ap_init_modules(pconf, server_conf);
version_locked++;
- if (!is_graceful)
- reinit_scoreboard(pconf);
restart_pending = shutdown_pending = 0;
@@ -5478,15 +5548,17 @@
ap_log_error(APLOG_MARK,APLOG_ERR|APLOG_NOERRNO, server_conf,
"master_main: no child processes alive! creating one");
if (create_process(process_handles, process_kill_events,
- ¤t_live_processes, &child_num, buf, argc, argv) < 0) {
+ ¤t_live_processes, &child_num, signal_prefix_string,
+ argc, argv) < 0) {
goto die_now;
}
if (processes_to_create) {
processes_to_create--;
}
}
- process_handles[current_live_processes] = signal_event;
- rv = WaitForMultipleObjects(current_live_processes+1, (HANDLE
*)process_handles,
+ process_handles[current_live_processes] = signal_shutdown_event;
+ process_handles[current_live_processes+1] = signal_restart_event;
+ rv = WaitForMultipleObjects(current_live_processes+2, (HANDLE
*)process_handles,
FALSE, INFINITE);
if (rv == WAIT_FAILED) {
/* Something serious is wrong */
@@ -5494,14 +5566,36 @@
"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");
+ shutdown_pending = 1;
}
- ap_assert(rv != WAIT_TIMEOUT);
+
cld = rv - WAIT_OBJECT_0;
APD4("main process: wait finished, cld=%d handle %d (max=%d)", cld,
process_handles[cld], current_live_processes);
if (cld == current_live_processes) {
- /* stop_event is signalled, we should exit now */
- if (ResetEvent(signal_event) == 0)
- APD1("main process: *** ERROR: ResetEvent(stop_event)
failed ***");
+ /* shutdown event signalled, we should exit now */
+ if (ResetEvent(signal_shutdown_event) == 0) {
+ ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_WIN32ERROR,
server_conf,
+ "ResetEvent(signal_shutdown_event)");
+ /* Continue -- since we are doing a shutdown anyway */
+ }
+ shutdown_pending = 1;
+ APD3("main process: stop_event signalled: shutdown_pending=%d,
restart_pending=%d",
+ shutdown_pending, restart_pending);
+ break;
+ }
+ if (cld == current_live_processes+1) {
+ /* restart event signalled, we should exit now */
+ if (ResetEvent(signal_restart_event) == 0) {
+ ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_WIN32ERROR,
server_conf,
+ "ResetEvent(signal_restart_event)");
+ /* Continue -- hopefully the restart will fix the problem */
+ }
+ restart_pending = 1;
APD3("main process: stop_event signalled: shutdown_pending=%d,
restart_pending=%d",
shutdown_pending, restart_pending);
break;
@@ -5510,7 +5604,8 @@
cleanup_process(process_handles, process_kill_events, cld,
¤t_live_processes);
APD2("main_process: child in slot %d died", rv);
if (processes_to_create) {
- create_process(process_handles, process_kill_events,
¤t_live_processes, &child_num, buf, argc, argv);
+ create_process(process_handles, process_kill_events,
¤t_live_processes,
+ &child_num, signal_prefix_string, argc, argv);
processes_to_create--;
}
}
@@ -5527,7 +5622,6 @@
ap_log_error(APLOG_MARK,APLOG_WIN32ERROR, server_conf,
"SetEvent for child process in slot #%d", i);
}
-
break;
}
if (restart_pending) {
@@ -5539,7 +5633,8 @@
for (i = 0; i < nchild; ++i) {
if (current_live_processes >= MAX_PROCESSES)
break;
- create_process(process_handles, process_kill_events,
¤t_live_processes, &child_num, buf, argc, argv);
+ create_process(process_handles, process_kill_events,
¤t_live_processes,
+ &child_num, signal_prefix_string, argc, argv);
processes_to_create--;
}
for (i = 0; i < children_to_kill; i++) {
@@ -5559,7 +5654,8 @@
APD2("*** main process shutdown, processes=%d ***",
current_live_processes);
die_now:
- CloseHandle(signal_event);
+ CloseHandle(signal_restart_event);
+ CloseHandle(signal_shutdown_event);
tmstart = time(NULL);
while (current_live_processes && ((tmstart+60) > time(NULL))) {
@@ -5596,10 +5692,61 @@
}
ap_destroy_mutex(start_mutex);
+
service_set_status(SERVICE_STOPPED);
return (0);
}
+/*
+ * Send signal to a running Apache. On entry signal should contain
+ * either "shutdown" or "restart"
+ */
+
+void send_signal(pool *p, char *signal)
+{
+ char prefix[20];
+ FILE *fp;
+ int nread;
+ char *fname;
+ int end;
+
+ fname = ap_server_root_relative (p, ap_pid_fname);
+
+ fp = fopen(fname, "r");
+ if (!fp) {
+ printf("Cannot read apache PID file %s\n", fname);
+ return;
+ }
+ prefix[0] = 'a';
+ prefix[1] = 'p';
+
+ nread = fread(prefix+2, 1, sizeof(prefix)-3, fp);
+ if (nread == 0) {
+ fclose(fp);
+ printf("PID file %s was empty\n", fname);
+ return;
+ }
+ fclose(fp);
+
+ /* Terminate the prefix string */
+ end = 2 + nread - 1;
+ while (end > 0 && (prefix[end] == '\r' || prefix[end] == '\n'))
+ end--;
+ prefix[end + 1] = '\0';
+
+ setup_signal_names(prefix);
+
+ if (!strcasecmp(signal, "shutdown"))
+ ap_start_shutdown();
+ else if (!strcasecmp(signal, "restart"))
+ ap_start_restart(1);
+ else
+ printf("Unknown signal name \"%s\". Use either shutdown or restart.\n",
+ signal);
+
+ return;
+}
+
#ifdef WIN32
__declspec(dllexport)
int apache_main(int argc, char *argv[])
@@ -5613,6 +5760,7 @@
int run_as_service = 1;
int install = 0;
int configtestonly = 0;
+ char *signal_to_send = NULL;
common_init();
@@ -5637,7 +5785,7 @@
ap_setup_prelinked_modules();
- while ((c = getopt(argc, argv, "D:C:c:Xd:f:vVhlZ:iusSt")) != -1) {
+ while ((c = getopt(argc, argv, "D:C:c:Xd:f:vVhlZ:iusStk:")) != -1) {
char **new;
switch (c) {
case 'c':
@@ -5659,7 +5807,9 @@
cp = strchr(optarg, '_');
ap_assert(cp);
*cp = 0;
- start_mutex = ap_open_mutex(optarg);
+ setup_signal_names(optarg);
+ start_mutex = ap_open_mutex(signal_name_prefix);
+ ap_assert(start_mutex);
child = 1;
break;
case 'i':
@@ -5674,6 +5824,9 @@
case 'S':
ap_dump_settings = 1;
break;
+ case 'k':
+ signal_to_send = optarg;
+ break;
#endif /* WIN32 */
case 'd':
ap_cpystrn(ap_server_root, ap_os_canonical_filename(pconf, optarg),
sizeof(ap_server_root));
@@ -5716,6 +5869,11 @@
if (configtestonly) {
fprintf(stderr, "Syntax OK\n");
exit(0);
+ }
+
+ if (signal_to_send) {
+ send_signal(pconf, signal_to_send);
+ exit(0);
}
if (!child && !ap_dump_settings && !install) {