From 40b92feb96188523a2f3e893f85cffd6b5b6801b Mon Sep 17 00:00:00 2001 From: "William A. Rowe Jr" Date: Fri, 19 May 2000 05:01:53 +0000 Subject: [PATCH] Fixes to allow Apache to run as a Win95 service... highlights main_win32.h : Moved delarations to a header, by request ap_listen.h : References types declared in http_config.h http_main.h : Add the Win32 flavor entry point declaration apr.hw : Cleanup the redundancy department of redundancy win32/proc.c : Double null termination was required here Everything else should be obvious and isolated to Win32. Build files will be committed seperately. git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@85253 13f79535-47bb-0310-9956-ffa450edef68 --- include/ap_listen.h | 1 + include/http_main.h | 4 + os/win32/main_win32.h | 80 +++++++++++++++ server/mpm/winnt/mpm_winnt.c | 56 +++++++--- server/mpm/winnt/mpm_winnt.h | 6 ++ server/mpm/winnt/registry.c | 32 +----- server/mpm/winnt/service.c | 194 +++++++++++++++++++++++++---------- 7 files changed, 275 insertions(+), 98 deletions(-) create mode 100644 os/win32/main_win32.h diff --git a/include/ap_listen.h b/include/ap_listen.h index 089dcb5372..d611c2953e 100644 --- a/include/ap_listen.h +++ b/include/ap_listen.h @@ -56,6 +56,7 @@ #define AP_LISTEN_H #include "apr_network_io.h" +#include "http_config.h" typedef struct ap_listen_rec ap_listen_rec; struct ap_listen_rec { diff --git a/include/http_main.h b/include/http_main.h index 42ce25e52f..fecd5be56c 100644 --- a/include/http_main.h +++ b/include/http_main.h @@ -71,6 +71,10 @@ extern ap_array_header_t *ap_server_pre_read_config; extern ap_array_header_t *ap_server_post_read_config; extern ap_array_header_t *ap_server_config_defines; +#ifdef WIN32 +API_EXPORT(int) apache_main(int argc, char *argv[]); +#endif + #ifdef __cplusplus } #endif diff --git a/os/win32/main_win32.h b/os/win32/main_win32.h new file mode 100644 index 0000000000..97459d001d --- /dev/null +++ b/os/win32/main_win32.h @@ -0,0 +1,80 @@ +/* ==================================================================== + * The Apache Software License, Version 1.1 + * + * Copyright (c) 2000 The Apache Software Foundation. All rights + * reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, + * if any, must include the following acknowledgment: + * "This product includes software developed by the + * Apache Software Foundation (http://www.apache.org/)." + * Alternately, this acknowledgment may appear in the software itself, + * if and wherever such third-party acknowledgments normally appear. + * + * 4. The names "Apache" and "Apache Software Foundation" must + * not be used to endorse or promote products derived from this + * software without prior written permission. For written + * permission, please contact apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache", + * nor may "Apache" appear in their name, without prior written + * permission of the Apache Software Foundation. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + * Portions of this software are based upon public domain software + * originally written at the National Center for Supercomputing Applications, + * University of Illinois, Urbana-Champaign. + */ + +/* + * Declarations for users of the functions defined in registry.c + */ + +#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 +#include +#include diff --git a/server/mpm/winnt/mpm_winnt.c b/server/mpm/winnt/mpm_winnt.c index 0be5f8b266..baae88fd4f 100644 --- a/server/mpm/winnt/mpm_winnt.c +++ b/server/mpm/winnt/mpm_winnt.c @@ -68,11 +68,9 @@ #include "ap_config.h" #include "ap_listen.h" #include "mpm_default.h" -//#include "service.h" #include "iol_socket.h" #include "winnt.h" - /* * Definitions of WINNT MPM specific config globals */ @@ -105,9 +103,8 @@ int ap_daemons_to_start=0; static event *exit_event; HANDLE maintenance_event; ap_lock_t *start_mutex; -int my_pid; -int parent_pid; -typedef void (CALLBACK *ap_completion_t)(); +DWORD my_pid; +DWORD parent_pid; API_VAR_EXPORT ap_completion_t ap_mpm_init_complete = NULL; static ap_status_t socket_cleanup(void *sock) @@ -299,15 +296,19 @@ static void signal_parent(int type) } CloseHandle(e); } + static int volatile is_graceful = 0; + API_EXPORT(int) ap_graceful_stop_signalled(void) { return is_graceful; } -void ap_start_shutdown(void) + +API_EXPORT(void) ap_start_shutdown(void) { signal_parent(0); } + /* * Initialise the signal names, in the global variables signal_name_prefix, * signal_restart_name and signal_shutdown_name. @@ -354,6 +355,7 @@ static void sock_disable_nagle(int s) * Routines to deal with managing the list of listening sockets. */ static ap_listen_rec *head_listener; + static ap_inline ap_listen_rec *find_ready_listener(fd_set * main_fds) { ap_listen_rec *lr; @@ -371,6 +373,7 @@ static ap_inline ap_listen_rec *find_ready_listener(fd_set * main_fds) } return NULL; } + static int setup_listeners(server_rec *s) { ap_listen_rec *lr; @@ -1390,7 +1393,10 @@ static int create_process(ap_pool_t *p, HANDLE *handles, HANDLE *events, int *pr int rv; char buf[1024]; char *pCommand; + char *pEnvVar; + char *pEnvBlock; int i; + int iEnvBlockLen; STARTUPINFO si; /* Filled in prior to call to CreateProcess */ PROCESS_INFORMATION pi; /* filled in on call to CreateProces */ @@ -1425,6 +1431,32 @@ static int create_process(ap_pool_t *p, HANDLE *handles, HANDLE *events, int *pr pCommand = ap_pstrcat(p, pCommand, " \"", server_conf->process->argv[i], "\"", 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 + * be passed in an environment block, a null terminated block of + * null terminated strings. + */ + i = 0; + iEnvBlockLen = 1; + while (_environ[i]) { + iEnvBlockLen += strlen(_environ[i]) + 1; + i++; + } + + pEnvBlock = (char *)ap_pcalloc(p, iEnvBlockLen + strlen(pEnvVar) + 1); + strcpy(pEnvBlock, pEnvVar); + pEnvVar = strchr(pEnvBlock, '\0') + 1; + + i = 0; + while (_environ[i]) { + strcpy(pEnvVar, _environ[i]); + pEnvVar = strchr(pEnvVar, '\0') + 1; + i++; + } + pEnvVar = '\0'; /* Create a pipe to send socket info to the child */ if (!CreatePipe(&hPipeRead, &hPipeWrite, &sa, 0)) { ap_log_error(APLOG_MARK, APLOG_CRIT, GetLastError(), server_conf, @@ -1432,8 +1464,6 @@ static int create_process(ap_pool_t *p, HANDLE *handles, HANDLE *events, int *pr return -1; } - SetEnvironmentVariable("AP_PARENT_PID",ap_psprintf(p,"%d",parent_pid)); - /* Give the read end of the pipe (hPipeRead) to the child as stdin. The * parent will write the socket data to the child on this pipe. */ @@ -1447,7 +1477,7 @@ static int create_process(ap_pool_t *p, HANDLE *handles, HANDLE *events, int *pr if (!CreateProcess(NULL, pCommand, NULL, NULL, TRUE, /* Inherit handles */ CREATE_SUSPENDED, /* Creation flags */ - NULL, /* Environment block */ + pEnvBlock, /* Environment block */ NULL, &si, &pi)) { ap_log_error(APLOG_MARK, APLOG_CRIT, GetLastError(), server_conf, @@ -1694,12 +1724,12 @@ static void winnt_pre_config(ap_pool_t *pconf, ap_pool_t *plog, ap_pool_t *ptemp pid = getenv("AP_PARENT_PID"); if (pid) { /* This is the child */ - parent_pid = atoi(pid); - my_pid = getpid(); + parent_pid = (DWORD) atol(pid); + my_pid = GetCurrentProcessId(); } else { /* This is the parent */ - parent_pid = my_pid = getpid(); + parent_pid = my_pid = GetCurrentProcessId(); } ap_listen_pre_config(); @@ -1806,7 +1836,7 @@ API_EXPORT(int) ap_mpm_run(ap_pool_t *_pconf, ap_pool_t *plog, server_rec *s ) if (pidfile != NULL && unlink(pidfile) == 0) { ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO,APR_SUCCESS, server_conf, "removed PID file %s (pid=%ld)", - pidfile, (long)getpid()); + pidfile, GetCurrentProcessId()); } ap_destroy_lock(start_mutex); diff --git a/server/mpm/winnt/mpm_winnt.h b/server/mpm/winnt/mpm_winnt.h index ca1b3c67a9..9dcba61076 100644 --- a/server/mpm/winnt/mpm_winnt.h +++ b/server/mpm/winnt/mpm_winnt.h @@ -59,11 +59,17 @@ #ifndef APACHE_MPM_WINNT_H #define APACHE_MPM_WINNT_H +#include "ap_listen.h" + 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_IMPORT ap_completion_t ap_mpm_init_complete; + +API_EXPORT(void) ap_start_shutdown(void); typedef struct CompContext { OVERLAPPED Overlapped; diff --git a/server/mpm/winnt/registry.c b/server/mpm/winnt/registry.c index 02a452c69f..ed41d48564 100644 --- a/server/mpm/winnt/registry.c +++ b/server/mpm/winnt/registry.c @@ -101,6 +101,7 @@ #define SERVICEKEYPRE "System\\CurrentControlSet\\Services\\" #define SERVICEKEYPOST "\\Parameters" +#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 @@ -253,19 +254,6 @@ static int ap_registry_get_key_int(ap_pool_t *p, char *key, char *name, char *pB * dir will contain an empty string), or -1 if there was * an error getting the key. */ -#if 0 -int ap_registry_get_server_root(ap_pool_t *p, char *dir, int size) -{ - int rv; - - rv = ap_registry_get_key_int(p, REGKEY, "ServerRoot", dir, size, NULL); - if (rv < 0) { - dir[0] = '\0'; - } - - return (rv < -1) ? -1 : 0; -} -#else int ap_registry_get_server_root(ap_pool_t *p, char **buf) { int rv; @@ -277,7 +265,7 @@ int ap_registry_get_server_root(ap_pool_t *p, char **buf) return (rv < -1) ? -1 : 0; } -#endif + char *ap_get_service_key(char *display_name) { size_t keylen = strlen(display_name); @@ -290,21 +278,7 @@ char *ap_get_service_key(char *display_name) return(key); } -#if 0 -int ap_registry_get_service_conf(ap_pool_t *p, char *dir, int size, char *display_name) -{ - int rv; - char *key = ap_get_service_key(display_name); - - rv = ap_registry_get_key_int(p, key, "ConfPath", dir, size, NULL); - if (rv < 0) { - dir[0] = '\0'; - } - free(key); - return (rv < -1) ? -1 : 0; -} -#else int ap_registry_get_service_conf(ap_pool_t *p, char **buf, char *service_name) { int rv; @@ -318,7 +292,6 @@ int ap_registry_get_service_conf(ap_pool_t *p, char **buf, char *service_name) free(key); return (rv < -1) ? -1 : 0; } -#endif /********************************************************************** * The rest of this file deals with storing keys or values in the registry @@ -552,4 +525,3 @@ int ap_registry_set_server_root(char *dir) return rv < 0 ? -1 : 0; } - diff --git a/server/mpm/winnt/service.c b/server/mpm/winnt/service.c index e60b8a0169..412f25d046 100644 --- a/server/mpm/winnt/service.c +++ b/server/mpm/winnt/service.c @@ -58,21 +58,20 @@ #ifdef WIN32 -#include "os.h" -#include -#include - #define CORE_PRIVATE + +#include "main_win32.h" #include "httpd.h" #include "http_conf_globals.h" #include "http_log.h" -#include "http_main.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_IMPORT ap_completion_t ap_mpm_init_complete; +API_VAR_IMPORT char *ap_server_argv0; static struct { @@ -85,6 +84,8 @@ static struct SERVICE_STATUS ssStatus; FILE *logFile; char *service_dir; + HANDLE threadService; + HANDLE threadMonitor; } globdat; static void WINAPI service_main_fn(DWORD, LPTSTR *); @@ -93,54 +94,137 @@ static int ReportStatusToSCMgr(int currentState, int exitCode, int waitHint); static int ap_start_service(SC_HANDLE); static int ap_stop_service(SC_HANDLE); -static void CALLBACK report_service95_running() +static LRESULT CALLBACK MonitorWin9xWndProc(HWND hWnd, UINT msg, + WPARAM wParam, LPARAM lParam) { - FreeConsole(); +/* This is the WndProc procedure for our invisible window. + * When the user shuts down the system, this window is sent + * a signal WM_ENDSESSION. We clean up by signaling Apache + * to shut down, and idle until Apache's primary thread quits. + */ + if ((msg == WM_ENDSESSION) && (lParam != ENDSESSION_LOGOFF)) + { + ap_start_shutdown(); + if (wParam) + WaitForSingleObject(globdat.threadService, 30000); + return 0; + } + return (DefWindowProc(hWnd, msg, wParam, lParam)); +} + +static DWORD WINAPI MonitorWin9xEvents(LPVOID initEvent) +{ +/* When running on Windows 9x, the ConsoleCtrlHandler is _NOT_ + * called when the system is shutdown. So create an invisible + * window to watch for the WM_ENDSESSION message, and watch for + * the WM_CLOSE message to shut the window down. + */ + WNDCLASS wc; + HWND hwndMain; + + wc.style = CS_GLOBALCLASS; + wc.lpfnWndProc = MonitorWin9xWndProc; + wc.cbClsExtra = 0; + wc.cbWndExtra = 0; + wc.hInstance = NULL; + wc.hIcon = NULL; + wc.hCursor = NULL; + wc.hbrBackground = NULL; + wc.lpszMenuName = NULL; + wc.lpszClassName = "ApacheWin9xService"; + + if (RegisterClass(&wc)) + { + /* Create an invisible window */ + hwndMain = CreateWindow("ApacheWin9xService", "Apache", + 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. + * Signal the parent we are all set up, and + * watch the message queue while the window lives. + */ + FreeConsole(); + SetEvent((HANDLE) initEvent); + while (GetMessage(&msg, NULL, 0, 0)) + { + if (msg.message == WM_CLOSE) + DestroyWindow(hwndMain); + else { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + } + globdat.threadMonitor = 0; + return(0); + } + } + /* We failed or are soon to die... + * we won't need this much longer + */ + SetEvent((HANDLE) initEvent); + globdat.threadMonitor = 0; + return(0); +} - /* We do this only once, ever */ - ap_mpm_init_complete = NULL; +static void CALLBACK report_service9x_running() +{ } -int service95_main(int (*main_fn)(int, char **), int argc, char **argv ) +int service9x_main(int (*main_fn)(int, char **), int argc, char **argv ) { - HINSTANCE hkernel; 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) - return -1; - - /* Find the RegisterServiceProcess function */ - RegisterServiceProcess = (DWORD (WINAPI *)(DWORD, DWORD)) - GetProcAddress(hkernel, "RegisterServiceProcess"); - if (RegisterServiceProcess == NULL) - return -1; - - /* Register this process as a service */ - if (!RegisterServiceProcess((DWORD)NULL, 1)) - return -1; - - /* Eliminate the console for the remainer of the service session */ - ap_mpm_init_complete = report_service95_running; + 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) + { + PostThreadMessage(threadId, WM_CLOSE, 0, 0); + WaitForSingleObject(globdat.threadMonitor, 30000); + } + /* When the service quits, remove it from the system service table */ - RegisterServiceProcess((DWORD)NULL, 0); + if (RegisterServiceProcess) + RegisterServiceProcess((DWORD)NULL, 0); /* Free the kernel library */ - // Worthless, methinks, since it won't be reclaimed - // FreeLibrary(hkernel); + if (hkernel) + FreeLibrary(hkernel); - /* We have to quit right here to avoid an invalid page fault */ - // But, this is worth experimenting with! return (globdat.exit_status); } -int service_main(int (*main_fn)(int, char **), int argc, char **argv ) +int servicent_main(int (*main_fn)(int, char **), int argc, char **argv ) { SERVICE_TABLE_ENTRY dispatchTable[] = { @@ -166,24 +250,12 @@ int service_main(int (*main_fn)(int, char **), int argc, char **argv ) } } -void service_cd() -{ - /* change to the drive with the executable */ - char buf[300]; - GetModuleFileName(NULL, buf, 300); - buf[2] = 0; - chdir(buf); -} - -static void CALLBACK report_service_started() +static void CALLBACK report_servicent_started() { ReportStatusToSCMgr( SERVICE_RUNNING, // service state NO_ERROR, // exit code 0); // wait hint - - /* This is only reported once, ever! */ - ap_mpm_init_complete = NULL; } void __stdcall service_main_fn(DWORD argc, LPTSTR *argv) @@ -218,9 +290,7 @@ void __stdcall service_main_fn(DWORD argc, LPTSTR *argv) NO_ERROR, // exit code 3000); // wait hint - ap_mpm_init_complete = report_service_started; - - service_cd(); + ap_mpm_init_complete = report_servicent_started; /* Fetch server_conf from the registry * Rebuild argv and argc adding the -d server_root and -f server_conf then @@ -274,7 +344,6 @@ VOID WINAPI service_ctrl(DWORD dwCtrlCode) { int state; - state = globdat.ssStatus.dwCurrentState; switch(dwCtrlCode) { @@ -342,7 +411,7 @@ int ReportStatusToSCMgr(int currentState, int exitCode, int waitHint) return(1); } -void InstallService(char *display_name, char *conf) +void InstallServiceNT(char *display_name, char *conf) { SC_HANDLE schService; SC_HANDLE schSCManager; @@ -415,7 +484,7 @@ void InstallService(char *display_name, char *conf) } -void RemoveService(char *display_name) +void RemoveServiceNT(char *display_name) { SC_HANDLE schService; SC_HANDLE schSCManager; @@ -473,16 +542,27 @@ BOOL isProcessService() { } /* 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(char *display_name) { - SC_HANDLE schSCM, schSVC; +BOOL isValidService(ap_pool_t *p, char *display_name) { char service_name[256]; - int Err; + char *service_conf; /* Remove spaces from display name to create service name */ ap_collapse_spaces(service_name, display_name); + if(ap_registry_get_service_conf(p, &service_conf, service_name)) { + return TRUE; + } + + return FALSE; + +#if 0 + SC_HANDLE schSCM, schSVC; + int Err; + if (!(schSCM = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS))) { ap_log_error(APLOG_MARK, APLOG_ERR, GetLastError(), NULL, "OpenSCManager failed"); @@ -501,8 +581,12 @@ BOOL isValidService(char *display_name) { "OpenService failed"); return FALSE; +#endif } +/* 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; @@ -607,6 +691,6 @@ int ap_start_service(SC_HANDLE schService) { return TRUE; return FALSE; } - + #endif /* WIN32 */ -- 2.50.1