summaryrefslogtreecommitdiff
path: root/win/win32service.cpp
diff options
context:
space:
mode:
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;
+}