*/
#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"
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;
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;
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)
{
* or thrown out entirely...
*/
-
typedef void semaphore;
typedef void event;
* 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
* 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),
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;
}
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)) {
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) {
*/
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 */
static int create_process(ap_pool_t *p, HANDLE *handles, HANDLE *events, int *processes)
{
-
int rv;
char buf[1024];
char *pCommand;
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;
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;
}
/* 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
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);
/* 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,
¤t_live_processes) < 0) {
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;
* 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,
}
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]);
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");
/* 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();
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
*/
}
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'.
*/
}
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
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 )
CloseHandle(restart_event);
CloseHandle(shutdown_event);
- //service_set_status(SERVICE_STOPPED);
-
return 1;
}
} /* Parent process */
{ 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 */
#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;
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 */
* 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
* 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,
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);
}
* 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
*/
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
HWND hwndMain;
wc.style = CS_GLOBALCLASS;
- wc.lpfnWndProc = MonitorWin9xWndProc;
+ wc.lpfnWndProc = monitor_service_9x_proc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = NULL;
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))
{
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)
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;
+}