summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorWilliam A. Rowe Jr <wrowe@apache.org>2000-05-31 17:15:31 +0200
committerWilliam A. Rowe Jr <wrowe@apache.org>2000-05-31 17:15:31 +0200
commitce6df54a157b2172dcd71809c3b70a8e192723e3 (patch)
tree9283658cc934f7a9f90b93b0532577d0b113a883
parentRework DSO error reporting to be more flexible & informative. (diff)
downloadapache2-ce6df54a157b2172dcd71809c3b70a8e192723e3.tar.xz
apache2-ce6df54a157b2172dcd71809c3b70a8e192723e3.zip
PR:
Obtained from: Submitted by: Reviewed by: First checkin of Win32 service control structure merged into the mpm. Project files to be updated with this 'experimental' implementation once peer review is complete and the module maintainer accepts the patch. git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@85358 13f79535-47bb-0310-9956-ffa450edef68
-rw-r--r--server/mpm/winnt/mpm_winnt.c527
-rw-r--r--server/mpm/winnt/mpm_winnt.h92
-rw-r--r--server/mpm/winnt/registry.c659
-rw-r--r--server/mpm/winnt/service.c1125
4 files changed, 1505 insertions, 898 deletions
diff --git a/server/mpm/winnt/mpm_winnt.c b/server/mpm/winnt/mpm_winnt.c
index 9a110cf27a..e9bb8abf09 100644
--- a/server/mpm/winnt/mpm_winnt.c
+++ b/server/mpm/winnt/mpm_winnt.c
@@ -57,14 +57,14 @@
*/
#define CORE_PRIVATE
-#include "ap_config.h"
#include "httpd.h"
-#include "apr_portable.h"
#include "http_main.h"
#include "http_log.h"
#include "http_config.h" /* for read_config */
#include "http_core.h" /* for get_remote_host */
#include "http_connection.h"
+#include "apr_portable.h"
+#include "apr_getopt.h"
#include "ap_mpm.h"
#include "ap_config.h"
#include "ap_listen.h"
@@ -83,6 +83,11 @@ static int max_requests_per_child = 0;
static HANDLE shutdown_event; /* used to signal shutdown to parent */
static HANDLE restart_event; /* used to signal a restart to parent */
+#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];
+
static struct fd_set listenfds;
static int num_listenfds = 0;
static SOCKET listenmaxfd = INVALID_SOCKET;
@@ -95,8 +100,9 @@ static server_rec *server_conf;
static HANDLE AcceptExCompPort = NULL;
static int one_process = 0;
+static char *signal_arg;
-static OSVERSIONINFO osver; /* VER_PLATFORM_WIN32_NT */
+OSVERSIONINFO osver; /* VER_PLATFORM_WIN32_NT */
int ap_max_requests_per_child=0;
int ap_daemons_to_start=0;
@@ -106,7 +112,30 @@ HANDLE maintenance_event;
ap_lock_t *start_mutex;
DWORD my_pid;
DWORD parent_pid;
-API_VAR_EXPORT ap_completion_t ap_mpm_init_complete = NULL;
+
+/* This is the helper code to resolve late bound entry points
+ * missing from one or more releases of the Win32 API...
+ * but it sure would be nice if we didn't duplicate this code
+ * from the APR ;-)
+ */
+
+static const char* const lateDllName[DLL_defined] = {
+ "kernel32", "advapi32", "mswsock", "ws2_32" };
+static HMODULE lateDllHandle[DLL_defined] = {
+ NULL, NULL, NULL, NULL };
+
+FARPROC ap_load_dll_func(ap_dlltoken_e fnLib, char* fnName, int ordinal)
+{
+ if (!lateDllHandle[fnLib]) {
+ lateDllHandle[fnLib] = LoadLibrary(lateDllName[fnLib]);
+ if (!lateDllHandle[fnLib])
+ return NULL;
+ }
+ if (ordinal)
+ return GetProcAddress(lateDllHandle[fnLib], (char *) ordinal);
+ else
+ return GetProcAddress(lateDllHandle[fnLib], fnName);
+}
static ap_status_t socket_cleanup(void *sock)
{
@@ -122,7 +151,6 @@ static ap_status_t socket_cleanup(void *sock)
* or thrown out entirely...
*/
-
typedef void semaphore;
typedef void event;
@@ -257,13 +285,11 @@ static DWORD wait_for_many_objects(DWORD nCount, CONST HANDLE *lpHandles,
* On entry, type gives the event to signal. 0 means shutdown, 1 means
* graceful restart.
*/
-static void signal_parent(int type)
+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
@@ -315,11 +341,7 @@ API_EXPORT(void) ap_start_shutdown(void)
* signal_restart_name and signal_shutdown_name.
*/
-#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];
-static void setup_signal_names(char *prefix)
+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),
@@ -456,7 +478,6 @@ static int setup_inherited_listeners(server_rec *s)
listenmaxfd = nsd;
}
}
-// ap_register_cleanup(p, (void *)lr->sd, socket_cleanup, ap_null_cleanup);
ap_put_os_sock(&lr->sd, &nsd, pconf);
lr->count = 0;
}
@@ -667,7 +688,6 @@ static void accept_and_queue_connections(void * dummy)
tv.tv_usec = 0;
memcpy(&main_fds, &listenfds, sizeof(fd_set));
-// rc = ap_select(listenmaxfd + 1, &main_fds, NULL, NULL, &tv);
rc = select(listenmaxfd + 1, &main_fds, NULL, NULL, &tv);
if (rc == 0 || (rc == SOCKET_ERROR && h_errno == WSAEINTR)) {
@@ -741,8 +761,7 @@ static PCOMP_CONTEXT win9x_get_connection(PCOMP_CONTEXT context)
if (context->accept_socket == -1) {
return NULL;
}
- //ap_note_cleanups_for_socket(ptrans, csd);
- len = sizeof(struct sockaddr);
+ len = sizeof(struct sockaddr);
context->sa_server = ap_palloc(context->ptrans, len);
if (getsockname(context->accept_socket,
context->sa_server, &len)== SOCKET_ERROR) {
@@ -1283,7 +1302,7 @@ static void child_main()
*/
for (lr = ap_listeners; lr != NULL; lr = lr->next) {
ap_get_os_sock(&nsd,lr->sd);
- CancelIo(nsd);
+ CancelIo((HANDLE) nsd);
}
/* Drain the canceled contexts off the port */
@@ -1392,7 +1411,6 @@ static void cleanup_process(HANDLE *handles, HANDLE *events, int position, int *
static int create_process(ap_pool_t *p, HANDLE *handles, HANDLE *events, int *processes)
{
-
int rv;
char buf[1024];
char *pCommand;
@@ -1401,7 +1419,7 @@ static int create_process(ap_pool_t *p, HANDLE *handles, HANDLE *events, int *pr
int i;
int iEnvBlockLen;
STARTUPINFO si; /* Filled in prior to call to CreateProcess */
- PROCESS_INFORMATION pi; /* filled in on call to CreateProces */
+ PROCESS_INFORMATION pi; /* filled in on call to CreateProcess */
ap_listen_rec *lr;
DWORD BytesWritten;
@@ -1409,6 +1427,10 @@ static int create_process(ap_pool_t *p, HANDLE *handles, HANDLE *events, int *pr
HANDLE hPipeWrite = NULL;
SECURITY_ATTRIBUTES sa = {0};
+ HANDLE kill_event;
+ LPWSAPROTOCOL_INFO lpWSAProtocolInfo;
+ HANDLE hDupedCompPort;
+
sa.nLength = sizeof(sa);
sa.bInheritHandle = TRUE;
sa.lpSecurityDescriptor = NULL;
@@ -1435,7 +1457,6 @@ static int create_process(ap_pool_t *p, HANDLE *handles, HANDLE *events, int *pr
}
/* Build the environment, since Win9x disrespects the active env */
- // SetEnvironmentVariable("AP_PARENT_PID",ap_psprintf(p,"%l",parent_pid));
pEnvVar = ap_psprintf(p, "AP_PARENT_PID=%i", parent_pid);
/*
* Win32's CreateProcess call requires that the environment
@@ -1493,78 +1514,73 @@ static int create_process(ap_pool_t *p, HANDLE *handles, HANDLE *events, int *pr
CloseHandle(pi.hThread);
return -1;
}
- else {
- HANDLE kill_event;
- LPWSAPROTOCOL_INFO lpWSAProtocolInfo;
- HANDLE hDupedCompPort;
+
+ ap_log_error(APLOG_MARK, APLOG_INFO, APR_SUCCESS, server_conf,
+ "Parent: Created child process %d", pi.dwProcessId);
- ap_log_error(APLOG_MARK, APLOG_INFO, APR_SUCCESS, server_conf,
- "Parent: Created child process %d", pi.dwProcessId);
+ SetEnvironmentVariable("AP_PARENT_PID",NULL);
- SetEnvironmentVariable("AP_PARENT_PID",NULL);
+ /* Create the exit_event, apCchild_pid */
+ sa.nLength = sizeof(sa);
+ sa.bInheritHandle = TRUE;
+ sa.lpSecurityDescriptor = NULL;
+ kill_event = CreateEvent(&sa, TRUE, FALSE, ap_psprintf(pconf,"apC%d", pi.dwProcessId));
+ if (!kill_event) {
+ ap_log_error(APLOG_MARK, APLOG_CRIT, GetLastError(), server_conf,
+ "Parent: Could not create exit event for child process");
+ CloseHandle(pi.hProcess);
+ CloseHandle(pi.hThread);
+ return -1;
+ }
+
+ /* Assume the child process lives. Update the process and event tables */
+ handles[*processes] = pi.hProcess;
+ events[*processes] = kill_event;
+ (*processes)++;
+
+ /* We never store the thread's handle, so close it now. */
+ ResumeThread(pi.hThread);
+ CloseHandle(pi.hThread);
+
+ /* 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) {
+ int nsd;
+ lpWSAProtocolInfo = ap_pcalloc(p, sizeof(WSAPROTOCOL_INFO));
+ ap_get_os_sock(&nsd,lr->sd);
+ ap_log_error(APLOG_MARK, APLOG_INFO, APR_SUCCESS, server_conf,
+ "Parent: Duplicating socket %d and sending it to child process %d", nsd, pi.dwProcessId);
+ if (WSADuplicateSocket(nsd, pi.dwProcessId,
+ lpWSAProtocolInfo) == SOCKET_ERROR) {
+ ap_log_error(APLOG_MARK, APLOG_CRIT, h_errno, server_conf,
+ "Parent: WSADuplicateSocket failed for socket %d.", lr->sd );
+ return -1;
+ }
- /* Create the exit_event, apCchild_pid */
- sa.nLength = sizeof(sa);
- sa.bInheritHandle = TRUE;
- sa.lpSecurityDescriptor = NULL;
- kill_event = CreateEvent(&sa, TRUE, FALSE, ap_psprintf(pconf,"apC%d", pi.dwProcessId));
- if (!kill_event) {
+ if (!WriteFile(hPipeWrite, lpWSAProtocolInfo, (DWORD) sizeof(WSAPROTOCOL_INFO),
+ &BytesWritten,
+ (LPOVERLAPPED) NULL)) {
ap_log_error(APLOG_MARK, APLOG_CRIT, GetLastError(), server_conf,
- "Parent: Could not create exit event for child process");
- CloseHandle(pi.hProcess);
- CloseHandle(pi.hThread);
+ "Parent: Unable to write duplicated socket %d to the child.", lr->sd );
return -1;
}
-
- /* Assume the child process lives. Update the process and event tables */
- handles[*processes] = pi.hProcess;
- events[*processes] = kill_event;
- (*processes)++;
-
- /* We never store the thread's handle, so close it now. */
- ResumeThread(pi.hThread);
- CloseHandle(pi.hThread);
-
- /* 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) {
- int nsd;
- lpWSAProtocolInfo = ap_pcalloc(p, sizeof(WSAPROTOCOL_INFO));
- ap_get_os_sock(&nsd,lr->sd);
- ap_log_error(APLOG_MARK, APLOG_INFO, APR_SUCCESS, server_conf,
- "Parent: Duplicating socket %d and sending it to child process %d", nsd, pi.dwProcessId);
- if (WSADuplicateSocket(nsd, pi.dwProcessId,
- lpWSAProtocolInfo) == SOCKET_ERROR) {
- ap_log_error(APLOG_MARK, APLOG_CRIT, h_errno, server_conf,
- "Parent: WSADuplicateSocket failed for socket %d.", lr->sd );
- return -1;
- }
-
- if (!WriteFile(hPipeWrite, lpWSAProtocolInfo, (DWORD) sizeof(WSAPROTOCOL_INFO),
- &BytesWritten,
- (LPOVERLAPPED) NULL)) {
- ap_log_error(APLOG_MARK, APLOG_CRIT, GetLastError(), server_conf,
- "Parent: Unable to write duplicated socket %d to the child.", lr->sd );
- return -1;
- }
- ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, APR_SUCCESS, server_conf,
- "Parent: BytesWritten = %d WSAProtocolInfo = %x20", BytesWritten, *lpWSAProtocolInfo);
+ ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, APR_SUCCESS, server_conf,
+ "Parent: BytesWritten = %d WSAProtocolInfo = %x20", BytesWritten, *lpWSAProtocolInfo);
+ }
+ if (osver.dwPlatformId != VER_PLATFORM_WIN32_WINDOWS) {
+ /* Now, send the AcceptEx completion port to the child */
+ if (!DuplicateHandle(GetCurrentProcess(), AcceptExCompPort,
+ pi.hProcess, &hDupedCompPort, 0,
+ TRUE, DUPLICATE_SAME_ACCESS)) {
+ ap_log_error(APLOG_MARK, APLOG_CRIT, GetLastError(), server_conf,
+ "Parent: Unable to duplicate AcceptEx completion port. Shutting down.");
+ return -1;
}
- if (osver.dwPlatformId != VER_PLATFORM_WIN32_WINDOWS) {
- /* Now, send the AcceptEx completion port to the child */
- if (!DuplicateHandle(GetCurrentProcess(), AcceptExCompPort,
- pi.hProcess, &hDupedCompPort, 0,
- TRUE, DUPLICATE_SAME_ACCESS)) {
- ap_log_error(APLOG_MARK, APLOG_CRIT, GetLastError(), server_conf,
- "Parent: Unable to duplicate AcceptEx completion port. Shutting down.");
- return -1;
- }
- WriteFile(hPipeWrite, &hDupedCompPort, (DWORD) sizeof(hDupedCompPort), &BytesWritten, (LPOVERLAPPED) NULL);
- }
+ WriteFile(hPipeWrite, &hDupedCompPort, (DWORD) sizeof(hDupedCompPort), &BytesWritten, (LPOVERLAPPED) NULL);
}
-
+
CloseHandle(hPipeRead);
CloseHandle(hPipeWrite);
@@ -1590,7 +1606,6 @@ static int master_main(server_rec *s, HANDLE shutdown_event, HANDLE restart_even
/* Create child process
* Should only be one in this version of Apache for WIN32
*/
- //service_set_status(SERVICE_START_PENDING);
while (remaining_children_to_start--) {
if (create_process(pconf, process_handles, process_kill_events,
&current_live_processes) < 0) {
@@ -1600,10 +1615,12 @@ static int master_main(server_rec *s, HANDLE shutdown_event, HANDLE restart_even
goto die_now;
}
}
- //service_set_status(SERVICE_RUNNING);
-
- restart_pending = shutdown_pending = 0;
+ restart_pending = shutdown_pending = 0;
+
+ if (!strcasecmp(signal_arg, "runservice"))
+ mpm_service_started();
+
/* Wait for shutdown or restart events or for child death */
process_handles[current_live_processes] = shutdown_event;
process_handles[current_live_processes+1] = restart_event;
@@ -1666,6 +1683,8 @@ static int master_main(server_rec *s, HANDLE shutdown_event, HANDLE restart_even
* TODO: Consider restarting the child immediately without looping through http_main
* and without rereading the configuration. Will need this if we ever support multiple
* children. One option, create a parent thread which waits on child death and restarts it.
+ * Consider, however, that if the user makes httpd.conf invalid, we want to die before
+ * our child tries it... otherwise we have a nasty loop.
*/
restart_pending = 1;
ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, APR_SUCCESS, server_conf,
@@ -1681,8 +1700,14 @@ static int master_main(server_rec *s, HANDLE shutdown_event, HANDLE restart_even
}
die_now:
- if (shutdown_pending) {
+ if (shutdown_pending)
+ {
int tmstart = time(NULL);
+
+ if (strcasecmp(signal_arg,"runservice")
+ && (osver.dwPlatformId == VER_PLATFORM_WIN32_NT)) {
+ mpm_service_nt_stopping();
+ }
/* Signal each child processes to die */
for (i = 0; i < current_live_processes; i++) {
printf("SetEvent handle = %d\n", process_kill_events[i]);
@@ -1711,12 +1736,42 @@ die_now:
return 1; /* Tell the caller we want a restart */
}
-/*
- * winnt_pre_config() hook
+
+#define SERVICE_UNNAMED -1
+
+/* service_nt_main_fn needs to append the StartService() args
+ * outside of our call stack and thread as the service starts...
*/
-static void winnt_pre_config(ap_pool_t *pconf, ap_pool_t *plog, ap_pool_t *ptemp)
+ap_array_header_t *mpm_new_argv;
+
+/* 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.
+ */
+
+static ap_status_t service_to_start_success;
+static int inst_argc;
+static char **inst_argv;
+
+void winnt_rewrite_args(process_rec *process)
{
+ /* Handle the following SCM aspects in this phase:
+ *
+ * -k runservice [transition for WinNT, nothing for Win9x]
+ * -k (!)install [error out if name is not installed]
+ *
+ * We can't leave this phase until we know our identity
+ * and modify the command arguments appropriately.
+ */
+ ap_status_t service_named = SERVICE_UNNAMED;
+ ap_status_t rv;
+ char *def_server_root;
+ char fnbuf[MAX_PATH];
+ char optbuf[3];
+ char **new_arg;
+ int fixed_args;
char *pid;
+ int opt;
one_process = !!getenv("ONE_PROCESS");
@@ -1725,14 +1780,205 @@ static void winnt_pre_config(ap_pool_t *pconf, ap_pool_t *plog, ap_pool_t *ptemp
/* AP_PARENT_PID is only valid in the child */
pid = getenv("AP_PARENT_PID");
- if (pid) {
+ if (pid)
+ {
/* This is the child */
parent_pid = (DWORD) atol(pid);
my_pid = GetCurrentProcessId();
+
+ /* 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.
+ */
+ signal_arg = "runchild";
+ return;
}
- else {
- /* This is the parent */
- parent_pid = my_pid = GetCurrentProcessId();
+
+ /* This is the parent, we have a long way to go :-) */
+ parent_pid = my_pid = GetCurrentProcessId();
+
+ /* Rewrite process->argv[];
+ *
+ * strip out -k signal into signal_arg
+ * strip out -n servicename into service_name & display_name
+ * 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 command line arguments from this process
+ * The WinNT SCM's StartService() args
+ */
+
+ if (!GetModuleFileName(NULL, fnbuf, sizeof(fnbuf))) {
+ /* WARNING: There is an implict assumption here that the
+ * executable resides in the ServerRoot!
+ */
+ ap_log_error(APLOG_MARK,APLOG_ERR, GetLastError(), NULL,
+ "Failed to get the running module's file name");
+ exit(1);
+ }
+ def_server_root = (char *) ap_filename_of_pathname(fnbuf);
+ if (def_server_root > fnbuf) {
+ *(def_server_root - 1) = '\0';
+ def_server_root = ap_os_canonical_filename(process->pool, fnbuf);
+ }
+
+ /* 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.
+ */
+
+ mpm_new_argv = ap_make_array(process->pool, process->argc + 2, sizeof(char *));
+ new_arg = (char**) ap_push_array(mpm_new_argv);
+ *new_arg = (char *) process->argv[0];
+
+ new_arg = (char**) ap_push_array(mpm_new_argv);
+ *new_arg = "-d";
+ new_arg = (char**) ap_push_array(mpm_new_argv);
+ *new_arg = def_server_root;
+
+ fixed_args = mpm_new_argv->nelts;
+
+ optbuf[0] = '-'; optbuf[2] = '\0';
+ while (ap_getopt(process->argc, (char**) process->argv,
+ "n:k:iu" AP_SERVER_BASEARGS,
+ &opt, process->pool) == APR_SUCCESS) {
+ switch (opt) {
+ case 'n':
+ service_named = mpm_service_set_name(process->pool, ap_optarg);
+ break;
+ case 'k':
+ signal_arg = ap_optarg;
+ break;
+ case 'i':
+ /* TODO: warn of depreciated syntax, "use -k install instead" */
+ signal_arg = "install";
+ break;
+ case 'u':
+ /* TODO: warn of depreciated syntax, "use -k uninstall instead" */
+ signal_arg = "uninstall";
+ break;
+ default:
+ optbuf[1] = (char) opt;
+ new_arg = (char**) ap_push_array(mpm_new_argv);
+ *new_arg = ap_pstrdup(process->pool, optbuf);
+ if (ap_optarg) {
+ new_arg = (char**) ap_push_array(mpm_new_argv);
+ *new_arg = ap_optarg;
+ }
+ break;
+ }
+ }
+ /* Set optreset and optind to allow ap_getopt to work correctly
+ * when called from http_main.c
+ */
+ ap_optreset = 1;
+ ap_optind = 1;
+
+ /* Track the number of args actually entered by the user */
+ inst_argc = mpm_new_argv->nelts - fixed_args;
+
+ /* Provide a default 'run' -k arg to simplify signal_arg tests */
+ if (!signal_arg)
+ signal_arg = "run";
+
+ if (!strcasecmp(signal_arg, "runservice"))
+ {
+ /* 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
+ * add for us.
+ *
+ * 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.
+ */
+ if (osver.dwPlatformId == VER_PLATFORM_WIN32_NT) {
+ service_to_start_success = mpm_service_to_start();
+ if (service_to_start_success == APR_SUCCESS)
+ service_named = APR_SUCCESS;
+ }
+ }
+
+ if (service_named == SERVICE_UNNAMED) {
+ service_named = mpm_service_set_name(process->pool,
+ DEFAULT_SERVICE_NAME);
+ }
+
+ if (strcasecmp(signal_arg, "install")) /* not -k install */
+ {
+ if (service_named == APR_SUCCESS)
+ {
+ rv = mpm_merge_service_args(process->pool, mpm_new_argv,
+ fixed_args);
+ if (rv != APR_SUCCESS) {
+ ap_log_error(APLOG_MARK,APLOG_ERR, rv, server_conf,
+ "%s: ConfigArgs are missing from the registry.",
+ display_name);
+ }
+ }
+ else
+ {
+ ap_log_error(APLOG_MARK,APLOG_ERR, APR_BADARG, server_conf,
+ "%s: No installed service by that name.", display_name);
+ exit(1);
+ }
+ }
+ else /* -k install */
+ {
+ if (service_named == APR_SUCCESS)
+ {
+ ap_log_error(APLOG_MARK,APLOG_ERR, APR_BADARG, server_conf,
+ "%s: Service is already installed.", display_name);
+ exit(1);
+ }
+ }
+
+ /* 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.
+ */
+ inst_argv = (char**) mpm_new_argv->elts + mpm_new_argv->nelts - inst_argc;
+
+ process->argc = mpm_new_argv->nelts;
+ process->argv = (char**) mpm_new_argv->elts;
+}
+
+
+static void winnt_pre_config(ap_pool_t *pconf, ap_pool_t *plog, ap_pool_t *ptemp)
+{
+ /* Handle the following SCM aspects in this phase:
+ *
+ * -k runservice [WinNT errors logged from rewrite_args]
+ * -k uninstall
+ *
+ * in both cases we -don't- care if httpd.conf is error-free
+ */
+ ap_status_t rv;
+
+ if (!strcasecmp(signal_arg, "runservice")
+ && (osver.dwPlatformId == VER_PLATFORM_WIN32_NT)
+ && (service_to_start_success != APR_SUCCESS)) {
+ ap_log_error(APLOG_MARK,APLOG_ERR, service_to_start_success,
+ server_conf, "%s: Unable to start the service manager.",
+ display_name);
+ exit(1);
+ }
+
+ if (!strcasecmp(signal_arg, "uninstall")) {
+ rv = mpm_service_uninstall();
+ exit(rv);
}
ap_listen_pre_config();
@@ -1742,16 +1988,57 @@ static void winnt_pre_config(ap_pool_t *pconf, ap_pool_t *plog, ap_pool_t *ptemp
max_requests_per_child = DEFAULT_MAX_REQUESTS_PER_CHILD;
ap_cpystrn(ap_coredump_dir, ap_server_root, sizeof(ap_coredump_dir));
-
}
-static void winnt_post_config(ap_pool_t *pconf, ap_pool_t *plog, ap_pool_t *ptemp, server_rec* server_conf)
+static void winnt_post_config(ap_pool_t *pconf, ap_pool_t *plog, ap_pool_t *ptemp, server_rec* server)
{
static int restart_num = 0;
- server_conf = server_conf;
+ ap_status_t rv;
+
+ server_conf = server;
+
+ /* Handle the following SCM aspects in this phase:
+ *
+ * -k install
+ * -k start
+ * -k restart
+ * -k stop
+ * -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.
+ */
+
+ if (!strcasecmp(signal_arg, "install")) {
+ mpm_service_install(ptemp, inst_argc, inst_argv);
+ exit(rv);
+ }
+
+ if (!strcasecmp(signal_arg, "start")) {
+ rv = mpm_service_start(ptemp, inst_argc, inst_argv);
+ exit(rv);
+ }
+
+ if (!strcasecmp(signal_arg, "restart")) {
+ mpm_signal_service(ptemp, ap_pid_fname, 1);
+ exit(0);
+ }
+
+ // TODO: This Stinks - but we needed the ap_pid_fname entry from
+ // the config!?! Find a clean way to get the egg back into
+ // into the chicken and shove this signal into pre_config
+ if (!strcasecmp(signal_arg, "stop")) {
+ mpm_signal_service(ptemp, ap_pid_fname, 0);
+ exit(0);
+ }
- if (parent_pid == my_pid) {
- if (restart_num++ == 1) {
+ if (parent_pid == my_pid)
+ {
+ if (restart_num++ == 1)
+ {
/* This code should be run once in the parent and not run
* accross a restart
*/
@@ -1773,8 +2060,7 @@ static void winnt_post_config(ap_pool_t *pconf, ap_pool_t *plog, ap_pool_t *ptem
}
ap_log_pid(pconf, ap_pid_fname);
- //service_set_status(SERVICE_START_PENDING);
-
+
/* Create shutdown event, apPID_shutdown, where PID is the parent
* Apache process ID. Shutdown is signaled by 'apache -k shutdown'.
*/
@@ -1799,8 +2085,27 @@ static void winnt_post_config(ap_pool_t *pconf, ap_pool_t *plog, ap_pool_t *ptem
}
CleanNullACL((void *)sa);
- if (ap_mpm_init_complete)
- ap_mpm_init_complete();
+ /* Now that we are flying at 15000 feet...
+ * wipe out the Win95 service console,
+ * signal the SCM the WinNT service started, or
+ * if not a service, setup console handlers instead.
+ */
+ if (!strcasecmp(signal_arg, "runservice"))
+ {
+ if (osver.dwPlatformId != VER_PLATFORM_WIN32_NT)
+ {
+ rv = mpm_service_to_start();
+ if (rv != APR_SUCCESS) {
+ ap_log_error(APLOG_MARK,APLOG_ERR, rv, server_conf,
+ "%s: Unable to start the service manager.",
+ display_name);
+ exit(1);
+ }
+ }
+ }
+ else
+ mpm_start_console_handler();
+
/* Create the start mutex, apPID, where PID is the parent Apache process ID.
* Ths start mutex is used during a restart to prevent more than one
@@ -1810,6 +2115,10 @@ static void winnt_post_config(ap_pool_t *pconf, ap_pool_t *plog, ap_pool_t *ptem
server_conf->process->pool);
}
}
+ else /* parent_pid != my_pid */
+ {
+ mpm_start_child_console_handler();
+ }
}
API_EXPORT(int) ap_mpm_run(ap_pool_t *_pconf, ap_pool_t *plog, server_rec *s )
@@ -1846,8 +2155,6 @@ API_EXPORT(int) ap_mpm_run(ap_pool_t *_pconf, ap_pool_t *plog, server_rec *s )
CloseHandle(restart_event);
CloseHandle(shutdown_event);
- //service_set_status(SERVICE_STOPPED);
-
return 1;
}
} /* Parent process */
@@ -1967,9 +2274,9 @@ LISTEN_COMMANDS
{ NULL }
};
-module MODULE_VAR_EXPORT mpm_winnt_module = {
+MODULE_VAR_EXPORT module mpm_winnt_module = {
MPM20_MODULE_STUFF,
- NULL, /* hook run before arguments are parsed */
+ winnt_rewrite_args, /* hook to run before apache parses args */
winnt_pre_config, /* hook run before configuration is read */
NULL, /* create per-directory config structure */
NULL, /* merge per-directory config structures */
diff --git a/server/mpm/winnt/mpm_winnt.h b/server/mpm/winnt/mpm_winnt.h
index e0f7b50620..37ac047d37 100644
--- a/server/mpm/winnt/mpm_winnt.h
+++ b/server/mpm/winnt/mpm_winnt.h
@@ -61,16 +61,67 @@
#include "ap_listen.h"
+/* From registry.c: */
+
+ap_status_t ap_registry_create_key(const char *key);
+ap_status_t ap_registry_delete_key(const char *key);
+
+ap_status_t ap_registry_store_value(const char *key, const char *name,
+ const char *value);
+ap_status_t ap_registry_get_value(ap_pool_t *p, const char *key,
+ const char *name, char **ppValue);
+ap_status_t ap_registry_store_array(ap_pool_t *p, const char *key,
+ const char *name, int nelts,
+ char const* const* elts);
+ap_status_t ap_registry_get_array(ap_pool_t *p, const char *key,
+ const char *name,
+ ap_array_header_t **parray);
+ap_status_t ap_registry_delete_value(const char *key, const char *name);
+
+
+/* From service.c: */
+
+#define DEFAULT_SERVICE_NAME AP_SERVER_BASEPRODUCT
+#define SERVICECONFIG9X "Software\\Microsoft\\Windows\\CurrentVersion\\RunServices"
+#define SERVICECONFIG "System\\CurrentControlSet\\Services\\%s"
+#define SERVICEPARAMS "System\\CurrentControlSet\\Services\\%s\\Parameters"
+
+extern char *service_name;
+extern char *display_name;
+
+ap_status_t mpm_service_set_name(ap_pool_t *p, char *name);
+ap_status_t mpm_merge_service_args(ap_pool_t *p, ap_array_header_t *args,
+ int fixed_args);
+
+ap_status_t mpm_service_to_start(void);
+ap_status_t mpm_service_started(void);
+ap_status_t mpm_service_install(ap_pool_t *ptemp, int argc,
+ char const* const* argv);
+ap_status_t mpm_service_uninstall(void);
+
+ap_status_t mpm_service_start(ap_pool_t *ptemp, int argc,
+ char const* const* argv);
+
+void mpm_signal_service(ap_pool_t *ptemp, char *fname, int signal);
+
+void mpm_service_nt_stopping(void);
+
+void mpm_start_console_handler(void);
+void mpm_start_child_console_handler(void);
+
+/* From winnt.c: */
+
+extern OSVERSIONINFO osver;
extern int ap_threads_per_child;
extern int ap_max_requests_per_child;
extern int ap_extended_status;
extern void clean_child_exit(int);
-typedef void (CALLBACK *ap_completion_t)();
-API_VAR_EXPORT ap_completion_t ap_mpm_init_complete;
-
API_EXPORT(void) ap_start_shutdown(void);
+void setup_signal_names(char *prefix);
+void signal_parent(int type);
+
typedef struct CompContext {
OVERLAPPED Overlapped;
SOCKET accept_socket;
@@ -85,4 +136,39 @@ typedef struct CompContext {
struct sockaddr *sa_client;
int sa_client_len;
} COMP_CONTEXT, *PCOMP_CONTEXT;
+
+/* This code is stolen from the apr_private.h and misc/win32/misc.c
+ * Please see those sources for detailed documentation.
+ */
+typedef enum {
+ DLL_WINBASEAPI = 0, // kernel32 From WinBase.h
+ DLL_WINADVAPI = 1, // advapi32 From WinBase.h
+ DLL_WINSOCKAPI = 2, // mswsock From WinSock.h
+ DLL_WINSOCK2API = 3, // ws2_32 From WinSock2.h
+ DLL_defined = 4 // must define as last idx_ + 1
+} ap_dlltoken_e;
+
+FARPROC ap_load_dll_func(ap_dlltoken_e fnLib, char *fnName, int ordinal);
+
+#define AP_DECLARE_LATE_DLL_FUNC(lib, rettype, calltype, fn, ord, args, names) \
+ typedef rettype (calltype *ap_winapi_fpt_##fn) args; \
+ static ap_winapi_fpt_##fn ap_winapi_pfn_##fn = NULL; \
+ __inline rettype ap_winapi_##fn args \
+ { if (!ap_winapi_pfn_##fn) \
+ ap_winapi_pfn_##fn = (ap_winapi_fpt_##fn) ap_load_dll_func(lib, #fn, ord); \
+ return (*(ap_winapi_pfn_##fn)) names; }; \
+
+/* WinNT kernel only */
+AP_DECLARE_LATE_DLL_FUNC(DLL_WINBASEAPI, BOOL, WINAPI, CancelIo, 0, (
+ IN HANDLE hFile),
+ (hFile));
+#define CancelIo ap_winapi_CancelIo
+
+/* Win9x kernel only */
+AP_DECLARE_LATE_DLL_FUNC(DLL_WINBASEAPI, DWORD, WINAPI, RegisterServiceProcess, 0, (
+ DWORD dwProcessId,
+ DWORD dwType),
+ (dwProcessId, dwType));
+#define RegisterServiceProcess ap_winapi_RegisterServiceProcess
+
#endif /* APACHE_MPM_WINNT_H */
diff --git a/server/mpm/winnt/registry.c b/server/mpm/winnt/registry.c
index bcca09d91a..987811a6a0 100644
--- a/server/mpm/winnt/registry.c
+++ b/server/mpm/winnt/registry.c
@@ -73,336 +73,241 @@
* release to a development or beta version.
*/
-/* To allow for multiple services, store the configuration file's full path
- * under each service entry:
- *
- * HKLM\System\CurrentControlSet\Services\[service name]\Parameters\ConfPath
- *
- * The default configuration path (for console apache) is still stored:
- *
- * HKLM\Software\[Vendor]\[Software]\[Version]\ServerRoot
- */
-
-#include "ap_config.h"
#include "httpd.h"
#include "http_log.h"
-
-/* Define where the Apache values are stored in the registry.
- *
- * If you are looking here to roll the tarball, you didn't need to visit.
- * registry.c now picks up the version from include/httpd.h
+#include "winnt.h"
+/* bet you are looking to change revisions to roll the tarball...
+ * Guess what, you already did. Revised May '00 to save you from
+ * searching all over creation for every revision tag.
*/
-#define REGKEY "SOFTWARE\\" AP_SERVER_BASEVENDOR "\\" AP_SERVER_BASEPRODUCT "\\" AP_SERVER_BASEREVISION
+#define VENDOR AP_SERVER_BASEVENDOR
+#define SOFTWARE AP_SERVER_BASEPRODUCT
+#define VERSION AP_SERVER_BASEREVISION
-#define SERVICEKEYPRE "System\\CurrentControlSet\\Services\\"
-#define SERVICEKEYPOST "\\Parameters"
+#define REGKEY "SOFTWARE\\" VENDOR "\\" SOFTWARE "\\" VERSION
-#define SERVICELAUNCH9X "Software\\Microsoft\\Windows\\CurrentVersion\\RunServices\\"
/*
* The Windows API registry key functions don't set the last error
* value (the windows equivalent of errno). So we need to set it
* with SetLastError() before calling the aplog_error() function.
* Because this is common, let's have a macro.
*/
-#define do_error(rv,fmt,arg) do { \
- ap_log_error(APLOG_MARK, APLOG_ERR, rv, NULL, fmt, arg); \
- } while (0);
-
-/*
- * Get the data for registry key value. This is a generic function that
- * can either get a value into a caller-supplied buffer, or it can
- * allocate space for the value from the pass in pool. It will normally
- * be used by other functions within this file to get specific key values
- * (e.g. registry_get_server_root()). This function returns a number of
- * different error statuses, allowing the caller to differentiate
- * between a key or value not existing and other kinds of errors. Depending
- * on the type of data being obtained the caller can then either ignore
- * the key-not-existing error, or treat it as a real error.
- *
- * If ppValue is NULL, allocate space for the value and return it in
- * *pValue. The return value is the number of bytes in the value.
- * The first argument is the pool to use to allocate space for the value.
- *
- * If pValue is not NULL, assume it is a buffer of nSizeValue bytes,
- * and write the value into the buffer. The return value is the number
- * of bytes in the value (so if the return value is greater than
- * the supplied nSizeValue, the caller knows that *pValue is truncated).
- * The pool argument is ignored.
- *
- * The return value is the number of bytes in the successfully retreived
- * key if everything worked, or:
- *
- * -1 the key does not exists
- * -2 if out of memory during the function
- * -3 if the buffer specified by *pValue/nSizeValue was not large enough
- * for the value.
- * -4 if an error occurred
- *
- * If the return value is negative a message will be logged to the error
- * log (aplog_error) function. If the return value is -2, -3 or -4 the message
- * will be logged at priority "error", while if the return value is -1 the
- * message will be logged at priority "warning".
- */
+#define return_error(rv) return (SetLastError(rv), rv);
-static int ap_registry_get_key_int(ap_pool_t *p, char *key, char *name, char *pBuffer, int nSizeBuffer, char **ppValue)
+ap_status_t ap_registry_create_key(const char *key)
{
- long rv;
- HKEY hKey;
- char *pValue;
- int nSize;
- int retval;
-
- rv = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
- key,
- 0,
- KEY_READ,
- &hKey);
-
- if (rv == ERROR_FILE_NOT_FOUND) {
- ap_log_error(APLOG_MARK,APLOG_WARNING|APLOG_NOERRNO,rv,NULL,
- "Registry does not contain key %s",key);
- return -1;
- }
- if (rv != ERROR_SUCCESS) {
- do_error(rv, "RegOpenKeyEx HKLM\\%s",key);
- return -4;
- }
+ HKEY hKey = HKEY_LOCAL_MACHINE;
+ HKEY hKeyNext;
+ char keystr[MAX_PATH + 1];
+ char *parsekey = keystr;
+ char *nextkey = keystr;
+ DWORD result;
+ int rv;
- if (pBuffer == NULL) {
- /* Find the size required for the data by passing NULL as the buffer
- * pointer. On return nSize will contain the size required for the
- * buffer if the return value is ERROR_SUCCESS.
- */
- rv = RegQueryValueEx(hKey,
- name, /* key name */
- NULL, /* reserved */
- NULL, /* type */
- NULL, /* for value */
- &nSize); /* for size of "value" */
-
- if (rv != ERROR_SUCCESS) {
- do_error(rv, "RegQueryValueEx(key %s)", key);
- return -1;
- }
-
- pValue = ap_palloc(p, nSize);
- *ppValue = pValue;
- if (!pValue) {
- /* Eek, out of memory, probably not worth trying to carry on,
- * but let's give it a go
- */
- ap_log_error(APLOG_MARK,APLOG_ERR|APLOG_NOERRNO,APR_ENOMEM,NULL,
- "Error getting registry key: out of memory");
- return -2;
- }
- }
- else {
- /* Get the value into the existing buffer of length nSizeBuffer */
- pValue = pBuffer;
- nSize = nSizeBuffer;
- }
+ ap_cpystrn(keystr, key, sizeof(keystr) - 1);
+
+ /* Walk the tree, creating at each stage if necessary */
+ while (parsekey) {
+ if (nextkey = strchr(parsekey, '\\'))
+ *(nextkey++) = '\0';
- rv = RegQueryValueEx(hKey,
- name, /* key name */
- NULL, /* reserved */
- NULL, /* type */
- pValue, /* for value */
- &nSize); /* for size of "value" */
+ rv = RegCreateKeyEx(hKey,
+ parsekey, /* subkey */
+ 0, /* reserved */
+ NULL, /* class */
+ REG_OPTION_NON_VOLATILE,
+ KEY_WRITE,
+ NULL,
+ &hKeyNext,
+ &result);
- retval = 0; /* Return value */
+ /* Close the old key */
+ if (hKey != HKEY_LOCAL_MACHINE)
+ RegCloseKey(hKey);
+ hKey = hKeyNext;
+
+ if (rv != ERROR_SUCCESS)
+ break;
- if (rv == ERROR_FILE_NOT_FOUND) {
- ap_log_error(APLOG_MARK,APLOG_WARNING|APLOG_NOERRNO,rv,NULL,
- "Registry does not contain value %s\\%s", key, name);
- retval = -1;
- }
- else if (rv == ERROR_MORE_DATA) {
- /* This should only happen if we got passed a pre-existing buffer
- * (pBuffer, nSizeBuffer). But I suppose it could also happen if we
- * allocate a buffer if another process changed the length of the
- * value since we found out its length above. Umm.
- */
- ap_log_error(APLOG_MARK,APLOG_ERR|APLOG_NOERRNO,rv,NULL,
- "Error getting registry value %s: buffer not big enough", key);
- retval = -3;
- }
- else if (rv != ERROR_SUCCESS) {
- do_error(rv, "RegQueryValueEx(key %s)", key);
- retval = -4;
+ parsekey = nextkey;
}
- rv = RegCloseKey(hKey);
- if (rv != ERROR_SUCCESS) {
- do_error(rv, "RegCloseKey HKLM\\%s", key);
- if (retval == 0) {
- /* Keep error status from RegQueryValueEx, if any */
- retval = -4;
- }
- }
+ if (hKey != HKEY_LOCAL_MACHINE)
+ RegCloseKey(hKey);
- return retval < 0 ? retval : nSize;
+ return_error(rv);
}
-/*
- * Get the server root from the registry into 'dir' which is
- * size bytes long. Returns 0 if the server root was found
- * or if the serverroot key does not exist (in which case
- * dir will contain an empty string), or -1 if there was
- * an error getting the key.
- */
-int ap_registry_get_server_root(ap_pool_t *p, char **buf)
+ap_status_t ap_registry_delete_key(const char *key)
{
- int rv;
+ ap_status_t rv;
+ HKEY hKey;
+ int nSize = 0;
+ char tempkey[MAX_PATH + 1];
+ char *parsekey;
- rv = ap_registry_get_key_int(p, REGKEY, "ServerRoot", NULL, 0, buf);
- if (rv < 0) {
- *buf = NULL;
+ ap_cpystrn(tempkey, key, sizeof(parsekey) - 1);
+ parsekey = strrchr(tempkey, '\\');
+
+ if (parsekey) {
+ *(parsekey++) = '\0';
+ rv = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
+ tempkey,
+ 0,
+ KEY_WRITE,
+ &hKey);
+
+ if (rv == ERROR_FILE_NOT_FOUND)
+ return APR_ENODIR;
+
+ if (rv != ERROR_SUCCESS)
+ return_error(rv);
}
+ else {
+ parsekey = tempkey;
+ hKey = HKEY_LOCAL_MACHINE;
+ }
+
+ rv = RegDeleteKey(hKey, key);
- return (rv < -1) ? -1 : 0;
+ if (hKey != HKEY_LOCAL_MACHINE)
+ RegCloseKey(hKey);
+
+ return_error(rv);
}
-char *ap_get_service_key(char *display_name)
+/* Clean up a way over complicated process.
+ *
+ * The return value is APR_SUCCESS, APR_ENOPATH, APR_NOTFOUND, or the OS error
+ */
+
+ap_status_t ap_registry_get_value(ap_pool_t *p, const char *key, const char *name, char **ppValue)
{
- size_t keylen = strlen(display_name);
- char *key2, *key = malloc(sizeof(SERVICEKEYPRE) + keylen
- + sizeof(SERVICEKEYPOST) + 1);
+ ap_status_t rv;
+ HKEY hKey;
+ int nSize = 0;
- key2 = ap_cpystrn(key, SERVICEKEYPRE, sizeof(SERVICEKEYPRE) + 1);
- key2 = ap_collapse_spaces(key2, display_name);
- key2 = ap_cpystrn(key2, SERVICEKEYPOST, sizeof(SERVICEKEYPOST) + 1);
-
- return(key);
-}
+ rv = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
+ key,
+ 0,
+ KEY_READ,
+ &hKey);
-int ap_registry_get_service_conf(ap_pool_t *p, char **buf, char *service_name)
-{
- int rv;
- char *key = ap_get_service_key(service_name);
+ if (rv == ERROR_FILE_NOT_FOUND)
+ return APR_ENODIR;
+
+ if (rv != ERROR_SUCCESS)
+ return_error(rv);
- rv = ap_registry_get_key_int(p, key, "ConfPath", NULL, 0, buf);
- if (rv < 0) {
- *buf = NULL;
- }
+ /* Find the size required for the data by passing NULL as the buffer
+ * pointer. On return nSize will contain the size required for the
+ * buffer if the return value is ERROR_SUCCESS.
+ */
+ rv = RegQueryValueEx(hKey,
+ name, /* key name */
+ NULL, /* reserved */
+ NULL, /* type */
+ NULL, /* for value */
+ &nSize); /* for size of "value" */
- free(key);
- return (rv < -1) ? -1 : 0;
-}
+ if (rv != ERROR_SUCCESS)
+ return_error(rv);
-/**********************************************************************
- * The rest of this file deals with storing keys or values in the registry
- */
+ *ppValue = ap_palloc(p, nSize);
+ rv = RegQueryValueEx(hKey,
+ name, /* key name */
+ NULL, /* reserved */
+ NULL, /* type */
+ *ppValue, /* for value */
+ &nSize); /* for size of "value" */
-char *ap_registry_parse_key(int index, char *key)
-{
- char *head = key, *skey;
- int i;
-
- if(!key)
- return(NULL);
+ if (rv == ERROR_FILE_NOT_FOUND)
+ rv = APR_NOTFOUND;
- for(i = 0; i <= index; i++)
- {
- if(key && key[0] == '\\')
- key++;
- if (!key)
- return(NULL);
- head = key;
- key = strchr(head, '\\');
- }
+ RegCloseKey(hKey);
- if(!key)
- return(strdup(head));
- *key = '\0';
- skey = strdup(head);
- *key = '\\';
- return(skey);
+ return_error(rv);
}
-/*
- * ap_registry_create_apache_key() creates the Apache registry key
- * (HLKM\SOFTWARE\Apache Software Foundation\Apache\version, as defined at the start
- * of this file), if it does not already exist. It will be called by
- * ap_registry_store_key_int() if it cannot open this key. This
- * function is intended to be called by ap_registry_store_key_int() if
- * the Apache key does not exist when it comes to store a data item.
- *
- * Returns 0 on success or -1 on error. If -1 is returned, the error will
- * already have been logged.
- */
-
-static int ap_registry_create_key(char *longkey)
+ap_status_t ap_registry_get_array(ap_pool_t *p, const char *key, const char *name, ap_array_header_t **parray)
{
- int index;
+ char *pValue;
+ char *tmp;
+ char **newelem;
+ ap_status_t rv;
HKEY hKey;
- HKEY hKeyNext;
- int retval;
- int rv;
- char *key;
+ int nSize = 0;
- hKey = HKEY_LOCAL_MACHINE;
- index = 0;
- retval = 0;
+ rv = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
+ key,
+ 0,
+ KEY_READ,
+ &hKey);
- /* Walk the tree, creating at each stage if necessary */
- while (key=ap_registry_parse_key(index,longkey)) {
- int result;
+ if (rv == ERROR_FILE_NOT_FOUND)
+ return APR_ENODIR;
+
+ if (rv != ERROR_SUCCESS)
+ return_error(rv);
- rv = RegCreateKeyEx(hKey,
- key, /* subkey */
- 0, /* reserved */
- NULL, /* class */
- REG_OPTION_NON_VOLATILE,
- KEY_WRITE,
- NULL,
- &hKeyNext,
- &result);
- if (rv != ERROR_SUCCESS) {
- do_error(rv, "RegCreateKeyEx(%s)", longkey);
- retval = -4;
- }
-
- /* Close the old key */
- rv = RegCloseKey(hKey);
- if (rv != ERROR_SUCCESS) {
- do_error(rv, "RegCloseKey", NULL);
- if (retval == 0) {
- /* Keep error status from RegCreateKeyEx, if any */
- retval = -4;
- }
- }
-
- if (retval) {
- break;
- }
+ /* Find the size required for the data by passing NULL as the buffer
+ * pointer. On return nSize will contain the size required for the
+ * buffer if the return value is ERROR_SUCCESS.
+ */
+ rv = RegQueryValueEx(hKey,
+ name, /* key name */
+ NULL, /* reserved */
+ NULL, /* type */
+ NULL, /* for value */
+ &nSize); /* for size of "value" */
- free(key);
- hKey = hKeyNext;
- index++;
+ if (rv == ERROR_FILE_NOT_FOUND) {
+ rv = APR_NOTFOUND;
}
-
- if (!key) {
- /* Close the final key we opened, if we walked the entire
- * tree
- */
- rv = RegCloseKey(hKey);
- if (rv != ERROR_SUCCESS) {
- do_error(rv, "RegCloseKey", NULL);
- if (retval == 0) {
- /* Keep error status from RegCreateKeyEx, if any */
- retval = -4;
- }
- }
+ else if (rv != ERROR_SUCCESS) {
+ return_error(rv);
}
- else
- free(key);
+ else
+ {
+ pValue = ap_palloc(p, nSize);
+ rv = RegQueryValueEx(hKey,
+ name, /* key name */
+ NULL, /* reserved */
+ NULL, /* type */
+ pValue, /* for value */
+ &nSize); /* for size of "value" */
+
+ nSize = 1; /* Element Count */
+ tmp = pValue;
+ while (tmp[0] || tmp[1])
+ {
+ if (!tmp[0])
+ ++nSize;
+ ++tmp;
+ }
+
+ *parray = ap_make_array(p, nSize, sizeof(char *));
+ tmp = pValue;
+ newelem = (char **) ap_push_array(*parray);
+ *newelem = tmp;
+ while (tmp[0] || tmp[1])
+ {
+ if (!tmp[0]) {
+ newelem = (char **) ap_push_array(*parray);
+ *newelem = tmp + 1;
+ }
+ ++tmp;
+ }
+ }
+
+ RegCloseKey(hKey);
- return retval;
+ return_error(rv);
}
/*
- * ap_registry_store_key_int() stores a value name and value under the
+ * ap_registry_store_key_value() stores a value name and value under the
* Apache registry key. If the Apache key does not exist it is created
* first. This function is intended to be called from a wrapper function
* in this file to set particular data values, such as
@@ -411,16 +316,15 @@ static int ap_registry_create_key(char *longkey)
* Returns 0 if the value name and data was stored successfully, or
* returns -1 if the Apache key does not exist (since we try to create
* this key, this should never happen), or -4 if any other error occurred
- * (these values are consistent with ap_registry_get_key_int()).
+ * (these values are consistent with ap_registry_get_key_value()).
* If the return value is negative then the error will already have been
* logged via aplog_error().
*/
-static int ap_registry_store_key_int(char *key, char *name, DWORD type, void *value, int value_size)
+ap_status_t ap_registry_store_value(const char *key, const char *name, const char *value)
{
long rv;
HKEY hKey;
- int retval;
rv = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
key,
@@ -428,97 +332,174 @@ static int ap_registry_store_key_int(char *key, char *name, DWORD type, void *va
KEY_WRITE,
&hKey);
- if (rv == ERROR_FILE_NOT_FOUND) {
- /* Key could not be opened -- try to create it
- */
- if (ap_registry_create_key(key) < 0) {
- /* Creation failed (error already reported) */
- return -4;
- }
+ if (rv == ERROR_FILE_NOT_FOUND)
+ {
+ rv = ap_registry_create_key(key);
+
+ if (rv != APR_SUCCESS)
+ return_error(rv);
- /* Now it has been created we should be able to open it
- */
rv = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
- key,
- 0,
- KEY_WRITE,
- &hKey);
-
- if (rv == ERROR_FILE_NOT_FOUND) {
- ap_log_error(APLOG_MARK,APLOG_WARNING|APLOG_NOERRNO,rv,NULL,
- "Registry does not contain key %s after creation",key);
- return -1;
- }
- }
+ key,
+ 0,
+ KEY_WRITE,
+ &hKey);
- if (rv != ERROR_SUCCESS) {
- do_error(rv, "RegOpenKeyEx HKLM\\%s", key);
- return -4;
+ if (rv == ERROR_FILE_NOT_FOUND)
+ return APR_ENODIR;
}
+ if (rv != ERROR_SUCCESS)
+ return_error(rv);
+
/* Now set the value and data */
rv = RegSetValueEx(hKey,
name, /* value key name */
0, /* reserved */
- type, /* type */
+ REG_SZ, /* type */
value, /* value data */
- (DWORD)value_size); /* for size of "value" */
+ (DWORD) strlen(value) + 1); /* for size of "value" */
- retval = 0; /* Return value */
+ if (rv == ERROR_SUCCESS) {
+ ap_log_error(APLOG_MARK,APLOG_INFO|APLOG_NOERRNO,rv,NULL,
+ "Registry stored HKLM\\" REGKEY "\\%s value %s", key, value);
+ }
- if (rv != ERROR_SUCCESS) {
- do_error(rv, "RegQueryValueEx(key %s)", key);
- retval = -4;
+ /* Make sure we close the key even if there was an error storing
+ * the data
+ */
+ RegCloseKey(hKey);
+
+ return_error(rv);
+}
+
+ap_status_t ap_registry_store_array(ap_pool_t *p, const char *key, const char *name, int nelts, char const* const* elts)
+{
+ int bufsize, i;
+ char *buf, *tmp;
+ long rv;
+ HKEY hKey;
+
+ rv = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
+ key,
+ 0,
+ KEY_WRITE,
+ &hKey);
+
+ if (rv == ERROR_FILE_NOT_FOUND)
+ {
+ rv = ap_registry_create_key(key);
+
+ if (rv != APR_SUCCESS)
+ return_error(rv);
+
+ rv = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
+ key,
+ 0,
+ KEY_WRITE,
+ &hKey);
+
+ if (rv == ERROR_FILE_NOT_FOUND)
+ return APR_ENODIR;
}
- else {
+
+ if (rv != ERROR_SUCCESS)
+ return_error(rv);
+
+ bufsize = 1; /* For trailing second null */
+ for (i = 0; i < nelts; ++i)
+ {
+ bufsize += strlen(elts[i]) + 1;
+ }
+ if (!nelts)
+ ++bufsize;
+
+ buf = ap_palloc(p, bufsize);
+ tmp = buf;
+ for (i = 0; i < nelts; ++i)
+ {
+ strcpy(tmp, elts[i]);
+ tmp += strlen(elts[i]) + 1;
+ }
+ if (!nelts)
+ (*tmp++) = '\0';
+ *tmp = '\0'; /* Trailing second null */
+
+ /* Now set the value and data */
+ rv = RegSetValueEx(hKey,
+ name, /* value key name */
+ 0, /* reserved */
+ REG_MULTI_SZ, /* type */
+ buf, /* value data */
+ (DWORD) bufsize); /* for size of "value" */
+
+ if (rv == ERROR_SUCCESS) {
ap_log_error(APLOG_MARK,APLOG_INFO|APLOG_NOERRNO,rv,NULL,
- "Registry stored HKLM\\" REGKEY "\\%s value %s", key,
- type == REG_SZ ? value : "(not displayable)");
+ "Registry stored HKLM\\" REGKEY "\\%s", key);
}
/* Make sure we close the key even if there was an error storing
* the data
*/
- rv = RegCloseKey(hKey);
- if (rv != ERROR_SUCCESS) {
- do_error(rv, "RegCloseKey HKLM\\%s", key);
- if (retval == 0) {
- /* Keep error status from RegQueryValueEx, if any */
- retval = -4;
- }
- }
+ RegCloseKey(hKey);
+
+ return_error(rv);
+}
- return retval;
+/* A key or value that does not exist is _not_ an error while deleting. */
+
+ap_status_t ap_registry_delete_value(const char *key, const char *name)
+{
+ ap_status_t rv;
+ HKEY hKey;
+
+ rv = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
+ key,
+ 0,
+ KEY_WRITE,
+ &hKey);
+
+ if (rv == ERROR_FILE_NOT_FOUND)
+ return APR_SUCCESS;
+
+ if (rv != ERROR_SUCCESS)
+ return_error(rv);
+
+ rv = RegDeleteValue(hKey, name);
+
+ if (rv == ERROR_FILE_NOT_FOUND)
+ rv = APR_SUCCESS;
+
+ RegCloseKey(hKey);
+ return_error(rv);
}
/*
- * Sets the service confpath value within the registry. Returns 0 on success
- * or -1 on error. If -1 is return the error will already have been
- * logged via aplog_error().
+ * Get the server root from the registry into 'dir' which is
+ * size bytes long. Returns 0 if the server root was found
+ * or if the serverroot key does not exist (in which case
+ * dir will contain an empty string), or -1 if there was
+ * an error getting the key.
*/
-
-int ap_registry_set_service_conf(char *conf, char *display_name)
+ap_status_t ap_registry_get_server_root(ap_pool_t *p, char **buf)
{
- int rv;
- char *key = ap_get_service_key(display_name);
-
- rv = ap_registry_store_key_int(key, "ConfPath", REG_SZ, conf, strlen(conf)+1);
- free(key);
+ ap_status_t rv;
- return rv < 0 ? -1: 0;
+ rv = ap_registry_get_value(p, REGKEY, "ServerRoot", buf);
+ if (rv)
+ *buf = NULL;
+
+ return rv;
}
+
/*
* Sets the serverroot value within the registry. Returns 0 on success
* or -1 on error. If -1 is return the error will already have been
* logged via aplog_error().
*/
-int ap_registry_set_server_root(char *dir)
+ap_status_t ap_registry_set_server_root(char *dir)
{
- int rv;
-
- rv = ap_registry_store_key_int(REGKEY, "ServerRoot", REG_SZ, dir, strlen(dir)+1);
-
- return rv < 0 ? -1 : 0;
+ return ap_registry_store_value(REGKEY, "ServerRoot", dir);
}
diff --git a/server/mpm/winnt/service.c b/server/mpm/winnt/service.c
index 8304cc1cf0..b7b6960471 100644
--- a/server/mpm/winnt/service.c
+++ b/server/mpm/winnt/service.c
@@ -56,47 +56,70 @@
* University of Illinois, Urbana-Champaign.
*/
-#ifdef WIN32
+/* This module ALONE requires the window message API from user.h
+ * and the default APR include of windows.h will omit it, so
+ * preload the API symbols now...
+ */
+
+#ifndef _WIN32_WINNT
+#define _WIN32_WINNT 0x0400
+#endif
+#ifndef NOGDI
+#define NOGDI
+#endif
+#ifndef NONLS
+#define NONLS
+#endif
+#ifndef NOMCX
+#define NOMCX
+#endif
+#ifndef NOIME
+#define NOIME
+#endif
+#include <windows.h>
+#include <winsock2.h>
+#include <mswsock.h>
#define CORE_PRIVATE
-#include "main_win32.h"
-#include "ap_config.h"
#include "httpd.h"
#include "http_conf_globals.h"
#include "http_log.h"
-#include "service.h"
-#include "registry.h"
#include "ap_mpm.h"
-#include "..\..\modules\mpm\winnt\winnt.h"
-
-typedef void (CALLBACK *ap_completion_t)();
-API_VAR_EXPORT ap_completion_t ap_mpm_init_complete;
-API_VAR_EXPORT char *ap_server_argv0;
+#include "winnt.h"
+char *service_name = NULL;
+char *display_name = NULL;
+char *signal_arg = NULL;
+
static struct
{
- int (*main_fn)(int, char **);
- event *stop_event;
- int connected;
- SERVICE_STATUS_HANDLE hServiceStatus;
- char *name;
- int exit_status;
+ HANDLE mpm_thread; /* primary thread handle of the apache server */
+ HANDLE service_thread; /* thread service/monitor handle */
+ DWORD service_thread_id;/* thread service/monitor ID */
+ HANDLE signal_monitor; /* service monitor thread signal event */
SERVICE_STATUS ssStatus;
- FILE *logFile;
- char *service_dir;
- HANDLE threadService;
- HANDLE threadMonitor;
+ SERVICE_STATUS_HANDLE hServiceStatus;
} globdat;
-static void WINAPI service_main_fn(DWORD, LPTSTR *);
-static void WINAPI service_ctrl(DWORD ctrlCode);
static int ReportStatusToSCMgr(int currentState, int exitCode, int waitHint);
-static int ap_start_service(SC_HANDLE);
-static int ap_stop_service(SC_HANDLE);
-static LRESULT CALLBACK MonitorWin9xWndProc(HWND hWnd, UINT msg,
- WPARAM wParam, LPARAM lParam)
+/* The service configuration's is stored under the following trees:
+ *
+ * HKLM\System\CurrentControlSet\Services\[service name]
+ *
+ * \DisplayName
+ * \ImagePath (NT Only)
+ * \Parameters\ConfigArgs
+ *
+ * For Win9x, the launch service command is stored under:
+ *
+ * HKLM\Software\Microsoft\Windows\CurrentVersion\RunServices\[service name]
+ */
+
+
+static LRESULT CALLBACK monitor_service_9x_proc(HWND hWnd, UINT msg,
+ WPARAM wParam, LPARAM lParam)
{
/* This is the WndProc procedure for our invisible window.
* When the user shuts down the system, this window is sent
@@ -105,15 +128,16 @@ static LRESULT CALLBACK MonitorWin9xWndProc(HWND hWnd, UINT msg,
*/
if ((msg == WM_ENDSESSION) && (lParam != ENDSESSION_LOGOFF))
{
- ap_start_shutdown();
+ signal_parent(0);
if (wParam)
- WaitForSingleObject(globdat.threadService, 30000);
+ /* Don't leave this message until we are dead! */
+ WaitForSingleObject(globdat.mpm_thread, 30000);
return 0;
}
return (DefWindowProc(hWnd, msg, wParam, lParam));
}
-static DWORD WINAPI MonitorWin9xEvents(LPVOID initEvent)
+static DWORD WINAPI monitor_service_9x_thread(LPVOID initEvent)
{
/* When running on Windows 9x, the ConsoleCtrlHandler is _NOT_
* called when the system is shutdown. So create an invisible
@@ -124,7 +148,7 @@ static DWORD WINAPI MonitorWin9xEvents(LPVOID initEvent)
HWND hwndMain;
wc.style = CS_GLOBALCLASS;
- wc.lpfnWndProc = MonitorWin9xWndProc;
+ wc.lpfnWndProc = monitor_service_9x_proc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = NULL;
@@ -132,24 +156,24 @@ static DWORD WINAPI MonitorWin9xEvents(LPVOID initEvent)
wc.hCursor = NULL;
wc.hbrBackground = NULL;
wc.lpszMenuName = NULL;
- wc.lpszClassName = "ApacheWin9xService";
+ wc.lpszClassName = "ApacheWin9xServiceMonitor";
if (RegisterClass(&wc))
{
/* Create an invisible window */
- hwndMain = CreateWindow("ApacheWin9xService", "Apache",
+ hwndMain = CreateWindow(wc.lpszClassName, display_name,
WS_OVERLAPPEDWINDOW & ~WS_VISIBLE,
CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT,
NULL, NULL, NULL, NULL);
if (hwndMain)
{
- MSG msg;
- /* If we succeed, eliminate the console window.
+ MSG msg;
+ /* If we succeed, eliminate the console window.
* Signal the parent we are all set up, and
* watch the message queue while the window lives.
*/
- FreeConsole();
+ FreeConsole();
SetEvent((HANDLE) initEvent);
while (GetMessage(&msg, NULL, 0, 0))
{
@@ -160,238 +184,144 @@ static DWORD WINAPI MonitorWin9xEvents(LPVOID initEvent)
DispatchMessage(&msg);
}
}
- globdat.threadMonitor = 0;
+ globdat.service_thread = 0;
return(0);
}
}
/* We failed or are soon to die...
* we won't need this much longer
*/
- SetEvent((HANDLE) initEvent);
- globdat.threadMonitor = 0;
+ SetEvent(globdat.signal_monitor);
+ globdat.service_thread = 0;
return(0);
}
-static void CALLBACK report_service9x_running()
+void service_9x_stopped(void)
{
-}
-
-int service9x_main(int (*main_fn)(int, char **), int argc, char **argv )
-{
- DWORD (WINAPI *RegisterServiceProcess)(DWORD, DWORD);
- HINSTANCE hkernel;
- DWORD threadId;
-
- globdat.threadService = GetCurrentThread();
-
- /* Obtain a handle to the kernel library */
- hkernel = LoadLibrary("KERNEL32.DLL");
- if (hkernel) {
- /* Find the RegisterServiceProcess function */
- RegisterServiceProcess = (DWORD (WINAPI *)(DWORD, DWORD))
- GetProcAddress(hkernel, "RegisterServiceProcess");
- if (RegisterServiceProcess) {
- if (RegisterServiceProcess((DWORD)NULL, 1)) {
- HANDLE installed = CreateEvent(NULL, FALSE, FALSE, NULL);
- globdat.threadMonitor = CreateThread(NULL, 0,
- MonitorWin9xEvents,
- (LPVOID) installed,
- 0, &threadId);
- WaitForSingleObject(installed, 30000);
- CloseHandle(installed);
- }
- }
- }
-
- /* Run the service */
- globdat.exit_status = main_fn(argc, argv);
-
/* Still have a thread & window to clean up, so signal now */
- if (globdat.threadMonitor)
+ if (globdat.service_thread)
{
- PostThreadMessage(threadId, WM_CLOSE, 0, 0);
- WaitForSingleObject(globdat.threadMonitor, 30000);
+ PostThreadMessage(globdat.service_thread_id, WM_CLOSE, 0, 0);
+ // TODO: Test Possible (30 second) deadlock if we are shutting down
+ WaitForSingleObject(globdat.service_thread, 30000);
}
/* When the service quits, remove it from the
system service table */
- if (RegisterServiceProcess)
- RegisterServiceProcess((DWORD)NULL, 0);
-
- /* Free the kernel library */
- if (hkernel)
- FreeLibrary(hkernel);
+ RegisterServiceProcess(0, 0);
- return (globdat.exit_status);
+ return;
}
-int servicent_main(int (*main_fn)(int, char **), int argc, char **argv )
+static BOOL CALLBACK console_control_handler(DWORD ctrl_type)
{
- SERVICE_TABLE_ENTRY dispatchTable[] =
+ switch (ctrl_type)
{
- { "", service_main_fn },
- { NULL, NULL }
- };
-
- globdat.main_fn = main_fn;
- globdat.stop_event = CreateEvent(NULL, 0, 0, "apache-signal");
- globdat.connected = 1;
- globdat.service_dir = argv[0];
+ case CTRL_C_EVENT:
+ case CTRL_BREAK_EVENT:
+ fprintf(stderr, "Apache server interrupted...\n");
+ /* for Interrupt signals, shut down the server.
+ * Tell the system we have dealt with the signal
+ * without waiting for Apache to terminate.
+ */
+ signal_parent(0);
+ return TRUE;
- if(!StartServiceCtrlDispatcher(dispatchTable))
- {
- /* This is a genuine failure of the SCM. */
- ap_log_error(APLOG_MARK, APLOG_ERR, GetLastError(), NULL,
- "Error starting service control dispatcher");
- return(globdat.exit_status);
- }
- else
- {
- return(globdat.exit_status);
+ case CTRL_CLOSE_EVENT:
+ case CTRL_LOGOFF_EVENT:
+ case CTRL_SHUTDOWN_EVENT:
+ /* for Terminate signals, shut down the server.
+ * Wait for Apache to terminate, but respond
+ * after a reasonable time to tell the system
+ * that we did attempt to shut ourself down.
+ * THESE EVENTS WILL NOT OCCUR UNDER WIN9x!
+ */
+ fprintf(stderr, "Apache server shutdown initiated...\n");
+ signal_parent(0);
+ Sleep(30000);
+ return TRUE;
}
+
+ /* We should never get here, but this is (mostly) harmless */
+ return FALSE;
}
-static void CALLBACK report_servicent_started()
+static void stop_console_handler(void)
{
- ReportStatusToSCMgr(
- SERVICE_RUNNING, // service state
- NO_ERROR, // exit code
- 0); // wait hint
+ SetConsoleCtrlHandler(console_control_handler, FALSE);
}
-void __stdcall service_main_fn(DWORD argc, LPTSTR *argv)
+void mpm_start_console_handler(void)
{
- int i, new_argc;
- char **new, *server_root, *tmp;
- char *server_confname = SERVER_CONFIG_FILE;
- ap_array_header_t *cmdtbl;
- ap_pool_t *pwincmd;
-
- ap_create_pool(&pwincmd, NULL);
- if (pwincmd == NULL) {
- exit(0);
- }
-
- ap_server_argv0 = globdat.name = argv[0];
- cmdtbl = ap_make_array(pwincmd, 1, sizeof(char *));
+ SetConsoleCtrlHandler(console_control_handler, TRUE);
+ atexit(stop_console_handler);
+}
- server_root = ap_pstrdup(pwincmd, globdat.service_dir);
- tmp = strrchr(server_root, '\\');
- *tmp = '\0';
+/* Special situation - children of services need to mind their
+ * P's & Q's and wait quietly, ignoring the mean OS signaling
+ * shutdown and other horrors, to kill them gracefully...
+ */
- if(!(globdat.hServiceStatus = RegisterServiceCtrlHandler( globdat.name, service_ctrl)))
+static BOOL CALLBACK child_control_handler(DWORD ctrl_type)
+{
+ switch (ctrl_type)
{
- ap_log_error(APLOG_MARK, APLOG_ERR, GetLastError(), NULL,
- "Failure registering service handler");
- return;
- }
-
- ReportStatusToSCMgr(
- SERVICE_START_PENDING, // service state
- NO_ERROR, // exit code
- 3000); // wait hint
-
- ap_mpm_init_complete = report_servicent_started;
+ case CTRL_C_EVENT:
+ case CTRL_BREAK_EVENT:
+ /* for Interrupt signals, ignore them.
+ * The system will also signal the parent process,
+ * which will terminate Apache.
+ */
+ return TRUE;
- /* Fetch server_conf from the registry
- * Rebuild argv and argc adding the -d server_root and -f server_conf then
- * call apache_main
- */
- ap_registry_get_service_conf(pwincmd, &server_confname, argv[0]);
- for (i = 0; i < argc ; i++) {
- new = (char **) ap_push_array(cmdtbl);
- *new = argv[i];
+ case CTRL_CLOSE_EVENT:
+ case CTRL_LOGOFF_EVENT:
+ case CTRL_SHUTDOWN_EVENT:
+ /* for Shutdown signals, ignore them, but... .
+ * The system will also signal the parent process,
+ * which will terminate Apache, so we need to wait.
+ */
+ Sleep(30000);
+ return TRUE;
}
- /* Add server_confname to the argument list */
- new = (char **) ap_push_array(cmdtbl);
- *new = "-f";
- new = (char **) ap_push_array(cmdtbl);
- *new = server_confname;
- new = (char **) ap_push_array(cmdtbl);
- *new = "-d";
- new = (char **) ap_push_array(cmdtbl);
- *new = server_root;
- new_argc = argc + 4;
-
- globdat.exit_status = (*globdat.main_fn)( new_argc, (char**) cmdtbl->elts );
-
- ReportStatusToSCMgr(SERVICE_STOPPED, NO_ERROR, 0);
-
- return;
+
+ /* We should never get here, but this is (mostly) harmless */
+ return FALSE;
}
-void service_set_status(int status)
+// TODO: We really need to play the RegisterServiceProcess game
+// if this is the child of the Win9x service process...
+// and if that isn't bad enought... a shutdown thread window
+// is really the ticket... ick.
+
+static void stop_child_console_handler(void)
{
- ReportStatusToSCMgr(status, NO_ERROR, 3000);
+ SetConsoleCtrlHandler(child_control_handler, FALSE);
}
-
-
-//
-// FUNCTION: service_ctrl
-//
-// PURPOSE: This function is called by the SCM whenever
-// ControlService() is called on this service.
-//
-// PARAMETERS:
-// dwCtrlCode - type of control requested
-//
-// RETURN VALUE:
-// none
-//
-// COMMENTS:
-//
-VOID WINAPI service_ctrl(DWORD dwCtrlCode)
+void mpm_start_child_console_handler(void)
{
- int state;
-
- state = globdat.ssStatus.dwCurrentState;
- switch(dwCtrlCode)
- {
- // Stop the service.
- //
- case SERVICE_CONTROL_STOP:
- state = SERVICE_STOP_PENDING;
- ap_start_shutdown();
- break;
-
- // Update the service status.
- //
- case SERVICE_CONTROL_INTERROGATE:
- break;
-
- // invalid control code
- //
- default:
- break;
-
- }
-
- ReportStatusToSCMgr(state, NO_ERROR, 0);
+ SetConsoleCtrlHandler(child_control_handler, TRUE);
+ atexit(stop_child_console_handler);
}
-int ReportStatusToSCMgr(int currentState, int exitCode, int waitHint)
+/**********************************
+ WinNT service control management
+ **********************************/
+
+static int ReportStatusToSCMgr(int currentState, int exitCode, int waitHint)
{
- static int firstTime = 1;
static int checkPoint = 1;
- int rv;
+ int rv = APR_SUCCESS;
- if(firstTime)
- {
- firstTime = 0;
- globdat.ssStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
- globdat.ssStatus.dwServiceSpecificExitCode = 0;
- globdat.ssStatus.dwCheckPoint = 1;
- }
-
- if(globdat.connected)
+ if(globdat.hServiceStatus)
{
- if (currentState == SERVICE_START_PENDING)
- globdat.ssStatus.dwControlsAccepted = 0;
- else
+ if (currentState == SERVICE_RUNNING)
globdat.ssStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
-
+ else
+ globdat.ssStatus.dwControlsAccepted = 0;
+
globdat.ssStatus.dwCurrentState = currentState;
globdat.ssStatus.dwWin32ExitCode = exitCode;
if(waitHint)
@@ -409,289 +339,592 @@ int ReportStatusToSCMgr(int currentState, int exitCode, int waitHint)
rv = SetServiceStatus(globdat.hServiceStatus, &globdat.ssStatus);
}
- return(1);
+ return(rv);
}
-void InstallServiceNT(char *display_name, char *conf)
+/* handle the SCM's ControlService() callbacks to our service */
+
+static VOID WINAPI service_nt_ctrl(DWORD dwCtrlCode)
{
- SC_HANDLE schService;
- SC_HANDLE schSCManager;
+ if (dwCtrlCode == SERVICE_CONTROL_STOP)
+ /* Reports our status change itself */
+ signal_parent(0);
+
+ ReportStatusToSCMgr(globdat.ssStatus.dwCurrentState, NO_ERROR, 0);
+}
+
+/* service_nt_main_fn is outside of the call stack and outside of the
+ * primary server thread... so now we _really_ need a placeholder!
+ * The winnt_rewrite_args has created and shared mpm_new_argv with us.
+ */
+extern ap_array_header_t *mpm_new_argv;
- TCHAR szPath[512];
- TCHAR szQuotedPath[512];
- char service_name[256];
+static void __stdcall service_nt_main_fn(DWORD argc, LPTSTR *argv)
+{
+ /* args and service names live in the same pool */
+ mpm_service_set_name(mpm_new_argv->cont, argv[0]);
- printf("Installing the %s service to use %s\n", display_name, conf);
+ globdat.ssStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
+ globdat.ssStatus.dwCurrentState = SERVICE_START_PENDING;
+ globdat.ssStatus.dwServiceSpecificExitCode = 0;
+ globdat.ssStatus.dwCheckPoint = 1;
- if (GetModuleFileName( NULL, szPath, 512 ) == 0)
+ if(!(globdat.hServiceStatus = RegisterServiceCtrlHandler(argv[0], service_nt_ctrl)))
{
ap_log_error(APLOG_MARK, APLOG_ERR, GetLastError(), NULL,
- "GetModuleFileName failed");
+ "Failure registering service handler");
return;
}
- /* Remove spaces from display name to create service name */
- ap_collapse_spaces(service_name, display_name);
-
- ap_snprintf(szQuotedPath, 512, "\"%s\"", szPath);
-
- schSCManager = OpenSCManager(
- NULL, // machine (NULL == local)
- NULL, // database (NULL == default)
- SC_MANAGER_ALL_ACCESS // access required
- );
- if (!schSCManager) {
- ap_log_error(APLOG_MARK, APLOG_ERR, GetLastError(), NULL,
- "OpenSCManager failed");
+ ReportStatusToSCMgr(globdat.ssStatus.dwCurrentState, // service state
+ NO_ERROR, // exit code
+ 3000); // wait hint, 3 seconds more
+
+ /* We need to append all the command arguments passed via StartService()
+ * to our running service... which just got here via the SCM...
+ * but we hvae no interest in argv[0] for the mpm_new_argv list.
+ */
+ if (argc > 1)
+ {
+ char **cmb_data;
+ cmb_data = ap_palloc(mpm_new_argv->cont,
+ (mpm_new_argv->nelts + argc - 1) * sizeof(char *));
+
+ /* mpm_new_argv remains first (of lower significance) */
+ memcpy (cmb_data, mpm_new_argv->elts,
+ mpm_new_argv->elt_size * mpm_new_argv->nelts);
+
+ /* Service args follow from StartService() invocation */
+ memcpy (cmb_data + mpm_new_argv->nelts, argv + 1,
+ mpm_new_argv->elt_size * (argc - 1));
+
+ /* The replacement arg list is complete */
+ mpm_new_argv->elts = (char*) cmb_data;
+ mpm_new_argv->nalloc = mpm_new_argv->nelts += argc - 1;
}
- else {
- /* Added dependencies for the following: TCPIP, AFD
- * AFD is the winsock handler, TCPIP is self evident
- *
- * RPCSS is the Remote Procedure Call (RPC) Locator
- * required for DCOM communication. I am far from
- * convinced we should toggle this, but be warned that
- * future apache modules or ISAPI dll's may depend on it.
+
+ /* Let the main thread continue now... but hang on to the
+ * signal_monitor event so we can take further action
*/
- schService = CreateService(
- schSCManager, // SCManager database
- service_name, // name of service
- display_name, // name to display
- SERVICE_ALL_ACCESS, // desired access
- SERVICE_WIN32_OWN_PROCESS, // service type
- SERVICE_AUTO_START, // start type
- SERVICE_ERROR_NORMAL, // error control type
- szQuotedPath, // service's binary
- NULL, // no load ordering group
- NULL, // no tag identifier
- "Tcpip\0Afd\0", // dependencies
- NULL, // LocalSystem account
- NULL); // no password
-
- if (schService) {
- CloseServiceHandle(schService);
-
- /* Now store the server_root in the registry */
- if(!ap_registry_set_service_conf(conf, service_name))
- printf("The %s service has been installed successfully.\n", display_name);
- }
- else {
- ap_log_error(APLOG_MARK, APLOG_ERR, GetLastError(), NULL,
- "CreateService failed");
- }
+ PulseEvent(globdat.signal_monitor);
- CloseServiceHandle(schSCManager);
- }
+ // TODO: Wait on mpm_thread as well!!!
+ // Hey, we could even add a timeout during startup
+ // to tickle the SCM every second or few till we finish.
+ WaitForSingleObject(globdat.signal_monitor, INFINITE);
+
+ /* This function only returns when we are killed */
}
-
-void RemoveServiceNT(char *display_name)
+DWORD WINAPI service_nt_dispatch_thread(LPVOID nada)
{
- SC_HANDLE schService;
- SC_HANDLE schSCManager;
- char service_name[256];
-
- printf("Removing the %s service\n", display_name);
-
- /* Remove spaces from display name to create service name */
- ap_collapse_spaces(service_name, display_name);
-
- schSCManager = OpenSCManager(
- NULL, // machine (NULL == local)
- NULL, // database (NULL == default)
- SC_MANAGER_ALL_ACCESS // access required
- );
- if (!schSCManager) {
- ap_log_error(APLOG_MARK, APLOG_ERR, GetLastError(), NULL,
- "OpenSCManager failed");
- }
- else {
- schService = OpenService(schSCManager, service_name, SERVICE_ALL_ACCESS);
+ ap_status_t rv = APR_SUCCESS;
- if (schService == NULL) {
- /* Could not open the service */
- ap_log_error(APLOG_MARK, APLOG_ERR, GetLastError(), NULL,
- "OpenService failed");
- }
- else {
- /* try to stop the service */
- ap_stop_service(schService);
-
- // now remove the service
- if (DeleteService(schService) == 0)
- ap_log_error(APLOG_MARK, APLOG_ERR, GetLastError(), NULL,
- "DeleteService failed");
- else
- printf("The %s service has been removed successfully.\n", display_name);
- CloseServiceHandle(schService);
- }
- /* SCM removes registry parameters */
- CloseServiceHandle(schSCManager);
- }
+ SERVICE_TABLE_ENTRY dispatchTable[] =
+ {
+ { "", service_nt_main_fn },
+ { NULL, NULL }
+ };
+
+ if (!StartServiceCtrlDispatcher(dispatchTable))
+ {
+ /* This is a genuine failure of the SCM. */
+ rv = GetLastError();
+ ap_log_error(APLOG_MARK, APLOG_ERR, rv, NULL,
+ "Error starting service control dispatcher");
+ };
+ globdat.service_thread = 0;
+ return (rv);
+}
+void mpm_service_nt_stopping(void)
+{
+ ReportStatusToSCMgr(SERVICE_STOP_PENDING, // service state
+ NO_ERROR, // exit code
+ 1000); // wait hint
}
-/* A hack to determine if we're running as a service without waiting for
- * the SCM to fail; if AllocConsole succeeds, we're a service.
- */
+static void service_nt_stopped(void)
+{
+ ReportStatusToSCMgr(SERVICE_STOPPED, // service state
+ NO_ERROR, // exit code
+ 0); // wait hint
-BOOL isProcessService() {
- if( !AllocConsole() )
- return FALSE;
- FreeConsole();
- return TRUE;
+ /* Cause the instant closure of the service_nt_main_fn */
+ SetEvent(globdat.signal_monitor);
}
-/* Determine is service_name is a valid service
- * Simplify by testing the registry rather than the SCM
- * as this will work on both WinNT and Win9x.
- */
-
-BOOL isValidService(ap_pool_t *p, char *display_name) {
- char service_name[256];
- char *service_conf;
+ap_status_t mpm_service_set_name(ap_pool_t *p, char *name)
+{
+ char *key_name;
+
+ // TODO: the display name might have been modified in the registry...
+ // Win9x could walk for DisplayName in the services entries
+ service_name = ap_palloc(p, strlen(name) + 1);
+ ap_collapse_spaces(service_name, name);
+ key_name = ap_psprintf(p, SERVICECONFIG, service_name);
+ if (ap_registry_get_value(p, key_name, "DisplayName", &display_name) == APR_SUCCESS)
+ return APR_SUCCESS;
+
+ display_name = ap_pstrdup(p, name);
+ return APR_NOTFOUND;
+}
- /* Remove spaces from display name to create service name */
- ap_collapse_spaces(service_name, display_name);
+ap_status_t mpm_merge_service_args(ap_pool_t *p,
+ ap_array_header_t *args,
+ int fixed_args)
+{
+ ap_array_header_t *svc_args = NULL;
+ char conf_key[MAX_PATH];
+ char **cmb_data;
+ ap_status_t rv;
+
+ ap_snprintf(conf_key, sizeof(conf_key), SERVICEPARAMS, service_name);
+ rv = ap_registry_get_array(p, conf_key, "ConfigArgs", &svc_args);
+ if (rv != APR_SUCCESS) {
+ // TODO: More message?
+ return (rv);
+ }
- if(ap_registry_get_service_conf(p, &service_conf, service_name)) {
- return TRUE;
+ if (!svc_args || svc_args->nelts == 0) {
+ return (APR_SUCCESS);
}
+
+ /* Now we have the service_name arg, and the mpm_runservice_nt()
+ * call appended the arguments passed by StartService(), so it's
+ * time to _prepend_ the default arguments for the server from
+ * the service's default arguments (all others override them)...
+ */
+ cmb_data = ap_palloc(p, (args->nelts + svc_args->nelts) * sizeof(char *));
+
+ /* First three args (argv[0], -f, path) remain first */
+ memcpy (cmb_data, args->elts, args->elt_size * fixed_args);
- return FALSE;
+ /* Service args follow from service registry array */
+ memcpy (cmb_data + fixed_args, svc_args->elts,
+ svc_args->elt_size * svc_args->nelts);
+
+ /* Remaining new args follow */
+ memcpy (cmb_data + fixed_args + svc_args->nelts,
+ (char**) args->elts + fixed_args,
+ args->elt_size * (args->nelts - fixed_args));
+
+ args->elts = (char*) cmb_data;
+ args->nalloc = (args->nelts += svc_args->nelts);
-#if 0
- SC_HANDLE schSCM, schSVC;
- int Err;
+ return APR_SUCCESS;
+}
- if (!(schSCM = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS))) {
- ap_log_error(APLOG_MARK, APLOG_ERR, GetLastError(), NULL,
- "OpenSCManager failed");
- return FALSE;
- }
+ap_status_t mpm_service_to_start(void)
+{
+ if (osver.dwPlatformId == VER_PLATFORM_WIN32_NT)
+ {
+ globdat.signal_monitor = CreateEvent(NULL, FALSE, FALSE, NULL);
+ globdat.service_thread = CreateThread(NULL, 0,
+ service_nt_dispatch_thread,
+ NULL, 0,
+ &globdat.service_thread_id);
+
+ // TODO: Add service_thread to this wait as well
+ WaitForSingleObject(globdat.signal_monitor, 45000);
- if ((schSVC = OpenService(schSCM, service_name, SERVICE_ALL_ACCESS))) {
- CloseServiceHandle(schSVC);
- CloseServiceHandle(schSCM);
- return TRUE;
+ if (!globdat.service_thread)
+ return APR_ENOTHREAD;
+
+ /* SetEvent(globdat.signal_monitor) to clean up the SCM thread */
+ atexit(service_nt_stopped);
}
+ else /* osver.dwPlatformId != VER_PLATFORM_WIN32_NT */
+ {
+ globdat.mpm_thread = GetCurrentThread();
+
+ if (RegisterServiceProcess(0, 1)) {
+ globdat.signal_monitor = CreateEvent(NULL, FALSE, FALSE, NULL);
+ globdat.service_thread = CreateThread(NULL, 0,
+ monitor_service_9x_thread,
+ NULL, 0,
+ &globdat.service_thread_id);
+ // TODO: Add service_thread to the wait as well.
+ WaitForSingleObject(globdat.signal_monitor, 30000);
+ }
- Err = GetLastError();
- if (Err != ERROR_SERVICE_DOES_NOT_EXIST && Err != ERROR_INVALID_NAME)
- ap_log_error(APLOG_MARK, APLOG_ERR, Err, NULL,
- "OpenService failed");
+ if (!globdat.service_thread)
+ return APR_ENOTHREAD;
- return FALSE;
-#endif
+ /* PostThreadMessage to clean up the hidden monitor window */
+ atexit(service_9x_stopped);
+ }
+ return APR_SUCCESS;
}
-/* Although the Win9x service enhancement added -k startservice,
- * it is never processed here, so we still ignore that param.
- */
-int send_signal_to_service(char *display_name, char *sig) {
- SC_HANDLE schService;
- SC_HANDLE schSCManager;
- char service_name[256];
- int success = FALSE;
-
- enum { start, restart, stop, unknown } action;
- static char *param[] = { "start", "restart", "shutdown" };
- static char *participle[] = { "starting", "restarting", "stopping" };
- static char *past[] = { "started", "restarted", "stopped" };
-
- for (action = start; action < unknown; action++)
- if (!strcasecmp(sig, param[action]))
- break;
-
- if (action == unknown) {
- printf("signal must be start, restart, or shutdown\n");
- return FALSE;
+ap_status_t mpm_service_started(void)
+{
+ if (osver.dwPlatformId == VER_PLATFORM_WIN32_NT)
+ {
+ ReportStatusToSCMgr(SERVICE_RUNNING, // service state
+ NO_ERROR, // exit code
+ 0); // wait hint
}
+ return APR_SUCCESS;
+}
- /* Remove spaces from display name to create service name */
- ap_collapse_spaces(service_name, display_name);
+ap_status_t mpm_service_install(ap_pool_t *ptemp, int argc,
+ char const* const* argv)
+{
+ char key_name[MAX_PATH];
+ char exe_path[MAX_PATH];
+ char *launch_cmd;
+ ap_status_t(rv);
+
+ printf("Installing the %s service\n", display_name);
- schSCManager = OpenSCManager(
- NULL, // machine (NULL == local)
- NULL, // database (NULL == default)
- SC_MANAGER_ALL_ACCESS // access required
- );
- if (!schSCManager) {
- ap_log_error(APLOG_MARK, APLOG_ERR, GetLastError(), NULL,
- "OpenSCManager failed");
+ if (GetModuleFileName(NULL, exe_path, sizeof(exe_path)) == 0)
+ {
+ ap_status_t rv = GetLastError();
+ ap_log_error(APLOG_MARK, APLOG_ERR, rv, NULL,
+ "GetModuleFileName failed");
+ return rv;
}
- else {
- schService = OpenService(schSCManager, service_name, SERVICE_ALL_ACCESS);
- if (schService == NULL) {
- /* Could not open the service */
- ap_log_error(APLOG_MARK, APLOG_ERR, GetLastError(), NULL,
- "OpenService failed");
+ if (osver.dwPlatformId == VER_PLATFORM_WIN32_NT)
+ {
+ SC_HANDLE schService;
+ SC_HANDLE schSCManager;
+
+ // TODO: Determine the minimum permissions required for security
+ schSCManager = OpenSCManager(NULL, NULL, /* local, default database */
+ SC_MANAGER_ALL_ACCESS);
+ if (!schSCManager) {
+ rv = GetLastError();
+ ap_log_error(APLOG_MARK, APLOG_ERR, rv, NULL,
+ "Failed to open the WinNT service manager");
+ return (rv);
}
- else {
- if (!QueryServiceStatus(schService, &globdat.ssStatus))
- ap_log_error(APLOG_MARK, APLOG_ERR, GetLastError(), NULL,
- "QueryService failed");
- else {
- if (globdat.ssStatus.dwCurrentState == SERVICE_STOPPED && action == stop)
- printf("The %s service is not started.\n", display_name);
- else if (globdat.ssStatus.dwCurrentState == SERVICE_RUNNING && action == start)
- printf("The %s service has already been started.\n", display_name);
- else {
- printf("The %s service is %s.\n", display_name, participle[action]);
-
- if (action == stop || action == restart)
- success = ap_stop_service(schService);
- if (action == start || action == restart)
- success = ap_start_service(schService);
-
- if( success )
- printf("The %s service has %s.\n", display_name, past[action]);
- else
- printf("Failed to %s the %s service.\n", sig, display_name);
- }
- CloseServiceHandle(schService);
- }
+ launch_cmd = ap_psprintf(ptemp, "\"%s\" -k runservice", exe_path);
+
+ /* RPCSS is the Remote Procedure Call (RPC) Locator required for DCOM
+ * communication pipes. I am far from convinced we should add this to
+ * the default service dependencies, but be warned that future apache
+ * modules or ISAPI dll's may depend on it.
+ */
+ schService = CreateService(schSCManager, // SCManager database
+ service_name, // name of service
+ display_name, // name to display
+ SERVICE_ALL_ACCESS, // access required
+ SERVICE_WIN32_OWN_PROCESS, // service type
+ SERVICE_AUTO_START, // start type
+ SERVICE_ERROR_NORMAL, // error control type
+ launch_cmd, // service's binary
+ NULL, // no load svc group
+ NULL, // no tag identifier
+ "Tcpip\0Afd\0", // dependencies
+ NULL, // use SYSTEM account
+ NULL); // no password
+
+ if (!schService)
+ {
+ ap_log_error(APLOG_MARK, APLOG_ERR, GetLastError(), NULL,
+ "Failed to create WinNT Service Profile");
+ CloseServiceHandle(schSCManager);
+ return (rv);
}
- /* SCM removes registry parameters */
+
+ CloseServiceHandle(schService);
CloseServiceHandle(schSCManager);
}
- return success;
+ else /* osver.dwPlatformId != VER_PLATFORM_WIN32_NT */
+ {
+ /* Store the launch command in the registry */
+ launch_cmd = ap_psprintf(ptemp, "\"%s\" -n %s -k runservice",
+ exe_path, service_name);
+ rv = ap_registry_store_value(SERVICECONFIG9X, service_name, launch_cmd);
+ if (rv != APR_SUCCESS) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, rv, NULL,
+ "%s: Failed to add the RunServices registry entry.",
+ display_name);
+ return (rv);
+ }
+
+ ap_snprintf(key_name, sizeof(key_name), SERVICECONFIG, service_name);
+ rv = ap_registry_store_value(key_name, "DisplayName", display_name);
+ if (rv != APR_SUCCESS) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, rv, NULL,
+ "%s: Failed to store DisplayName in the registry.",
+ display_name);
+ return (rv);
+ }
+ }
+
+ /* For both WinNT & Win9x store the service ConfigArgs in the registry...
+ */
+ ap_snprintf(key_name, sizeof(key_name), SERVICEPARAMS, service_name);
+ rv = ap_registry_store_array(ptemp, key_name, "ConfigArgs", argc, argv);
+ if (rv != APR_SUCCESS) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, rv, NULL,
+ "%s: Failed to store the ConfigArgs in the registry.",
+ display_name);
+ return (rv);
+ }
+ printf("The %s service is successfully installed.\n", display_name);
}
-int ap_stop_service(SC_HANDLE schService)
+
+ap_status_t mpm_service_uninstall(void)
{
- if (ControlService(schService, SERVICE_CONTROL_STOP, &globdat.ssStatus)) {
- Sleep(1000);
- while (QueryServiceStatus(schService, &globdat.ssStatus)) {
- if (globdat.ssStatus.dwCurrentState == SERVICE_STOP_PENDING)
- Sleep(1000);
- else
- break;
+ char key_name[MAX_PATH];
+ ap_status_t rv;
+
+ if (osver.dwPlatformId == VER_PLATFORM_WIN32_NT)
+ {
+ SC_HANDLE schService;
+ SC_HANDLE schSCManager;
+
+ printf("Removing the %s service\n", display_name);
+
+ // TODO: Determine the minimum permissions required for security
+ schSCManager = OpenSCManager(NULL, NULL, /* local, default database */
+ SC_MANAGER_ALL_ACCESS);
+ if (!schSCManager) {
+ rv = GetLastError();
+ ap_log_error(APLOG_MARK, APLOG_ERR, rv, NULL,
+ "Failed to open the WinNT service manager.");
+ return (rv);
+ }
+
+ schService = OpenService(schSCManager, service_name, SERVICE_ALL_ACCESS);
+
+ if (!schService) {
+ rv = GetLastError();
+ ap_log_error(APLOG_MARK, APLOG_ERR, rv, NULL,
+ "%s: OpenService failed", display_name);
+ return (rv);
+ }
+ /* assure the service is stopped before continuing
+ *
+ * This may be out of order... we might not be able to be
+ * granted all access if the service is running anyway.
+ *
+ * And do we want to make it *this easy* for them
+ * to uninstall their service unintentionally?
+ */
+ // ap_stop_service(schService);
+
+ if (DeleteService(schService) == 0) {
+ rv = GetLastError();
+ ap_log_error(APLOG_MARK, APLOG_ERR, rv, NULL,
+ "%s: Failed to delete the service.", display_name);
+ return (rv);
}
+
+ CloseServiceHandle(schService);
+ CloseServiceHandle(schSCManager);
}
- if (QueryServiceStatus(schService, &globdat.ssStatus))
- if (globdat.ssStatus.dwCurrentState == SERVICE_STOPPED)
- return TRUE;
- return FALSE;
+ else /* osver.dwPlatformId != VER_PLATFORM_WIN32_NT */
+ {
+ printf("Removing the %s service\n", display_name);
+
+ /* TODO: assure the service is stopped before continuing*/
+
+ if (ap_registry_delete_value(SERVICECONFIG9X, service_name)) {
+ rv = GetLastError();
+ ap_log_error(APLOG_MARK, APLOG_ERR, rv, NULL,
+ "%s: Failed to remove the RunServices registry "
+ "entry.", display_name);
+ return (rv);
+ }
+
+ /* we blast Services/us, not just the Services/us/Parameters branch */
+ ap_snprintf(key_name, sizeof(key_name), SERVICECONFIG, service_name);
+ if (ap_registry_delete_key(key_name)) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, rv, NULL,
+ "%s: Failed to remove the service config from the "
+ "registry.", display_name);
+ return (rv);
+ }
+ }
+ printf("The %s service has been removed successfully.\n", display_name);
+ return APR_SUCCESS;
}
-int ap_start_service(SC_HANDLE schService) {
- if (StartService(schService, 0, NULL)) {
- Sleep(1000);
- while(QueryServiceStatus(schService, &globdat.ssStatus)) {
- if(globdat.ssStatus.dwCurrentState == SERVICE_START_PENDING)
+ap_status_t mpm_service_start(ap_pool_t *ptemp, int argc,
+ char const* const* argv)
+{
+ ap_status_t rv;
+
+ printf("Starting the %s service\n", display_name);
+
+ if (osver.dwPlatformId == VER_PLATFORM_WIN32_NT)
+ {
+ char **start_argv;
+ SC_HANDLE schService;
+ SC_HANDLE schSCManager;
+
+ // TODO: Determine the minimum permissions required for security
+ schSCManager = OpenSCManager(NULL, NULL, /* local, default database */
+ SC_MANAGER_ALL_ACCESS);
+ if (!schSCManager) {
+ rv = GetLastError();
+ ap_log_error(APLOG_MARK, APLOG_ERR, rv, NULL,
+ "Failed to open the WinNT service manager");
+ return (rv);
+ }
+
+ schService = OpenService(schSCManager, service_name,
+ SERVICE_START | SERVICE_QUERY_STATUS);
+ if (!schService) {
+ rv = GetLastError();
+ ap_log_error(APLOG_MARK, APLOG_ERR, rv, NULL,
+ "%s: Failed to open the service.", display_name);
+ CloseServiceHandle(schSCManager);
+ return (rv);
+ }
+
+ argc += 1;
+ start_argv = ap_palloc(ptemp, argc * sizeof(char**));
+ start_argv[0] = service_name;
+ memcpy(start_argv + 1, argv, (argc - 1) * sizeof(char**));
+
+ rv = APR_SUCCESS;
+ if (StartService(schService, argc, start_argv))
+ {
+ globdat.ssStatus.dwCurrentState = SERVICE_START_PENDING;
+ while(globdat.ssStatus.dwCurrentState == SERVICE_START_PENDING) {
Sleep(1000);
- else
- break;
+ if (!QueryServiceStatus(schService, &globdat.ssStatus)) {
+ rv = GetLastError();
+ ap_log_error(APLOG_MARK, APLOG_ERR, rv, NULL,
+ "%s: QueryServiceStatus failed.",
+ display_name);
+ break;
+ }
+ }
+ // TODO: Something informative, plus a time out, would be nice.
+ }
+ else
+ {
+ rv = GetLastError();
+ ap_log_error(APLOG_MARK, APLOG_ERR, rv, NULL,
+ "%s: StartService failed.", display_name);
+ }
+
+ if ((rv == APR_SUCCESS )
+ && (globdat.ssStatus.dwCurrentState != SERVICE_RUNNING))
+ {
+ rv = globdat.ssStatus.dwCurrentState;
+ ap_log_error(APLOG_MARK, APLOG_ERR, rv, NULL,
+ "%s: StartService failed.", display_name);
}
+
+ CloseServiceHandle(schService);
+ CloseServiceHandle(schSCManager);
}
- if (QueryServiceStatus(schService, &globdat.ssStatus))
- if (globdat.ssStatus.dwCurrentState == SERVICE_RUNNING)
- return TRUE;
- return FALSE;
+ else /* osver.dwPlatformId != VER_PLATFORM_WIN32_NT */
+ {
+ STARTUPINFO si; /* Filled in prior to call to CreateProcess */
+ PROCESS_INFORMATION pi; /* filled in on call to CreateProcess */
+ char exe_path[MAX_PATH];
+ char *pCommand;
+ int i;
+
+ /* This may not appear intuitive, but Win9x will not allow a process
+ * to detach from the console without releasing the entire console.
+ * Ergo, we must spawn a new process for the service to get back our
+ * console window.
+ * The config is pre-flighted, so there should be no danger of failure.
+ */
+
+ if (GetModuleFileName(NULL, exe_path, sizeof(exe_path)) == 0)
+ {
+ ap_status_t rv = GetLastError();
+ ap_log_error(APLOG_MARK, APLOG_ERR, rv, NULL,
+ "GetModuleFileName failed");
+ return rv;
+ }
+
+ pCommand = ap_psprintf(ptemp, "\"%s\" -n %s -k runservice",
+ exe_path, service_name);
+ for (i = 0; i < argc; ++i) {
+ pCommand = ap_pstrcat(ptemp, pCommand, " \"", argv[i], "\"", NULL);
+ }
+
+ memset(&si, 0, sizeof(si));
+ memset(&pi, 0, sizeof(pi));
+ si.cb = sizeof(si);
+ si.dwFlags = STARTF_USESHOWWINDOW;
+ si.wShowWindow = SW_HIDE; /* This might be redundant */
+
+ if (!CreateProcess(NULL, pCommand, NULL, NULL, FALSE,
+ DETACHED_PROCESS, /* Creation flags */
+ NULL, NULL, &si, &pi))
+ {
+ rv = GetLastError();
+ ap_log_error(APLOG_MARK, APLOG_CRIT, rv, NULL,
+ "%s: Failed to create the service process.",
+ display_name);
+ /* Just in case... */
+ CloseHandle(pi.hProcess);
+ CloseHandle(pi.hThread);
+ return (rv);
+ }
+
+ // TODO: We can watch the pi.hProcess and wait to be able to open the
+ // shutdown Event of pi.dwProcessId... hang around for a minute
+ // or so on 1 second Sleeps, and declare failure on timeout or
+ // an invalid pi.hProcess handle.
+ // However, that isn't the biggest priority right now :-)
+
+ CloseHandle(pi.hProcess);
+ CloseHandle(pi.hThread);
+ }
+
+ if (rv != APR_SUCCESS) {
+ return (rv);
+ }
+
+ printf("The %s service is running.\n", display_name);
+ return APR_SUCCESS;
}
-#endif /* WIN32 */
+void mpm_signal_service(ap_pool_t *ptemp, char *fname, int signal)
+{
+ long readpid = 0;
+ char pid_str[10]; /* long enough for a long */
+ const char *pid_fname = ap_server_root_relative(ptemp, fname);
+ ap_file_t *pid_file = NULL;
+ ap_finfo_t finfo;
+
+ if (ap_stat(&finfo, pid_fname, ptemp) == APR_SUCCESS)
+ {
+ if (ap_open(&pid_file, pid_fname, APR_READ,
+ APR_OS_DEFAULT, ptemp) == APR_SUCCESS) {
+ ap_fgets(pid_str, sizeof(pid_str), pid_file);
+ readpid = atol(pid_str);
+ ap_close(pid_file);
+ }
+ if (!readpid)
+ {
+ ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, NULL,
+ "%s: could not retrieve pid from file %s",
+ display_name, pid_file);
+ return;
+ }
+ }
+ else
+ {
+ ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, NULL,
+ "%s: could not retrieve pid from file %s",
+ display_name, pid_file);
+ return;
+ }
+ setup_signal_names(ap_psprintf(ptemp,"ap%d", (int) readpid));
+ signal_parent(signal);
+ if (signal)
+ printf ("Signaled Service %s (pid %ld) to restart.",
+ display_name, readpid);
+ else
+ printf ("Signaled Service %s (pid %ld) to stop.",
+ display_name, readpid);
+ return;
+}