X-Git-Url: https://git.netwichtig.de/gitweb/?a=blobdiff_plain;f=win%2Fwin32service.cpp;h=0fb94f3e43370a655f687aae420f16ae141120f3;hb=dab0a575e232d4fa0f50615424d02fd1e0ba369b;hp=c3dd9871f0fc5602a7ed55e5034eeeec608e5dd6;hpb=9582002171b47c49f5de3cb6ae6f7ece788b233b;p=user%2Fhenk%2Fcode%2Finspircd.git diff --git a/win/win32service.cpp b/win/win32service.cpp index c3dd9871f..0fb94f3e4 100644 --- a/win/win32service.cpp +++ b/win/win32service.cpp @@ -9,336 +9,336 @@ * the file COPYING for details. * * --------------------------------------------------- - */ -#include "inspircd_config.h" -#include "inspircd.h" -#include -#include -#include -#include - -static SERVICE_STATUS_HANDLE serviceStatusHandle; -static HANDLE hThreadEvent; -static HANDLE killServiceEvent; -static int serviceCurrentStatus; - -/** This is used to define ChangeServiceConf2() as we can't link - * directly against this symbol (see below where it is used) - */ -typedef BOOL (CALLBACK* SETSERVDESC)(SC_HANDLE,DWORD,LPVOID); - -/* A commandline parameter handler for service specific commandline parameters */ -typedef void (*CommandlineParameterHandler)(void); - -/* Represents a commandline and its handler */ -struct Commandline -{ - const char* Switch; - CommandlineParameterHandler Handler; -}; - -/* A function pointer for dynamic linking tricks */ -SETSERVDESC ChangeServiceConf; - -/* Kills the service by setting an event which the other thread picks up and exits */ -void KillService() -{ - SetEvent(hThreadEvent); - Sleep(2000); - SetEvent(killServiceEvent); -} - -/** The main part of inspircd runs within this thread function. This allows the service part to run - * seperately on its own and to be able to kill the worker thread when its time to quit. - */ -DWORD WINAPI WorkerThread(LPDWORD param) -{ - char modname[MAX_PATH]; - GetModuleFileName(NULL, modname, sizeof(modname)); - char* argv[] = { modname, "--nofork", "--debug" }; - smain(3, argv); - KillService(); - return 0; -} - -/** Starts the worker thread above */ -void StartServiceThread() -{ - DWORD dwd; - CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)WorkerThread,NULL,0,&dwd); -} - -/** This function updates the status of the service in the SCM - * (service control manager, the services.msc applet) - */ -BOOL UpdateSCMStatus (DWORD dwCurrentState, DWORD dwWin32ExitCode, DWORD dwServiceSpecificExitCode, DWORD dwCheckPoint, DWORD dwWaitHint) -{ - BOOL success; - SERVICE_STATUS serviceStatus; - serviceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS; - serviceStatus.dwCurrentState = dwCurrentState; - - if (dwCurrentState == SERVICE_START_PENDING) - { - serviceStatus.dwControlsAccepted = 0; - } - else - { - serviceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN; - } - - if (dwServiceSpecificExitCode == 0) - { - serviceStatus.dwWin32ExitCode = dwWin32ExitCode; - } - else - { - serviceStatus.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR; - } - serviceStatus.dwServiceSpecificExitCode = dwServiceSpecificExitCode; - serviceStatus.dwCheckPoint = dwCheckPoint; - serviceStatus.dwWaitHint = dwWaitHint; - - success = SetServiceStatus (serviceStatusHandle, &serviceStatus); - if (!success) - { - KillService(); - } - return success; -} - -/** This function is called by us when the service is being shut down or when it can't be started */ -void terminateService (int code, int wincode) -{ - UpdateSCMStatus(SERVICE_STOPPED,wincode?wincode:ERROR_SERVICE_SPECIFIC_ERROR,(wincode)?0:code,0,0); - return; -} - -/** This callback is called by windows when the state of the service has been changed */ -VOID ServiceCtrlHandler (DWORD controlCode) -{ - switch(controlCode) - { - case SERVICE_CONTROL_INTERROGATE: - break; - case SERVICE_CONTROL_SHUTDOWN: - case SERVICE_CONTROL_STOP: - serviceCurrentStatus = SERVICE_STOP_PENDING; - UpdateSCMStatus(SERVICE_STOP_PENDING, NO_ERROR, 0, 1, 5000); - KillService(); - UpdateSCMStatus(SERVICE_STOPPED, NO_ERROR, 0, 0, 0); - return; - default: - break; - } - UpdateSCMStatus(serviceCurrentStatus, NO_ERROR, 0, 0, 0); -} - -/** This callback is called by windows when the service is started */ -VOID ServiceMain(DWORD argc, LPTSTR *argv) -{ - BOOL success; - DWORD type=0, size=0; - - serviceStatusHandle = RegisterServiceCtrlHandler("InspIRCd", (LPHANDLER_FUNCTION)ServiceCtrlHandler); - if (!serviceStatusHandle) - { - terminateService(1, GetLastError()); - return; - } - - success = UpdateSCMStatus(SERVICE_START_PENDING, NO_ERROR, 0, 1, 1000); - if (!success) - { - terminateService(2, GetLastError()); - return; - } - - killServiceEvent = CreateEvent(NULL, true, false, NULL); - hThreadEvent = CreateEvent(NULL, true, false, NULL); - - if (!killServiceEvent || !hThreadEvent) - { - terminateService(99, GetLastError()); - return; - } - - success = UpdateSCMStatus(SERVICE_START_PENDING, NO_ERROR, 0, 2, 1000); - if (!success) - { - terminateService(2, GetLastError()); - return; - } - - StartServiceThread(); - serviceCurrentStatus = SERVICE_RUNNING; - success = UpdateSCMStatus(SERVICE_RUNNING, NO_ERROR, 0, 0, 0); - if (!success) - { - terminateService(6, GetLastError()); - return; - } - WaitForSingleObject (killServiceEvent, INFINITE); -} - -/** Install the windows service. This requires administrator privileges. */ -void InstallService() -{ - SC_HANDLE myService, scm; - SERVICE_DESCRIPTION svDesc; - HINSTANCE advapi32; - - char modname[MAX_PATH]; - GetModuleFileName(NULL, modname, sizeof(modname)); - - scm = OpenSCManager(0,0,SC_MANAGER_CREATE_SERVICE); - if (!scm) - { - printf("Unable to open service control manager: %s\n", dlerror()); - return; - } - - myService = CreateService(scm,"InspIRCd","Inspire IRC Daemon", SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS, - SERVICE_AUTO_START, SERVICE_ERROR_NORMAL, modname, 0, 0, 0, NULL, NULL); - - if (!myService) - { - printf("Unable to create service: %s\n", dlerror()); - CloseServiceHandle(scm); - return; - } - - // *** Set service description *** - // this is supported from 5.0 (win2k) onwards only, so we can't link to the definition of - // this function in advapi32.lib, otherwise the program will not run on windows NT 4. We - // must use LoadLibrary and GetProcAddress to export the function name from advapi32.dll - advapi32 = LoadLibrary("advapi32.dll"); - if (advapi32) - { - ChangeServiceConf = (SETSERVDESC)GetProcAddress(advapi32,"ChangeServiceConfig2A"); - if (ChangeServiceConf) - { - char desc[] = "The Inspire Internet Relay Chat Daemon hosts IRC channels and conversations.\ - If this service is stopped, the IRC server will not run."; - svDesc.lpDescription = desc; - BOOL success = ChangeServiceConf(myService,SERVICE_CONFIG_DESCRIPTION, &svDesc); - if (!success) - { - printf("Unable to set service description: %s\n", dlerror()); - CloseServiceHandle(myService); - CloseServiceHandle(scm); - return; - } - } - FreeLibrary(advapi32); - } - - printf("Service installed.\n"); - CloseServiceHandle(myService); - CloseServiceHandle(scm); -} - -/** Remove the windows service. This requires administrator privileges. */ -void RemoveService() -{ - SC_HANDLE myService, scm; - - scm = OpenSCManager(0,0,SC_MANAGER_CREATE_SERVICE); - if (!scm) - { - printf("Unable to open service control manager: %s\n", dlerror()); - return; - } - - myService = OpenService(scm,"InspIRCd",SERVICE_ALL_ACCESS); - if (!myService) - { - printf("Unable to open service: %s\n", dlerror()); - CloseServiceHandle(scm); - return; - } - - if (!DeleteService(myService)) - { - printf("Unable to delete service: %s\n", dlerror()); - CloseServiceHandle(myService); - CloseServiceHandle(scm); - return; - } - - printf("Service removed.\n"); - CloseServiceHandle(myService); - CloseServiceHandle(scm); -} - -/* In windows, our main() flows through here, before calling the 'real' main, smain() in inspircd.cpp */ -int main(int argc, char** argv) -{ - /* List of parameters and handlers */ - Commandline params[] = { - { "--installservice", InstallService }, - { "--removeservice", RemoveService }, - { NULL } - }; - - /* Check for parameters */ - if (argc > 1) - { - for (int z = 0; params[z].Switch; ++z) - { - if (!_stricmp(argv[1], params[z].Switch)) - { - params[z].Handler(); - return 0; - } - } - } - - /* First, check if the service is installed. - * if it is not, or we're starting as non-administrator, - * just call smain() and start as normal non-service - * process. - */ - SC_HANDLE myService, scm; - scm = OpenSCManager(0,0,SC_MANAGER_CREATE_SERVICE); - if (scm) - { - myService = OpenService(scm,"InspIRCd",SERVICE_ALL_ACCESS); - if (!myService) - { - /* Service not installed or no permission to modify it */ - CloseServiceHandle(scm); - return smain(argc, argv); - } - } - else - { - /* Not enough privileges to open the SCM */ - return smain(argc, argv); - } - - CloseServiceHandle(myService); - CloseServiceHandle(scm); - - /* Check if the process is running interactively. InspIRCd does not run interactively - * as a service so if this is true, we just run the non-service inspircd. - */ - USEROBJECTFLAGS uoflags; - HWINSTA winstation = GetProcessWindowStation(); - if (GetUserObjectInformation(winstation, UOI_FLAGS, &uoflags, sizeof(uoflags), NULL)) - { - if (uoflags.dwFlags == WSF_VISIBLE) - return smain(argc, argv); - } - - /* If we get here, we know the service is installed so we can start it */ - - SERVICE_TABLE_ENTRY serviceTable[] = - { - {"InspIRCd", (LPSERVICE_MAIN_FUNCTION) ServiceMain }, - {NULL, NULL} - }; - - StartServiceCtrlDispatcher(serviceTable); - return 0; -} + */ +#include "inspircd_config.h" +#include "inspircd.h" +#include +#include +#include +#include + +static SERVICE_STATUS_HANDLE serviceStatusHandle; +static HANDLE hThreadEvent; +static HANDLE killServiceEvent; +static int serviceCurrentStatus; + +/** This is used to define ChangeServiceConf2() as we can't link + * directly against this symbol (see below where it is used) + */ +typedef BOOL (CALLBACK* SETSERVDESC)(SC_HANDLE,DWORD,LPVOID); + +/* A commandline parameter handler for service specific commandline parameters */ +typedef void (*CommandlineParameterHandler)(void); + +/* Represents a commandline and its handler */ +struct Commandline +{ + const char* Switch; + CommandlineParameterHandler Handler; +}; + +/* A function pointer for dynamic linking tricks */ +SETSERVDESC ChangeServiceConf; + +/* Kills the service by setting an event which the other thread picks up and exits */ +void KillService() +{ + SetEvent(hThreadEvent); + Sleep(2000); + SetEvent(killServiceEvent); +} + +/** The main part of inspircd runs within this thread function. This allows the service part to run + * seperately on its own and to be able to kill the worker thread when its time to quit. + */ +DWORD WINAPI WorkerThread(LPDWORD param) +{ + char modname[MAX_PATH]; + GetModuleFileName(NULL, modname, sizeof(modname)); + char* argv[] = { modname, "--nofork", "--debug" }; + smain(3, argv); + KillService(); + return 0; +} + +/** Starts the worker thread above */ +void StartServiceThread() +{ + DWORD dwd; + CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)WorkerThread,NULL,0,&dwd); +} + +/** This function updates the status of the service in the SCM + * (service control manager, the services.msc applet) + */ +BOOL UpdateSCMStatus (DWORD dwCurrentState, DWORD dwWin32ExitCode, DWORD dwServiceSpecificExitCode, DWORD dwCheckPoint, DWORD dwWaitHint) +{ + BOOL success; + SERVICE_STATUS serviceStatus; + serviceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS; + serviceStatus.dwCurrentState = dwCurrentState; + + if (dwCurrentState == SERVICE_START_PENDING) + { + serviceStatus.dwControlsAccepted = 0; + } + else + { + serviceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN; + } + + if (dwServiceSpecificExitCode == 0) + { + serviceStatus.dwWin32ExitCode = dwWin32ExitCode; + } + else + { + serviceStatus.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR; + } + serviceStatus.dwServiceSpecificExitCode = dwServiceSpecificExitCode; + serviceStatus.dwCheckPoint = dwCheckPoint; + serviceStatus.dwWaitHint = dwWaitHint; + + success = SetServiceStatus (serviceStatusHandle, &serviceStatus); + if (!success) + { + KillService(); + } + return success; +} + +/** This function is called by us when the service is being shut down or when it can't be started */ +void terminateService (int code, int wincode) +{ + UpdateSCMStatus(SERVICE_STOPPED,wincode?wincode:ERROR_SERVICE_SPECIFIC_ERROR,(wincode)?0:code,0,0); + return; +} + +/** This callback is called by windows when the state of the service has been changed */ +VOID ServiceCtrlHandler (DWORD controlCode) +{ + switch(controlCode) + { + case SERVICE_CONTROL_INTERROGATE: + break; + case SERVICE_CONTROL_SHUTDOWN: + case SERVICE_CONTROL_STOP: + serviceCurrentStatus = SERVICE_STOP_PENDING; + UpdateSCMStatus(SERVICE_STOP_PENDING, NO_ERROR, 0, 1, 5000); + KillService(); + UpdateSCMStatus(SERVICE_STOPPED, NO_ERROR, 0, 0, 0); + return; + default: + break; + } + UpdateSCMStatus(serviceCurrentStatus, NO_ERROR, 0, 0, 0); +} + +/** This callback is called by windows when the service is started */ +VOID ServiceMain(DWORD argc, LPTSTR *argv) +{ + BOOL success; + DWORD type=0, size=0; + + serviceStatusHandle = RegisterServiceCtrlHandler("InspIRCd", (LPHANDLER_FUNCTION)ServiceCtrlHandler); + if (!serviceStatusHandle) + { + terminateService(1, GetLastError()); + return; + } + + success = UpdateSCMStatus(SERVICE_START_PENDING, NO_ERROR, 0, 1, 1000); + if (!success) + { + terminateService(2, GetLastError()); + return; + } + + killServiceEvent = CreateEvent(NULL, true, false, NULL); + hThreadEvent = CreateEvent(NULL, true, false, NULL); + + if (!killServiceEvent || !hThreadEvent) + { + terminateService(99, GetLastError()); + return; + } + + success = UpdateSCMStatus(SERVICE_START_PENDING, NO_ERROR, 0, 2, 1000); + if (!success) + { + terminateService(2, GetLastError()); + return; + } + + StartServiceThread(); + serviceCurrentStatus = SERVICE_RUNNING; + success = UpdateSCMStatus(SERVICE_RUNNING, NO_ERROR, 0, 0, 0); + if (!success) + { + terminateService(6, GetLastError()); + return; + } + WaitForSingleObject (killServiceEvent, INFINITE); +} + +/** Install the windows service. This requires administrator privileges. */ +void InstallService() +{ + SC_HANDLE myService, scm; + SERVICE_DESCRIPTION svDesc; + HINSTANCE advapi32; + + char modname[MAX_PATH]; + GetModuleFileName(NULL, modname, sizeof(modname)); + + scm = OpenSCManager(0,0,SC_MANAGER_CREATE_SERVICE); + if (!scm) + { + printf("Unable to open service control manager: %s\n", dlerror()); + return; + } + + myService = CreateService(scm,"InspIRCd","Inspire IRC Daemon", SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS, + SERVICE_AUTO_START, SERVICE_ERROR_NORMAL, modname, 0, 0, 0, NULL, NULL); + + if (!myService) + { + printf("Unable to create service: %s\n", dlerror()); + CloseServiceHandle(scm); + return; + } + + // *** Set service description *** + // this is supported from 5.0 (win2k) onwards only, so we can't link to the definition of + // this function in advapi32.lib, otherwise the program will not run on windows NT 4. We + // must use LoadLibrary and GetProcAddress to export the function name from advapi32.dll + advapi32 = LoadLibrary("advapi32.dll"); + if (advapi32) + { + ChangeServiceConf = (SETSERVDESC)GetProcAddress(advapi32,"ChangeServiceConfig2A"); + if (ChangeServiceConf) + { + char desc[] = "The Inspire Internet Relay Chat Daemon hosts IRC channels and conversations.\ + If this service is stopped, the IRC server will not run."; + svDesc.lpDescription = desc; + BOOL success = ChangeServiceConf(myService,SERVICE_CONFIG_DESCRIPTION, &svDesc); + if (!success) + { + printf("Unable to set service description: %s\n", dlerror()); + CloseServiceHandle(myService); + CloseServiceHandle(scm); + return; + } + } + FreeLibrary(advapi32); + } + + printf("Service installed.\n"); + CloseServiceHandle(myService); + CloseServiceHandle(scm); +} + +/** Remove the windows service. This requires administrator privileges. */ +void RemoveService() +{ + SC_HANDLE myService, scm; + + scm = OpenSCManager(0,0,SC_MANAGER_CREATE_SERVICE); + if (!scm) + { + printf("Unable to open service control manager: %s\n", dlerror()); + return; + } + + myService = OpenService(scm,"InspIRCd",SERVICE_ALL_ACCESS); + if (!myService) + { + printf("Unable to open service: %s\n", dlerror()); + CloseServiceHandle(scm); + return; + } + + if (!DeleteService(myService)) + { + printf("Unable to delete service: %s\n", dlerror()); + CloseServiceHandle(myService); + CloseServiceHandle(scm); + return; + } + + printf("Service removed.\n"); + CloseServiceHandle(myService); + CloseServiceHandle(scm); +} + +/* In windows, our main() flows through here, before calling the 'real' main, smain() in inspircd.cpp */ +int main(int argc, char** argv) +{ + /* List of parameters and handlers */ + Commandline params[] = { + { "--installservice", InstallService }, + { "--removeservice", RemoveService }, + { NULL } + }; + + /* Check for parameters */ + if (argc > 1) + { + for (int z = 0; params[z].Switch; ++z) + { + if (!_stricmp(argv[1], params[z].Switch)) + { + params[z].Handler(); + return 0; + } + } + } + + /* First, check if the service is installed. + * if it is not, or we're starting as non-administrator, + * just call smain() and start as normal non-service + * process. + */ + SC_HANDLE myService, scm; + scm = OpenSCManager(0,0,SC_MANAGER_CREATE_SERVICE); + if (scm) + { + myService = OpenService(scm,"InspIRCd",SERVICE_ALL_ACCESS); + if (!myService) + { + /* Service not installed or no permission to modify it */ + CloseServiceHandle(scm); + return smain(argc, argv); + } + } + else + { + /* Not enough privileges to open the SCM */ + return smain(argc, argv); + } + + CloseServiceHandle(myService); + CloseServiceHandle(scm); + + /* Check if the process is running interactively. InspIRCd does not run interactively + * as a service so if this is true, we just run the non-service inspircd. + */ + USEROBJECTFLAGS uoflags; + HWINSTA winstation = GetProcessWindowStation(); + if (GetUserObjectInformation(winstation, UOI_FLAGS, &uoflags, sizeof(uoflags), NULL)) + { + if (uoflags.dwFlags == WSF_VISIBLE) + return smain(argc, argv); + } + + /* If we get here, we know the service is installed so we can start it */ + + SERVICE_TABLE_ENTRY serviceTable[] = + { + {"InspIRCd", (LPSERVICE_MAIN_FUNCTION) ServiceMain }, + {NULL, NULL} + }; + + StartServiceCtrlDispatcher(serviceTable); + return 0; +}