From: brain Date: Thu, 21 Aug 2008 11:01:51 +0000 (+0000) Subject: add support for windows service. This now is part of inspircd itself being as we... X-Git-Tag: v2.0.23~2804 X-Git-Url: https://git.netwichtig.de/gitweb/?a=commitdiff_plain;h=2c5db1dfcb326fb6665b40c0bf2613257ebaf3d5;p=user%2Fhenk%2Fcode%2Finspircd.git add support for windows service. This now is part of inspircd itself being as we have native build. It doesnt quite work yet in all the ways it should. git-svn-id: http://svn.inspircd.org/repository/trunk/inspircd@10193 e03df62e-2008-0410-955e-edbf42e46eb7 --- diff --git a/configure b/configure index e47512a97..616ebfea7 100755 --- a/configure +++ b/configure @@ -1159,6 +1159,7 @@ sub writefiles { #define OPTIMISATION $config{OPTIMITEMP} #define LIBRARYDIR "$config{LIBRARY_DIR}" #define SYSTEM "$incos" +#define ENTRYPOINT int main(int argc, char** argv) EOF print FILEHANDLE "#define MAXBUF " . ($config{MAXBUF}+2) . "\n"; diff --git a/include/inspircd.h b/include/inspircd.h index 50dc5c73b..81a9f3cbe 100644 --- a/include/inspircd.h +++ b/include/inspircd.h @@ -911,4 +911,6 @@ class CoreExport InspIRCd : public classbase } }; +ENTRYPOINT; + #endif diff --git a/src/inspircd.cpp b/src/inspircd.cpp index 8d3a8fc27..e2ed1282c 100644 --- a/src/inspircd.cpp +++ b/src/inspircd.cpp @@ -867,7 +867,7 @@ void InspIRCd::BufferedSocketCull() * An ircd in five lines! bwahahaha. ahahahahaha. ahahah *cough*. */ -int main(int argc, char ** argv) +ENTRYPOINT { SI = new InspIRCd(argc, argv); mysig = &SI->s_signal; diff --git a/win/inspircdVC80.vcproj b/win/inspircdVC80.vcproj index 3ddddf8bc..263c597ff 100644 --- a/win/inspircdVC80.vcproj +++ b/win/inspircdVC80.vcproj @@ -527,6 +527,10 @@ RelativePath="..\src\wildcard.cpp" > + + diff --git a/win/inspircd_win32wrapper.h b/win/inspircd_win32wrapper.h index 600882839..94f799c18 100644 --- a/win/inspircd_win32wrapper.h +++ b/win/inspircd_win32wrapper.h @@ -55,6 +55,9 @@ #define DllExport __declspec(dllimport) #endif +/* Redirect main() through a different method in win32service.cpp, to intercept service startup */ +#define ENTRYPOINT CoreExport int smain(int argc, char** argv) + /* Disable the deprecation warnings.. it spams :P */ #define _CRT_SECURE_NO_DEPRECATE #define _SCL_SECURE_NO_DEPRECATE diff --git a/win/win32service.cpp b/win/win32service.cpp new file mode 100644 index 000000000..100afba73 --- /dev/null +++ b/win/win32service.cpp @@ -0,0 +1,295 @@ +/* +------------------------------------+ + * | Inspire Internet Relay Chat Daemon | + * +------------------------------------+ + * + * InspIRCd: (C) 2002-2008 InspIRCd Development Team + * See: http://www.inspircd.org/wiki/index.php/Credits + * + * This program is free but copyrighted software; see + * the file COPYING for details. + * + * --------------------------------------------------- + */ + +#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" }; + smain(1, 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) + 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, 0, 0); + + if (!myService) + { + 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) + { + CloseServiceHandle(myService); + CloseServiceHandle(scm); + return; + } + } + FreeLibrary(advapi32); + } + + CloseServiceHandle(myService); + CloseServiceHandle(scm); +} + +void RemoveService(void) +{ + SC_HANDLE myService, scm; + + scm = OpenSCManager(0,0,SC_MANAGER_CREATE_SERVICE); + if (!scm) + return; + + myService = OpenService(scm,"InspIRCd",SERVICE_ALL_ACCESS); + if (!myService) + { + CloseServiceHandle(scm); + return; + } + + if (!DeleteService(myService)) + { + CloseServiceHandle(myService); + CloseServiceHandle(scm); + return; + } + + 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 > 0) + { + 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, just call smain(). + */ + 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; +}