X-Git-Url: https://git.netwichtig.de/gitweb/?a=blobdiff_plain;f=win%2Fwin32service.cpp;h=45aba726824c0d3af6146934f159887c97a500fa;hb=2ba32afa9a9aca7c82966b66bda16c3c3dbfbba5;hp=0eb9d0c1ac2cef919ea4f5b9a13b4090ac476215;hpb=c1a2a8abca5430a41d145de5c7a7521fe3f3fae5;p=user%2Fhenk%2Fcode%2Finspircd.git diff --git a/win/win32service.cpp b/win/win32service.cpp index 0eb9d0c1a..45aba7268 100644 --- a/win/win32service.cpp +++ b/win/win32service.cpp @@ -1,311 +1,290 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ +/* + * InspIRCd -- Internet Relay Chat Daemon * - * InspIRCd: (C) 2002-2008 InspIRCd Development Team - * See: http://www.inspircd.org/wiki/index.php/Credits + * Copyright (C) 2013 Sadie Powell + * Copyright (C) 2012-2013 ChrisTX + * Copyright (C) 2012-2013 Attila Molnar + * Copyright (C) 2012 Robby + * Copyright (C) 2008 Craig Edwards * - * This program is free but copyrighted software; see - * the file COPYING for details. + * This file is part of InspIRCd. InspIRCd is free software: you can + * redistribute it and/or modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation, version 2. * - * --------------------------------------------------- - */ - -#include -#include -#include -#include - -extern int smain(int argc, char** argv); - -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); - -SETSERVDESC ChangeServiceConf; // A function pointer for dynamic linking tricks - - -void KillService() -{ - /* FIXME: This should set a flag in the mainloop for shutting down */ - SetEvent(hThreadEvent); - Sleep(2000); - SetEvent(killServiceEvent); -} - -DWORD WINAPI WorkerThread(LPDWORD param) -{ - // *** REAL MAIN HERE *** - char modname[MAX_PATH]; - GetModuleFileName(NULL, modname, sizeof(modname)); - char* argv[] = { modname, "--nofork", "--debug" }; - smain(3, argv); - KillService(); - return 0; -} - -void StartServiceThread() -{ - DWORD dwd; - CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)WorkerThread,NULL,0,&dwd); -} - - -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; -} - - -void terminateService (int code, int wincode) -{ - UpdateSCMStatus(SERVICE_STOPPED,wincode?wincode:ERROR_SERVICE_SPECIFIC_ERROR,(wincode)?0:code,0,0); - return; -} - - -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); -} - - -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); -} - -void InstallService(void) -{ - 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: %d\n", GetLastError()); - return; - } - - myService = CreateService(scm,"InspIRCd","Inspire IRC Daemon", SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS, - SERVICE_DEMAND_START, SERVICE_ERROR_NORMAL, modname, 0, 0, 0, NULL, NULL); - - if (!myService) - { - printf("Unable to create service: %d\n", GetLastError()); - 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: %d\n", GetLastError()); - CloseServiceHandle(myService); - CloseServiceHandle(scm); - return; - } - } - FreeLibrary(advapi32); - } - - printf("Service installed.\n"); - CloseServiceHandle(myService); - CloseServiceHandle(scm); -} - -void RemoveService(void) -{ - SC_HANDLE myService, scm; - - scm = OpenSCManager(0,0,SC_MANAGER_CREATE_SERVICE); - if (!scm) - { - printf("Unable to open service control manager: %d\n", GetLastError()); - return; - } - - myService = OpenService(scm,"InspIRCd",SERVICE_ALL_ACCESS); - if (!myService) - { - printf("Unable to open service: %d\n", GetLastError()); - CloseServiceHandle(scm); - return; - } - - if (!DeleteService(myService)) - { - printf("Unable to delete service: %d\n", GetLastError()); - 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) -{ - /* Check for parameters */ - if (argc > 1) - { - if (!_stricmp(argv[1], "--installservice")) - { - InstallService(); - return 0; - } - else if (!_stricmp(argv[1], "--removeservice")) - { - RemoveService(); - 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); - smain(argc, argv); - } - } - else - { - /* Not enough privileges to open the SCM */ - smain(argc, argv); - } - - CloseServiceHandle(myService); - CloseServiceHandle(scm); - - /* 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; -} + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + +#include "inspircd.h" +#include "exitcodes.h" +#include +#include +#include +#include +#include + +static SERVICE_STATUS_HANDLE g_ServiceStatusHandle; +static SERVICE_STATUS g_ServiceStatus; +static bool g_bRunningAsService; + +struct Service_Data { + DWORD argc; + LPSTR *argv; +}; + +static Service_Data g_ServiceData; + +/** The main part of inspircd runs within this thread function. This allows the service part to run + * separately on its own and to be able to kill the worker thread when its time to quit. + */ +DWORD WINAPI WorkerThread(LPVOID param) +{ + smain(g_ServiceData.argc, g_ServiceData.argv); + return 0; +} + +/* This is called when all startup is done */ +void SetServiceRunning() +{ + if (!g_bRunningAsService) + return; + + g_ServiceStatus.dwCurrentState = SERVICE_RUNNING; + g_ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN; + + if( !SetServiceStatus( g_ServiceStatusHandle, &g_ServiceStatus ) ) + throw CWin32Exception(); +} + +/* In windows we hook this to InspIRCd::Exit() */ +void SetServiceStopped(DWORD dwStatus) +{ + if (!g_bRunningAsService) + return; + + g_ServiceStatus.dwCurrentState = SERVICE_STOPPED; + if(dwStatus != EXIT_STATUS_NOERROR) + { + g_ServiceStatus.dwServiceSpecificExitCode = dwStatus; + g_ServiceStatus.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR; + } + else + { + g_ServiceStatus.dwWin32ExitCode = ERROR_SUCCESS; + } + SetServiceStatus( g_ServiceStatusHandle, &g_ServiceStatus ); +} + +/** This callback is called by windows when the state of the service has been changed */ +VOID ServiceCtrlHandler(DWORD controlCode) +{ + switch(controlCode) + { + case SERVICE_CONTROL_SHUTDOWN: + case SERVICE_CONTROL_STOP: + g_ServiceStatus.dwCurrentState = SERVICE_STOPPED; + SetServiceStatus( g_ServiceStatusHandle, &g_ServiceStatus ); + break; + } +} + +/** This callback is called by windows when the service is started */ +VOID ServiceMain(DWORD argc, LPCSTR *argv) +{ + g_ServiceStatusHandle = RegisterServiceCtrlHandler(TEXT("InspIRCd"), (LPHANDLER_FUNCTION)ServiceCtrlHandler); + if( !g_ServiceStatusHandle ) + return; + + g_ServiceStatus.dwCheckPoint = 1; + g_ServiceStatus.dwControlsAccepted = 0; + g_ServiceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS; + g_ServiceStatus.dwWaitHint = 5000; + g_ServiceStatus.dwWin32ExitCode = NO_ERROR; + g_ServiceStatus.dwCurrentState = SERVICE_START_PENDING; + + if( !SetServiceStatus( g_ServiceStatusHandle, &g_ServiceStatus ) ) + return; + + char szModuleName[MAX_PATH]; + if(GetModuleFileNameA(NULL, szModuleName, MAX_PATH)) + { + if(!argc) + argc = 1; + + g_ServiceData.argc = argc; + + // Note: since this memory is going to stay allocated for the rest of the execution, + // it doesn't make sense to free it, as it's going to be "freed" on process termination + try { + g_ServiceData.argv = new char*[argc]; + + uint32_t allocsize = strnlen_s(szModuleName, MAX_PATH) + 1; + g_ServiceData.argv[0] = new char[allocsize]; + strcpy_s(g_ServiceData.argv[0], allocsize, szModuleName); + + for(uint32_t i = 1; i < argc; i++) + { + allocsize = strnlen_s(argv[i], MAX_PATH) + 1; + g_ServiceData.argv[i] = new char[allocsize]; + strcpy_s(g_ServiceData.argv[i], allocsize, argv[i]); + } + + *(strrchr(szModuleName, '\\') + 1) = NULL; + SetCurrentDirectoryA(szModuleName); + + HANDLE hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)WorkerThread, NULL, 0, NULL); + if (hThread != NULL) + { + WaitForSingleObject(hThread, INFINITE); + CloseHandle(hThread); + } + } + catch(...) + { + g_ServiceStatus.dwCurrentState = SERVICE_STOPPED; + g_ServiceStatus.dwWin32ExitCode = ERROR_OUTOFMEMORY; + SetServiceStatus( g_ServiceStatusHandle, &g_ServiceStatus ); + } + } + if(g_ServiceStatus.dwCurrentState == SERVICE_STOPPED) + return; + + g_ServiceStatus.dwCurrentState = SERVICE_STOPPED; + g_ServiceStatus.dwWin32ExitCode = GetLastError(); + SetServiceStatus( g_ServiceStatusHandle, &g_ServiceStatus ); +} + +/** Install the windows service. This requires administrator privileges. */ +void InstallService() +{ + SC_HANDLE InspServiceHandle = 0, SCMHandle = 0; + + try { + TCHAR tszBinaryPath[MAX_PATH]; + if(!GetModuleFileName(NULL, tszBinaryPath, _countof(tszBinaryPath))) + { + throw CWin32Exception(); + } + + SCMHandle = OpenSCManager(0, 0, SC_MANAGER_ALL_ACCESS); + if (!SCMHandle) + { + throw CWin32Exception(); + } + + InspServiceHandle = CreateService(SCMHandle, TEXT("InspIRCd"),TEXT("InspIRCd Daemon"), SERVICE_CHANGE_CONFIG, SERVICE_WIN32_OWN_PROCESS, + SERVICE_AUTO_START, SERVICE_ERROR_NORMAL, tszBinaryPath, 0, 0, 0, TEXT("NT AUTHORITY\\NetworkService"), NULL); + + if (!InspServiceHandle) + { + throw CWin32Exception(); + } + + TCHAR tszDescription[] = TEXT("The InspIRCd service hosts IRC channels and conversations. If this service is stopped, the IRC server will be unavailable."); + SERVICE_DESCRIPTION svDescription = { tszDescription }; + if(!ChangeServiceConfig2(InspServiceHandle, SERVICE_CONFIG_DESCRIPTION, &svDescription)) + { + throw CWin32Exception(); + } + + CloseServiceHandle(InspServiceHandle); + CloseServiceHandle(SCMHandle); + std::cout << "Service installed." << std::endl; + } + catch(CWin32Exception e) + { + if(InspServiceHandle) + CloseServiceHandle(InspServiceHandle); + + if(SCMHandle) + CloseServiceHandle(SCMHandle); + + std::cout << "Service installation failed: " << e.what() << std::endl; + } +} + +/** Remove the windows service. This requires administrator privileges. */ +void UninstallService() +{ + SC_HANDLE InspServiceHandle = 0, SCMHandle = 0; + + try + { + SCMHandle = OpenSCManager(NULL, SERVICES_ACTIVE_DATABASE, DELETE); + if (!SCMHandle) + throw CWin32Exception(); + + InspServiceHandle = OpenService(SCMHandle, TEXT("InspIRCd"), DELETE); + if (!InspServiceHandle) + throw CWin32Exception(); + + if (!DeleteService(InspServiceHandle) && GetLastError() != ERROR_SERVICE_MARKED_FOR_DELETE) + { + throw CWin32Exception(); + } + + CloseServiceHandle(InspServiceHandle); + CloseServiceHandle(SCMHandle); + std::cout << "Service removed." << std::endl; + } + catch(CWin32Exception e) + { + if(InspServiceHandle) + CloseServiceHandle(InspServiceHandle); + + if(SCMHandle) + CloseServiceHandle(SCMHandle); + + std::cout << "Service deletion failed: " << e.what() << std::endl; + } +} + +/* In windows, our main() flows through here, before calling the 'real' main, smain() in inspircd.cpp */ +int main(int argc, char* argv[]) +{ + /* Check for parameters */ + if (argc > 1) + { + for (int i = 1; i < argc; i++) + { + if(!_stricmp(argv[i], "--installservice")) + { + InstallService(); + return 0; + } + if(!_stricmp(argv[i], "--uninstallservice") || !_stricmp(argv[i], "--removeservice")) + { + UninstallService(); + return 0; + } + } + } + + SERVICE_TABLE_ENTRY serviceTable[] = + { + { TEXT("InspIRCd"), (LPSERVICE_MAIN_FUNCTION)ServiceMain }, + { NULL, NULL } + }; + + g_bRunningAsService = true; + if( !StartServiceCtrlDispatcher(serviceTable) ) + { + // This error means that the program was not started as service. + if( GetLastError() == ERROR_FAILED_SERVICE_CONTROLLER_CONNECT ) + { + g_bRunningAsService = false; + return smain(argc, argv); + } + else + { + return EXIT_STATUS_SERVICE; + } + } + return 0; +}