mariadb/sql/nt_servc.cc
Vladislav Vaintroub bebde5dba0 merge
2008-11-22 01:10:38 +01:00

585 lines
15 KiB
C++

/**
@file
@brief
Windows NT Service class library.
Copyright Abandoned 1998 Irena Pancirov - Irnet Snc
This file is public domain and comes with NO WARRANTY of any kind
*/
#include <windows.h>
#include <process.h>
#include <stdio.h>
#include "nt_servc.h"
static NTService *pService;
/* ------------------------------------------------------------------------
-------------------------------------------------------------------------- */
NTService::NTService()
{
bOsNT = FALSE;
//service variables
ServiceName = NULL;
hExitEvent = 0;
bPause = FALSE;
bRunning = FALSE;
hThreadHandle = 0;
fpServiceThread = NULL;
//time-out variables
nStartTimeOut = 15000;
nStopTimeOut = 86400000;
nPauseTimeOut = 5000;
nResumeTimeOut = 5000;
//install variables
dwDesiredAccess = SERVICE_ALL_ACCESS;
dwServiceType = SERVICE_WIN32_OWN_PROCESS;
dwStartType = SERVICE_AUTO_START;
dwErrorControl = SERVICE_ERROR_NORMAL;
szLoadOrderGroup = NULL;
lpdwTagID = NULL;
szDependencies = NULL;
my_argc = 0;
my_argv = NULL;
hShutdownEvent = 0;
nError = 0;
dwState = 0;
}
/* ------------------------------------------------------------------------
-------------------------------------------------------------------------- */
NTService::~NTService()
{
if (ServiceName != NULL) delete[] ServiceName;
}
/* ------------------------------------------------------------------------
-------------------------------------------------------------------------- */
BOOL NTService::GetOS()
{
bOsNT = FALSE;
memset(&osVer, 0, sizeof(OSVERSIONINFO));
osVer.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
if (GetVersionEx(&osVer))
{
if (osVer.dwPlatformId == VER_PLATFORM_WIN32_NT)
bOsNT = TRUE;
}
return bOsNT;
}
/**
Registers the main service thread with the service manager.
@param ServiceThread pointer to the main programs entry function
when the service is started
*/
long NTService::Init(LPCSTR szInternName,void *ServiceThread)
{
pService = this;
fpServiceThread = (THREAD_FC)ServiceThread;
ServiceName = new char[lstrlen(szInternName)+1];
lstrcpy(ServiceName,szInternName);
SERVICE_TABLE_ENTRY stb[] =
{
{ (char *)szInternName,(LPSERVICE_MAIN_FUNCTION) ServiceMain} ,
{ NULL, NULL }
};
return StartServiceCtrlDispatcher(stb); //register with the Service Manager
}
/**
Installs the service with Service manager.
nError values:
- 0 success
- 1 Can't open the Service manager
- 2 Failed to create service.
*/
BOOL NTService::Install(int startType, LPCSTR szInternName,
LPCSTR szDisplayName,
LPCSTR szFullPath, LPCSTR szAccountName,
LPCSTR szPassword)
{
BOOL ret_val=FALSE;
SC_HANDLE newService, scm;
if (!SeekStatus(szInternName,1))
return FALSE;
char szFilePath[_MAX_PATH];
GetModuleFileName(NULL, szFilePath, sizeof(szFilePath));
// open a connection to the SCM
if (!(scm = OpenSCManager(0, 0,SC_MANAGER_CREATE_SERVICE)))
printf("Failed to install the service (Couldn't open the SCM)\n");
else // Install the new service
{
if (!(newService=
CreateService(scm,
szInternName,
szDisplayName,
dwDesiredAccess,//default: SERVICE_ALL_ACCESS
dwServiceType, //default: SERVICE_WIN32_OWN_PROCESS
//default: SERVICE_AUTOSTART
(startType == 1 ? SERVICE_AUTO_START :
SERVICE_DEMAND_START),
dwErrorControl, //default: SERVICE_ERROR_NORMAL
szFullPath, //exec full path
szLoadOrderGroup, //default: NULL
lpdwTagID, //default: NULL
szDependencies, //default: NULL
szAccountName, //default: NULL
szPassword))) //default: NULL
printf("Failed to install the service (Couldn't create service)\n");
else
{
printf("Service successfully installed.\n");
CloseServiceHandle(newService);
ret_val=TRUE; // Everything went ok
}
CloseServiceHandle(scm);
}
return ret_val;
}
/**
Removes the service.
nError values:
- 0 success
- 1 Can't open the Service manager
- 2 Failed to locate service
- 3 Failed to delete service.
*/
BOOL NTService::Remove(LPCSTR szInternName)
{
BOOL ret_value=FALSE;
SC_HANDLE service, scm;
if (!SeekStatus(szInternName,0))
return FALSE;
nError=0;
// open a connection to the SCM
if (!(scm = OpenSCManager(0, 0,SC_MANAGER_CREATE_SERVICE)))
{
printf("Failed to remove the service (Couldn't open the SCM)\n");
}
else
{
if ((service = OpenService(scm,szInternName, DELETE)))
{
if (!DeleteService(service))
printf("Failed to remove the service\n");
else
{
printf("Service successfully removed.\n");
ret_value=TRUE; // everything went ok
}
CloseServiceHandle(service);
}
else
printf("Failed to remove the service (Couldn't open the service)\n");
CloseServiceHandle(scm);
}
return ret_value;
}
/**
this function should be called before the app. exits to stop
the service
*/
void NTService::Stop(void)
{
SetStatus(SERVICE_STOP_PENDING,NO_ERROR, 0, 1, 60000);
StopService();
SetStatus(SERVICE_STOPPED, NO_ERROR, 0, 1, 1000);
}
/**
This is the function that is called from the
service manager to start the service.
*/
void NTService::ServiceMain(DWORD argc, LPTSTR *argv)
{
// registration function
if (!(pService->hServiceStatusHandle =
RegisterServiceCtrlHandler(pService->ServiceName,
(LPHANDLER_FUNCTION)
NTService::ServiceCtrlHandler)))
goto error;
// notify SCM of progress
if (!pService->SetStatus(SERVICE_START_PENDING,NO_ERROR, 0, 1, 8000))
goto error;
// create the exit event
if (!(pService->hExitEvent = CreateEvent (0, TRUE, FALSE,0)))
goto error;
if (!pService->SetStatus(SERVICE_START_PENDING,NO_ERROR, 0, 3,
pService->nStartTimeOut))
goto error;
// save start arguments
pService->my_argc=argc;
pService->my_argv=argv;
// start the service
if (!pService->StartService())
goto error;
// wait for exit event
WaitForSingleObject (pService->hExitEvent, INFINITE);
// wait for thread to exit
if (WaitForSingleObject (pService->hThreadHandle, INFINITE) == WAIT_TIMEOUT)
CloseHandle(pService->hThreadHandle);
pService->Exit(0);
return;
error:
pService->Exit(GetLastError());
return;
}
void NTService::SetRunning()
{
if (pService)
pService->SetStatus(SERVICE_RUNNING,NO_ERROR, 0, 0, 0);
}
/* ------------------------------------------------------------------------
StartService() - starts the application thread
-------------------------------------------------------------------------- */
BOOL NTService::StartService()
{
// Start the real service's thread (application)
if (!(hThreadHandle = (HANDLE) _beginthread((THREAD_FC)fpServiceThread,0,
(void *) this)))
return FALSE;
bRunning = TRUE;
return TRUE;
}
/* ------------------------------------------------------------------------
-------------------------------------------------------------------------- */
void NTService::StopService()
{
bRunning=FALSE;
// Set the event for application
if (hShutdownEvent)
SetEvent(hShutdownEvent);
// Set the event for ServiceMain
SetEvent(hExitEvent);
}
/* ------------------------------------------------------------------------
-------------------------------------------------------------------------- */
void NTService::PauseService()
{
bPause = TRUE;
SuspendThread(hThreadHandle);
}
/* ------------------------------------------------------------------------
-------------------------------------------------------------------------- */
void NTService::ResumeService()
{
bPause=FALSE;
ResumeThread(hThreadHandle);
}
/* ------------------------------------------------------------------------
-------------------------------------------------------------------------- */
BOOL NTService::SetStatus (DWORD dwCurrentState,DWORD dwWin32ExitCode,
DWORD dwServiceSpecificExitCode, DWORD dwCheckPoint,
DWORD dwWaitHint)
{
BOOL bRet;
SERVICE_STATUS serviceStatus;
dwState=dwCurrentState;
serviceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
serviceStatus.dwCurrentState = dwCurrentState;
if (dwCurrentState == SERVICE_START_PENDING)
serviceStatus.dwControlsAccepted = 0; //don't accept control events
else
serviceStatus.dwControlsAccepted = (SERVICE_ACCEPT_STOP |
SERVICE_ACCEPT_PAUSE_CONTINUE |
SERVICE_ACCEPT_SHUTDOWN);
// if a specific exit code is defined,set up the win32 exit code properly
if (dwServiceSpecificExitCode == 0)
serviceStatus.dwWin32ExitCode = dwWin32ExitCode;
else
serviceStatus.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;
serviceStatus.dwServiceSpecificExitCode = dwServiceSpecificExitCode;
serviceStatus.dwCheckPoint = dwCheckPoint;
serviceStatus.dwWaitHint = dwWaitHint;
// Pass the status to the Service Manager
if (!(bRet=SetServiceStatus (hServiceStatusHandle, &serviceStatus)))
StopService();
return bRet;
}
/* ------------------------------------------------------------------------
-------------------------------------------------------------------------- */
void NTService::ServiceCtrlHandler(DWORD ctrlCode)
{
DWORD dwState;
if (!pService)
return;
dwState=pService->dwState; // get current state
switch(ctrlCode) {
#ifdef NOT_USED /* do we need this ? */
case SERVICE_CONTROL_PAUSE:
if (pService->bRunning && ! pService->bPause)
{
dwState = SERVICE_PAUSED;
pService->SetStatus(SERVICE_PAUSE_PENDING,NO_ERROR, 0, 1,
pService->nPauseTimeOut);
pService->PauseService();
}
break;
case SERVICE_CONTROL_CONTINUE:
if (pService->bRunning && pService->bPause)
{
dwState = SERVICE_RUNNING;
pService->SetStatus(SERVICE_CONTINUE_PENDING,NO_ERROR, 0, 1,
pService->nResumeTimeOut);
pService->ResumeService();
}
break;
#endif
case SERVICE_CONTROL_SHUTDOWN:
case SERVICE_CONTROL_STOP:
dwState = SERVICE_STOP_PENDING;
pService->SetStatus(SERVICE_STOP_PENDING,NO_ERROR, 0, 1,
pService->nStopTimeOut);
pService->StopService();
break;
default:
pService->SetStatus(dwState, NO_ERROR,0, 0, 0);
break;
}
//pService->SetStatus(dwState, NO_ERROR,0, 0, 0);
}
/* ------------------------------------------------------------------------
-------------------------------------------------------------------------- */
void NTService::Exit(DWORD error)
{
if (hExitEvent)
CloseHandle(hExitEvent);
// Send a message to the scm to tell that we stop
if (hServiceStatusHandle)
SetStatus(SERVICE_STOPPED, error,0, 0, 0);
// If the thread has started kill it ???
// if (hThreadHandle) CloseHandle(hThreadHandle);
}
/* ------------------------------------------------------------------------
-------------------------------------------------------------------------- */
BOOL NTService::SeekStatus(LPCSTR szInternName, int OperationType)
{
BOOL ret_value=FALSE;
SC_HANDLE service, scm;
// open a connection to the SCM
if (!(scm = OpenSCManager(0, 0,SC_MANAGER_CREATE_SERVICE)))
{
DWORD ret_error=GetLastError();
if (ret_error == ERROR_ACCESS_DENIED)
{
printf("Install/Remove of the Service Denied!\n");
if (!is_super_user())
printf("That operation should be made by an user with Administrator privileges!\n");
}
else
printf("There is a problem for to open the Service Control Manager!\n");
}
else
{
if (OperationType == 1)
{
/* an install operation */
if ((service = OpenService(scm,szInternName, SERVICE_ALL_ACCESS )))
{
LPQUERY_SERVICE_CONFIG ConfigBuf;
DWORD dwSize;
ConfigBuf = (LPQUERY_SERVICE_CONFIG) LocalAlloc(LPTR, 4096);
printf("The service already exists!\n");
if (QueryServiceConfig(service,ConfigBuf,4096,&dwSize))
printf("The current server installed: %s\n",
ConfigBuf->lpBinaryPathName);
LocalFree(ConfigBuf);
CloseServiceHandle(service);
}
else
ret_value=TRUE;
}
else
{
/* a remove operation */
if (!(service = OpenService(scm,szInternName, SERVICE_ALL_ACCESS )))
printf("The service doesn't exist!\n");
else
{
SERVICE_STATUS ss;
memset(&ss, 0, sizeof(ss));
if (QueryServiceStatus(service,&ss))
{
DWORD dwState = ss.dwCurrentState;
if (dwState == SERVICE_RUNNING)
printf("Failed to remove the service because the service is running\nStop the service and try again\n");
else if (dwState == SERVICE_STOP_PENDING)
printf("\
Failed to remove the service because the service is in stop pending state!\n\
Wait 30 seconds and try again.\n\
If this condition persist, reboot the machine and try again\n");
else
ret_value= TRUE;
}
CloseServiceHandle(service);
}
}
CloseServiceHandle(scm);
}
return ret_value;
}
/* ------------------------------------------------------------------------
-------------------------------------------------------------------------- */
BOOL NTService::IsService(LPCSTR ServiceName)
{
BOOL ret_value=FALSE;
SC_HANDLE service, scm;
if ((scm= OpenSCManager(0, 0,SC_MANAGER_ENUMERATE_SERVICE)))
{
if ((service = OpenService(scm,ServiceName, SERVICE_QUERY_STATUS)))
{
ret_value=TRUE;
CloseServiceHandle(service);
}
CloseServiceHandle(scm);
}
return ret_value;
}
/* ------------------------------------------------------------------------
-------------------------------------------------------------------------- */
BOOL NTService::got_service_option(char **argv, char *service_option)
{
char *option;
for (option= argv[1]; *option; option++)
if (!strcmp(option, service_option))
return TRUE;
return FALSE;
}
/* ------------------------------------------------------------------------
-------------------------------------------------------------------------- */
BOOL NTService::is_super_user()
{
HANDLE hAccessToken;
UCHAR InfoBuffer[1024];
PTOKEN_GROUPS ptgGroups=(PTOKEN_GROUPS)InfoBuffer;
DWORD dwInfoBufferSize;
PSID psidAdministrators;
SID_IDENTIFIER_AUTHORITY siaNtAuthority = SECURITY_NT_AUTHORITY;
UINT x;
BOOL ret_value=FALSE;
if (!OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, TRUE,&hAccessToken ))
{
if (GetLastError() != ERROR_NO_TOKEN)
return FALSE;
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hAccessToken))
return FALSE;
}
ret_value= GetTokenInformation(hAccessToken,TokenGroups,InfoBuffer,
1024, &dwInfoBufferSize);
CloseHandle(hAccessToken);
if (!ret_value )
return FALSE;
if (!AllocateAndInitializeSid(&siaNtAuthority, 2,
SECURITY_BUILTIN_DOMAIN_RID,
DOMAIN_ALIAS_RID_ADMINS,
0, 0, 0, 0, 0, 0,
&psidAdministrators))
return FALSE;
ret_value = FALSE;
for (x=0;x<ptgGroups->GroupCount;x++)
{
if ( EqualSid(psidAdministrators, ptgGroups->Groups[x].Sid) )
{
ret_value = TRUE;
break;
}
}
FreeSid(psidAdministrators);
return ret_value;
}