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
This commit is contained in:
William A. Rowe Jr
2000-05-19 05:01:53 +00:00
parent 42ec41c2a7
commit 40b92feb96
7 changed files with 276 additions and 99 deletions

View File

@ -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 {

View File

@ -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

80
os/win32/main_win32.h Normal file
View File

@ -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
* <http://www.apache.org/>.
*
* 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 <windows.h>
#include <winsock2.h>
#include <mswsock.h>

View File

@ -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);

View File

@ -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;

View File

@ -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;
}

View File

@ -58,21 +58,20 @@
#ifdef WIN32
#include "os.h"
#include <stdlib.h>
#include <direct.h>
#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();
/* We do this only once, ever */
ap_mpm_init_complete = NULL;
/* 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));
}
int service95_main(int (*main_fn)(int, char **), int argc, char **argv )
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);
}
static void CALLBACK report_service9x_running()
{
}
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 */