mirror of
https://github.com/FirebirdSQL/firebird.git
synced 2025-01-27 18:03:04 +01:00
290 lines
7.2 KiB
C++
290 lines
7.2 KiB
C++
/*
|
|
* PROGRAM: Guardian CNTL function.
|
|
* MODULE: cntl_guard.cpp
|
|
* DESCRIPTION: Windows NT service control panel interface
|
|
*
|
|
* The contents of this file are subject to the Interbase Public
|
|
* License Version 1.0 (the "License"); you may not use this file
|
|
* except in compliance with the License. You may obtain a copy
|
|
* of the License at http://www.Inprise.com/IPL.html
|
|
*
|
|
* Software distributed under the License is distributed on an
|
|
* "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express
|
|
* or implied. See the License for the specific language governing
|
|
* rights and limitations under the License.
|
|
*
|
|
* The Original Code was created by Inprise Corporation
|
|
* and its predecessors. Portions created by Inprise Corporation are
|
|
* Copyright (C) Inprise Corporation.
|
|
*
|
|
* All Rights Reserved.
|
|
* Contributor(s): ______________________________________.
|
|
*/
|
|
|
|
#include "firebird.h"
|
|
#include <stdio.h>
|
|
#include "../jrd/common.h"
|
|
#include "../iscguard/cntlg_proto.h"
|
|
#include "../remote/remote.h"
|
|
#include "../utilities/install/install_nt.h"
|
|
#include "../jrd/thd.h"
|
|
#include "../jrd/isc_proto.h"
|
|
#include "../jrd/gds_proto.h"
|
|
#include "../jrd/sch_proto.h"
|
|
#include "../common/classes/fb_string.h"
|
|
|
|
#ifdef WIN_NT
|
|
#include <windows.h>
|
|
#endif
|
|
|
|
struct thread {
|
|
thread* thread_next;
|
|
HANDLE thread_handle;
|
|
};
|
|
|
|
static void WINAPI control_thread(DWORD);
|
|
static void parse_switch(const TEXT*, int*);
|
|
static USHORT report_status(DWORD, DWORD, DWORD, DWORD);
|
|
|
|
static DWORD current_state;
|
|
static ThreadEntryPoint* main_handler;
|
|
static SERVICE_STATUS_HANDLE service_handle;
|
|
static Firebird::string* service_name = NULL;
|
|
static Firebird::string* remote_name = NULL;
|
|
static HANDLE stop_event_handle;
|
|
#ifdef NOT_USED_OR_REPLACED
|
|
static MUTX_T thread_mutex[1];
|
|
static thread* threads;
|
|
#endif
|
|
|
|
|
|
void CNTL_init(ThreadEntryPoint* handler, const TEXT* name)
|
|
{
|
|
/**************************************
|
|
*
|
|
* C N T L _ i n i t
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
*
|
|
**************************************/
|
|
|
|
main_handler = handler;
|
|
MemoryPool& pool = *getDefaultMemoryPool();
|
|
service_name = FB_NEW(pool) Firebird::string(pool);
|
|
service_name->printf(ISCGUARD_SERVICE, name);
|
|
remote_name = FB_NEW(pool) Firebird::string(pool);
|
|
remote_name->printf(REMOTE_SERVICE, name);
|
|
}
|
|
|
|
|
|
void WINAPI CNTL_main_thread( DWORD argc, char* argv[])
|
|
{
|
|
/**************************************
|
|
*
|
|
* C N T L _ m a i n _ t h r e a d
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
*
|
|
**************************************/
|
|
service_handle =
|
|
RegisterServiceCtrlHandler(service_name->c_str(), control_thread);
|
|
if (!service_handle)
|
|
return;
|
|
|
|
// THD_mutex_init (thread_mutex);
|
|
|
|
/* start everything, and wait here for ever, or at
|
|
* least until we get the stop event indicating that
|
|
* the service is stoping. */
|
|
|
|
bool failure = true;
|
|
DWORD temp = 0;
|
|
if (report_status(SERVICE_START_PENDING, NO_ERROR, 1, 3000) &&
|
|
(stop_event_handle = CreateEvent(NULL, TRUE, FALSE, NULL)) != NULL &&
|
|
report_status(SERVICE_START_PENDING, NO_ERROR, 2, 3000) &&
|
|
!gds__thread_start(main_handler, NULL, 0, 0, 0)
|
|
&& report_status(SERVICE_RUNNING, NO_ERROR, 0, 0))
|
|
{
|
|
failure = false;
|
|
temp = WaitForSingleObject(stop_event_handle, INFINITE);
|
|
}
|
|
|
|
DWORD last_error = 0;
|
|
if (failure || temp == WAIT_FAILED)
|
|
last_error = GetLastError();
|
|
|
|
if (stop_event_handle)
|
|
CloseHandle(stop_event_handle);
|
|
|
|
/* ONce we are stopped, we will tell the server to
|
|
* do the same. We could not do this in the control_thread
|
|
* since the Services Control Manager is single threaded,
|
|
* and thus can only process one request at the time. */
|
|
SERVICE_STATUS status_info;
|
|
SC_HANDLE hScManager = 0, hService = 0;
|
|
hScManager =
|
|
OpenSCManager(NULL, NULL, GENERIC_READ);
|
|
hService = OpenService(hScManager, remote_name->c_str(),
|
|
GENERIC_READ | GENERIC_EXECUTE);
|
|
ControlService(hService, SERVICE_CONTROL_STOP, &status_info);
|
|
CloseServiceHandle(hScManager);
|
|
CloseServiceHandle(hService);
|
|
|
|
report_status(SERVICE_STOPPED, last_error, 0, 0);
|
|
|
|
// THD_mutex_destroy (thread_mutex);
|
|
}
|
|
|
|
void CNTL_shutdown_service(const TEXT* message)
|
|
{
|
|
/**************************************
|
|
*
|
|
* C N T L _ s h u t d o w n _ s e r v i c e
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
*
|
|
**************************************/
|
|
const char* strings[2];
|
|
char buffer[BUFFER_SMALL];
|
|
|
|
sprintf(buffer, "%s error: %lu", service_name->c_str(), GetLastError());
|
|
|
|
HANDLE event_source = RegisterEventSource(NULL, service_name->c_str());
|
|
if (event_source) {
|
|
strings[0] = buffer;
|
|
strings[1] = message;
|
|
ReportEvent(event_source,
|
|
EVENTLOG_ERROR_TYPE,
|
|
0,
|
|
0,
|
|
NULL, 2, 0, strings, NULL);
|
|
DeregisterEventSource(event_source);
|
|
}
|
|
|
|
if (stop_event_handle)
|
|
SetEvent(stop_event_handle);
|
|
}
|
|
|
|
|
|
void CNTL_stop_service(const TEXT* service) // unused param
|
|
{
|
|
/**************************************
|
|
*
|
|
* C N T L _ s t o p _ s e r v i c e
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* This function is called to stop the service.
|
|
*
|
|
*
|
|
**************************************/
|
|
|
|
SC_HANDLE servicemgr_handle = OpenSCManager(NULL, NULL, GENERIC_READ);
|
|
if (servicemgr_handle == NULL) {
|
|
// return error
|
|
int error = GetLastError();
|
|
gds__log("SC manager error %d", error);
|
|
return;
|
|
}
|
|
|
|
SC_HANDLE service_handleL =
|
|
OpenService(servicemgr_handle, service_name->c_str(),
|
|
GENERIC_READ | GENERIC_EXECUTE);
|
|
|
|
if (service_handleL == NULL) {
|
|
// return error
|
|
int error = GetLastError();
|
|
gds__log("open services error %d", error);
|
|
return;
|
|
}
|
|
else {
|
|
SERVICE_STATUS status_info;
|
|
if (!ControlService
|
|
(service_handleL, SERVICE_CONTROL_STOP, &status_info))
|
|
{
|
|
// return error
|
|
const int error = GetLastError();
|
|
gds__log("Control services error %d", error);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
static void WINAPI control_thread( DWORD action)
|
|
{
|
|
/**************************************
|
|
*
|
|
* c o n t r o l _ t h r e a d
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Process a service control request.
|
|
*
|
|
**************************************/
|
|
const DWORD state = SERVICE_RUNNING;
|
|
|
|
switch (action) {
|
|
case SERVICE_CONTROL_STOP:
|
|
report_status(SERVICE_STOP_PENDING, NO_ERROR, 1, 3000);
|
|
SetEvent(stop_event_handle);
|
|
return;
|
|
|
|
case SERVICE_CONTROL_INTERROGATE:
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
report_status(state, NO_ERROR, 0, 0);
|
|
}
|
|
|
|
|
|
static USHORT report_status(
|
|
DWORD state,
|
|
DWORD exit_code, DWORD checkpoint, DWORD hint)
|
|
{
|
|
/**************************************
|
|
*
|
|
* r e p o r t _ s t a t u s
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Report our status to the control manager.
|
|
*
|
|
**************************************/
|
|
SERVICE_STATUS status;
|
|
|
|
status.dwServiceType =
|
|
(SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS);
|
|
status.dwServiceSpecificExitCode = 0;
|
|
|
|
if (state == SERVICE_START_PENDING)
|
|
status.dwControlsAccepted = 0;
|
|
else
|
|
status.dwControlsAccepted = SERVICE_ACCEPT_STOP;
|
|
|
|
status.dwCurrentState = current_state = state;
|
|
status.dwWin32ExitCode = exit_code;
|
|
status.dwCheckPoint = checkpoint;
|
|
status.dwWaitHint = hint;
|
|
|
|
const USHORT ret = SetServiceStatus(service_handle, &status);
|
|
if (!ret)
|
|
CNTL_shutdown_service("SetServiceStatus");
|
|
|
|
return ret;
|
|
}
|
|
|