summaryrefslogtreecommitdiff
path: root/win/win32service.cpp
diff options
context:
space:
mode:
authorbrain <brain@e03df62e-2008-0410-955e-edbf42e46eb7>2008-08-21 11:01:51 +0000
committerbrain <brain@e03df62e-2008-0410-955e-edbf42e46eb7>2008-08-21 11:01:51 +0000
commit2c5db1dfcb326fb6665b40c0bf2613257ebaf3d5 (patch)
tree9bca9f188bcfbeb37276c219c048fa7f67b44b78 /win/win32service.cpp
parentbc73d93718fbc47f33fb6b9780fc0af1b8bb916a (diff)
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
Diffstat (limited to 'win/win32service.cpp')
-rw-r--r--win/win32service.cpp295
1 files changed, 295 insertions, 0 deletions
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 <windows.h>
+#include <stdlib.h>
+#include <string.h>
+
+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;
+}