* | Inspire Internet Relay Chat Daemon |
* +------------------------------------+
*
- * InspIRCd: (C) 2002-2008 InspIRCd Development Team
- * See: http://www.inspircd.org/wiki/index.php/Credits
+ * InspIRCd: (C) 2002-2009 InspIRCd Development Team
+ * See: http://wiki.inspircd.org/Credits
*
* This program is free but copyrighted software; see
* the file COPYING for details.
*
* ---------------------------------------------------
- */\r
-\r
-#include <windows.h>\r
-#include <stdlib.h>\r
-#include <string.h>\r
-#include <stdio.h>\r
-\r
-extern int smain(int argc, char** argv);\r
-\r
-static SERVICE_STATUS_HANDLE serviceStatusHandle;\r
-static HANDLE hThreadEvent;\r
-static HANDLE killServiceEvent;\r
-static int serviceCurrentStatus;\r
-\r
-// This is used to define ChangeServiceConf2() as we can't link\r
-// directly against this symbol (see below where it is used)\r
-typedef BOOL (CALLBACK* SETSERVDESC)(SC_HANDLE,DWORD,LPVOID);\r
-\r
-SETSERVDESC ChangeServiceConf; // A function pointer for dynamic linking tricks\r
-\r
-\r
-void KillService()\r
-{\r
- /* FIXME: This should set a flag in the mainloop for shutting down */\r
- SetEvent(hThreadEvent);\r
- Sleep(2000);\r
- SetEvent(killServiceEvent);\r
-}\r
-\r
-DWORD WINAPI WorkerThread(LPDWORD param)\r
-{\r
- // *** REAL MAIN HERE ***\r
- char modname[MAX_PATH];\r
- GetModuleFileName(NULL, modname, sizeof(modname));\r
- char* argv[] = { modname, "--nofork", "--debug" };\r
- smain(3, argv);\r
- KillService();\r
- return 0;\r
-}\r
-\r
-void StartServiceThread()\r
-{\r
- DWORD dwd;\r
- CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)WorkerThread,NULL,0,&dwd);\r
-}\r
-\r
-\r
-BOOL UpdateSCMStatus (DWORD dwCurrentState, DWORD dwWin32ExitCode, DWORD dwServiceSpecificExitCode, DWORD dwCheckPoint, DWORD dwWaitHint)\r
-{\r
- BOOL success;\r
- SERVICE_STATUS serviceStatus;\r
- serviceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;\r
- serviceStatus.dwCurrentState = dwCurrentState;\r
-\r
- if (dwCurrentState == SERVICE_START_PENDING)\r
- {\r
- serviceStatus.dwControlsAccepted = 0;\r
- }\r
- else\r
- {\r
- serviceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;\r
- }\r
-\r
- if (dwServiceSpecificExitCode == 0)\r
- {\r
- serviceStatus.dwWin32ExitCode = dwWin32ExitCode;\r
- }\r
- else\r
- {\r
- serviceStatus.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;\r
- }\r
- serviceStatus.dwServiceSpecificExitCode = dwServiceSpecificExitCode;\r
- serviceStatus.dwCheckPoint = dwCheckPoint;\r
- serviceStatus.dwWaitHint = dwWaitHint;\r
-\r
- success = SetServiceStatus (serviceStatusHandle, &serviceStatus);\r
- if (!success)\r
- {\r
- KillService();\r
- }\r
- return success;\r
-}\r
-\r
-\r
-void terminateService (int code, int wincode)\r
-{\r
- UpdateSCMStatus(SERVICE_STOPPED,wincode?wincode:ERROR_SERVICE_SPECIFIC_ERROR,(wincode)?0:code,0,0);\r
- return;\r
-}\r
-\r
-\r
-VOID ServiceCtrlHandler (DWORD controlCode)\r
-{\r
- switch(controlCode)\r
- {\r
- case SERVICE_CONTROL_INTERROGATE:\r
- break;\r
- case SERVICE_CONTROL_SHUTDOWN:\r
- case SERVICE_CONTROL_STOP:\r
- serviceCurrentStatus = SERVICE_STOP_PENDING;\r
- UpdateSCMStatus(SERVICE_STOP_PENDING, NO_ERROR, 0, 1, 5000);\r
- KillService();\r
- UpdateSCMStatus(SERVICE_STOPPED, NO_ERROR, 0, 0, 0);\r
- return;\r
- default:\r
- break;\r
- }\r
- UpdateSCMStatus(serviceCurrentStatus, NO_ERROR, 0, 0, 0);\r
-}\r
-\r
-\r
-VOID ServiceMain(DWORD argc, LPTSTR *argv)\r
-{\r
- BOOL success;\r
- DWORD type=0, size=0;\r
-\r
- serviceStatusHandle = RegisterServiceCtrlHandler("InspIRCd", (LPHANDLER_FUNCTION)ServiceCtrlHandler);\r
- if (!serviceStatusHandle)\r
- {\r
- terminateService(1,GetLastError());\r
- return;\r
- }\r
-\r
- success = UpdateSCMStatus(SERVICE_START_PENDING, NO_ERROR, 0, 1, 1000);\r
- if (!success)\r
- {\r
- terminateService(2,GetLastError());\r
- return;\r
- }\r
-\r
- killServiceEvent = CreateEvent(NULL,true,false,NULL);\r
- hThreadEvent = CreateEvent(NULL,true,false,NULL);\r
-\r
- if (!killServiceEvent || !hThreadEvent)\r
- {\r
- terminateService(99,GetLastError());\r
- return;\r
- }\r
-\r
- success = UpdateSCMStatus(SERVICE_START_PENDING, NO_ERROR, 0, 2, 1000);\r
- if (!success)\r
- {\r
- terminateService(2,GetLastError());\r
- return;\r
- }\r
-\r
- StartServiceThread();\r
- serviceCurrentStatus = SERVICE_RUNNING;\r
- success = UpdateSCMStatus(SERVICE_RUNNING, NO_ERROR, 0, 0, 0);\r
- if (!success)\r
- {\r
- terminateService(6,GetLastError());\r
- return;\r
- }\r
- WaitForSingleObject (killServiceEvent, INFINITE);\r
-}\r
-\r
-void InstallService(void)\r
-{\r
- SC_HANDLE myService, scm;\r
- SERVICE_DESCRIPTION svDesc;\r
- HINSTANCE advapi32;\r
-\r
- char modname[MAX_PATH];\r
- GetModuleFileName(NULL, modname, sizeof(modname));\r
-\r
- scm = OpenSCManager(0,0,SC_MANAGER_CREATE_SERVICE);\r
- if (!scm)\r
- {\r
- printf("Unable to open service control manager: %d\n", GetLastError());\r
- return;\r
- }\r
-\r
- myService = CreateService(scm,"InspIRCd","Inspire IRC Daemon", SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS,\r
- SERVICE_AUTO_START, SERVICE_ERROR_NORMAL, modname, 0, 0, 0, NULL, NULL);\r
-\r
- if (!myService)\r
- {\r
- printf("Unable to create service: %d\n", GetLastError());\r
- CloseServiceHandle(scm);\r
- return;\r
- }\r
-\r
- // *** Set service description ***\r
- // this is supported from 5.0 (win2k) onwards only, so we can't link to the definition of\r
- // this function in advapi32.lib, otherwise the program will not run on windows NT 4. We\r
- // must use LoadLibrary and GetProcAddress to export the function name from advapi32.dll\r
- advapi32 = LoadLibrary("advapi32.dll");\r
- if (advapi32)\r
- {\r
- ChangeServiceConf = (SETSERVDESC)GetProcAddress(advapi32,"ChangeServiceConfig2A");\r
- if (ChangeServiceConf)\r
- {\r
- char desc[] = "The Inspire Internet Relay Chat Daemon hosts IRC channels and conversations. \\r
- If this service is stopped, the IRC server will not run.";\r
- svDesc.lpDescription = desc;\r
- BOOL success = ChangeServiceConf(myService,SERVICE_CONFIG_DESCRIPTION, &svDesc);\r
- if (!success)\r
- {\r
- printf("Unable to set service description: %d\n", GetLastError());\r
- CloseServiceHandle(myService);\r
- CloseServiceHandle(scm);\r
- return;\r
- }\r
- }\r
- FreeLibrary(advapi32);\r
- }\r
-\r
- printf("Service installed.\n");\r
- CloseServiceHandle(myService);\r
- CloseServiceHandle(scm);\r
-}\r
-\r
-void RemoveService(void)\r
-{\r
- SC_HANDLE myService, scm;\r
-\r
- scm = OpenSCManager(0,0,SC_MANAGER_CREATE_SERVICE);\r
- if (!scm)\r
- {\r
- printf("Unable to open service control manager: %d\n", GetLastError());\r
- return;\r
- }\r
-\r
- myService = OpenService(scm,"InspIRCd",SERVICE_ALL_ACCESS);\r
- if (!myService)\r
- {\r
- printf("Unable to open service: %d\n", GetLastError());\r
- CloseServiceHandle(scm);\r
- return;\r
- }\r
-\r
- if (!DeleteService(myService))\r
- {\r
- printf("Unable to delete service: %d\n", GetLastError());\r
- CloseServiceHandle(myService);\r
- CloseServiceHandle(scm);\r
- return;\r
- }\r
-\r
- printf("Service removed.\n");\r
- CloseServiceHandle(myService);\r
- CloseServiceHandle(scm);\r
-}\r
-\r
-/* In windows, our main() flows through here, before calling the 'real' main, smain() in inspircd.cpp */\r
-int main(int argc, char** argv)\r
-{\r
- /* Check for parameters */\r
- if (argc > 1)\r
- {\r
- if (!_stricmp(argv[1], "--installservice"))\r
- {\r
- InstallService();\r
- return 0;\r
- }\r
- else if (!_stricmp(argv[1], "--removeservice"))\r
- {\r
- RemoveService();\r
- return 0;\r
- }\r
- }\r
-\r
- /* First, check if the service is installed.\r
- * if it is not, or we're starting as non-administrator,\r
- * just call smain() and start as normal non-service\r
- * process.\r
- */\r
- SC_HANDLE myService, scm;\r
- scm = OpenSCManager(0,0,SC_MANAGER_CREATE_SERVICE);\r
- if (scm)\r
- {\r
- myService = OpenService(scm,"InspIRCd",SERVICE_ALL_ACCESS);\r
- if (!myService)\r
- {\r
- /* Service not installed or no permission to modify it */\r
- CloseServiceHandle(scm);\r
- smain(argc, argv);\r
- }\r
- }\r
- else\r
- {\r
- /* Not enough privileges to open the SCM */\r
- smain(argc, argv);\r
- }\r
-\r
- CloseServiceHandle(myService);\r
- CloseServiceHandle(scm);\r
-\r
- /* If we get here, we know the service is installed so we can start it */\r
-\r
- SERVICE_TABLE_ENTRY serviceTable[] =\r
- {\r
- {"InspIRCd", (LPSERVICE_MAIN_FUNCTION) ServiceMain },\r
- {NULL, NULL}\r
- };\r
-\r
- StartServiceCtrlDispatcher(serviceTable);\r
- return 0;\r
-}\r
+ */
+#include "inspircd_config.h"
+#include "inspircd.h"
+#include "exitcodes.h"
+#include <windows.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+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);
+
+BOOL UpdateSCMStatus(DWORD dwCurrentState, DWORD dwWin32ExitCode, DWORD dwServiceSpecificExitCode, DWORD dwCheckPoint, DWORD dwWaitHint);
+void terminateService(int code, int wincode);
+
+/* A commandline parameter handler for service specific commandline parameters */
+typedef void (*CommandlineParameterHandler)(void);
+
+/* Represents a commandline and its handler */
+struct Commandline
+{
+ const char* Switch;
+ CommandlineParameterHandler Handler;
+};
+
+/* A function pointer for dynamic linking tricks */
+SETSERVDESC ChangeServiceConf;
+
+/* Returns true if this program is running as a service, false if it is running interactive */
+bool IsAService()
+{
+ USEROBJECTFLAGS uoflags;
+ HWINSTA winstation = GetProcessWindowStation();
+ if (GetUserObjectInformation(winstation, UOI_FLAGS, &uoflags, sizeof(uoflags), NULL))
+ return ((uoflags.dwFlags & WSF_VISIBLE) == 0);
+ else
+ return false;
+}
+
+/* Kills the service by setting an event which the other thread picks up and exits */
+void KillService()
+{
+ SetEvent(hThreadEvent);
+ Sleep(2000);
+ SetEvent(killServiceEvent);
+}
+
+/** The main part of inspircd runs within this thread function. This allows the service part to run
+ * seperately on its own and to be able to kill the worker thread when its time to quit.
+ */
+DWORD WINAPI WorkerThread(LPDWORD param)
+{
+ char modname[MAX_PATH];
+ GetModuleFileName(NULL, modname, sizeof(modname));
+ char* argv[] = { modname, "--nofork" };
+ smain(2, argv);
+ KillService();
+ return 0;
+}
+
+/* This is called when all startup is done */
+void SetServiceRunning()
+{
+ if (!IsAService())
+ return;
+
+ serviceCurrentStatus = SERVICE_RUNNING;
+ BOOL success = UpdateSCMStatus(SERVICE_RUNNING, NO_ERROR, 0, 0, 0);
+ if (!success)
+ {
+ terminateService(EXIT_STATUS_UPDATESCM_FAILED, GetLastError());
+ return;
+ }
+}
+
+
+/** Starts the worker thread above */
+void StartServiceThread()
+{
+ DWORD dwd;
+ CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)WorkerThread,NULL,0,&dwd);
+}
+
+/** This function updates the status of the service in the SCM
+ * (service control manager, the services.msc applet)
+ */
+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;
+}
+
+/** This function is called by us when the service is being shut down or when it can't be started */
+void terminateService(int code, int wincode)
+{
+ UpdateSCMStatus(SERVICE_STOPPED, wincode ? wincode : ERROR_SERVICE_SPECIFIC_ERROR, wincode ? 0 : code, 0, 0);
+ return;
+}
+
+/* In windows we hook this to InspIRCd::Exit() */
+void SetServiceStopped(int status)
+{
+ if (!IsAService())
+ exit(status);
+
+ /* Are we running as a service? If so, trigger the service specific exit code */
+ terminateService(status, 0);
+ KillService();
+ exit(status);
+}
+
+/** This callback is called by windows when the state of the service has been changed */
+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);
+}
+
+/** This callback is called by windows when the service is started */
+VOID ServiceMain(DWORD argc, LPTSTR *argv)
+{
+ BOOL success;
+
+ serviceStatusHandle = RegisterServiceCtrlHandler("InspIRCd", (LPHANDLER_FUNCTION)ServiceCtrlHandler);
+ if (!serviceStatusHandle)
+ {
+ terminateService(EXIT_STATUS_RSCH_FAILED, GetLastError());
+ return;
+ }
+
+ success = UpdateSCMStatus(SERVICE_START_PENDING, NO_ERROR, 0, 1, 1000);
+ if (!success)
+ {
+ terminateService(EXIT_STATUS_UPDATESCM_FAILED, GetLastError());
+ return;
+ }
+
+ killServiceEvent = CreateEvent(NULL, true, false, NULL);
+ hThreadEvent = CreateEvent(NULL, true, false, NULL);
+
+ if (!killServiceEvent || !hThreadEvent)
+ {
+ terminateService(EXIT_STATUS_CREATE_EVENT_FAILED, GetLastError());
+ return;
+ }
+
+ success = UpdateSCMStatus(SERVICE_START_PENDING, NO_ERROR, 0, 2, 1000);
+ if (!success)
+ {
+ terminateService(EXIT_STATUS_UPDATESCM_FAILED, GetLastError());
+ return;
+ }
+
+ StartServiceThread();
+ WaitForSingleObject (killServiceEvent, INFINITE);
+}
+
+/** Install the windows service. This requires administrator privileges. */
+void InstallService()
+{
+ 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: %s\n", dlerror());
+ return;
+ }
+
+ myService = CreateService(scm,"InspIRCd","Inspire IRC Daemon", SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS,
+ SERVICE_AUTO_START, SERVICE_ERROR_NORMAL, modname, 0, 0, 0, NULL, NULL);
+
+ if (!myService)
+ {
+ printf("Unable to create service: %s\n", dlerror());
+ 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: %s\n", dlerror());
+ CloseServiceHandle(myService);
+ CloseServiceHandle(scm);
+ return;
+ }
+ }
+ FreeLibrary(advapi32);
+ }
+
+ printf("Service installed.\n");
+ CloseServiceHandle(myService);
+ CloseServiceHandle(scm);
+}
+
+/** Remove the windows service. This requires administrator privileges. */
+void RemoveService()
+{
+ SC_HANDLE myService, scm;
+
+ scm = OpenSCManager(0,0,SC_MANAGER_CREATE_SERVICE);
+ if (!scm)
+ {
+ printf("Unable to open service control manager: %s\n", dlerror());
+ return;
+ }
+
+ myService = OpenService(scm,"InspIRCd",SERVICE_ALL_ACCESS);
+ if (!myService)
+ {
+ printf("Unable to open service: %s\n", dlerror());
+ CloseServiceHandle(scm);
+ return;
+ }
+
+ if (!DeleteService(myService))
+ {
+ printf("Unable to delete service: %s\n", dlerror());
+ 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)
+{
+ /* List of parameters and handlers */
+ Commandline params[] = {
+ { "--installservice", InstallService },
+ { "--removeservice", RemoveService },
+ { NULL }
+ };
+
+ /* Check for parameters */
+ if (argc > 1)
+ {
+ for (int z = 0; params[z].Switch; ++z)
+ {
+ if (!_stricmp(argv[1], params[z].Switch))
+ {
+ params[z].Handler();
+ 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);
+ return smain(argc, argv);
+ }
+ }
+ else
+ {
+ /* Not enough privileges to open the SCM */
+ return smain(argc, argv);
+ }
+
+ CloseServiceHandle(myService);
+ CloseServiceHandle(scm);
+
+ /* Check if the process is running interactively. InspIRCd does not run interactively
+ * as a service so if this is true, we just run the non-service inspircd.
+ */
+ if (!IsAService())
+ return smain(argc, argv);
+
+ /* 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;
+}