From 2c5db1dfcb326fb6665b40c0bf2613257ebaf3d5 Mon Sep 17 00:00:00 2001 From: brain Date: Thu, 21 Aug 2008 11:01:51 +0000 Subject: 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 --- configure | 1 + include/inspircd.h | 2 + src/inspircd.cpp | 2 +- win/inspircdVC80.vcproj | 4 + win/inspircd_win32wrapper.h | 3 + win/win32service.cpp | 295 ++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 306 insertions(+), 1 deletion(-) create mode 100644 win/win32service.cpp 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; +} -- cgit v1.2.3