- * ---------------------------------------------------
- */\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
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "config.h"
+#include "inspircd.h"
+#include "exitcodes.h"
+#include <windows.h>
+#include <cstdlib>
+#include <cstring>
+#include <cstdio>
+#include <iostream>
+
+static SERVICE_STATUS_HANDLE g_ServiceStatusHandle;
+static SERVICE_STATUS g_ServiceStatus;
+static bool g_bRunningAsService;
+
+struct Service_Data {
+ DWORD argc;
+ LPSTR *argv;
+};
+
+static Service_Data g_ServiceData;
+
+/** 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(LPVOID param)
+{
+ smain(g_ServiceData.argc, g_ServiceData.argv);
+ return 0;
+}
+
+/* This is called when all startup is done */
+void SetServiceRunning()
+{
+ if (!g_bRunningAsService)
+ return;
+
+ g_ServiceStatus.dwCurrentState = SERVICE_RUNNING;
+ g_ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
+
+ if( !SetServiceStatus( g_ServiceStatusHandle, &g_ServiceStatus ) )
+ throw CWin32Exception();
+}
+
+/* In windows we hook this to InspIRCd::Exit() */
+void SetServiceStopped(DWORD dwStatus)
+{
+ if (!g_bRunningAsService)
+ return;
+
+ g_ServiceStatus.dwCurrentState = SERVICE_STOPPED;
+ if(dwStatus != EXIT_STATUS_NOERROR)
+ {
+ g_ServiceStatus.dwServiceSpecificExitCode = dwStatus;
+ g_ServiceStatus.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;
+ }
+ else
+ {
+ g_ServiceStatus.dwWin32ExitCode = ERROR_SUCCESS;
+ }
+ SetServiceStatus( g_ServiceStatusHandle, &g_ServiceStatus );
+}
+
+/** This callback is called by windows when the state of the service has been changed */
+VOID ServiceCtrlHandler(DWORD controlCode)
+{
+ switch(controlCode)
+ {
+ case SERVICE_CONTROL_SHUTDOWN:
+ case SERVICE_CONTROL_STOP:
+ g_ServiceStatus.dwCurrentState = SERVICE_STOPPED;
+ SetServiceStatus( g_ServiceStatusHandle, &g_ServiceStatus );
+ break;
+ }
+}
+
+/** This callback is called by windows when the service is started */
+VOID ServiceMain(DWORD argc, LPCSTR *argv)
+{
+ g_ServiceStatusHandle = RegisterServiceCtrlHandler(TEXT("InspIRCd"), (LPHANDLER_FUNCTION)ServiceCtrlHandler);
+ if( !g_ServiceStatusHandle )
+ return;
+
+ g_ServiceStatus.dwCheckPoint = 1;
+ g_ServiceStatus.dwControlsAccepted = 0;
+ g_ServiceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
+ g_ServiceStatus.dwWaitHint = 5000;
+ g_ServiceStatus.dwWin32ExitCode = NO_ERROR;
+ g_ServiceStatus.dwCurrentState = SERVICE_START_PENDING;
+
+ if( !SetServiceStatus( g_ServiceStatusHandle, &g_ServiceStatus ) )
+ return;
+
+ char szModuleName[MAX_PATH];
+ if(GetModuleFileNameA(NULL, szModuleName, MAX_PATH))
+ {
+ if(!argc)
+ argc = 1;
+
+ g_ServiceData.argc = argc;
+
+ // Note: since this memory is going to stay allocated for the rest of the execution,
+ // it doesn't make sense to free it, as it's going to be "freed" on process termination
+ try {
+ g_ServiceData.argv = new char*[argc];
+
+ uint32_t allocsize = strnlen_s(szModuleName, MAX_PATH) + 1;
+ g_ServiceData.argv[0] = new char[allocsize];
+ strcpy_s(g_ServiceData.argv[0], allocsize, szModuleName);
+
+ for(uint32_t i = 1; i < argc; i++)
+ {
+ allocsize = strnlen_s(argv[i], MAX_PATH) + 1;
+ g_ServiceData.argv[i] = new char[allocsize];
+ strcpy_s(g_ServiceData.argv[i], allocsize, argv[i]);
+ }
+
+ *(strrchr(szModuleName, '\\') + 1) = NULL;
+ SetCurrentDirectoryA(szModuleName);
+
+ HANDLE hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)WorkerThread, NULL, 0, NULL);
+ if (hThread != NULL)
+ {
+ WaitForSingleObject(hThread, INFINITE);
+ CloseHandle(hThread);
+ }
+ }
+ catch(...)
+ {
+ g_ServiceStatus.dwCurrentState = SERVICE_STOPPED;
+ g_ServiceStatus.dwWin32ExitCode = ERROR_OUTOFMEMORY;
+ SetServiceStatus( g_ServiceStatusHandle, &g_ServiceStatus );
+ }
+ }
+ if(g_ServiceStatus.dwCurrentState == SERVICE_STOPPED)
+ return;
+
+ g_ServiceStatus.dwCurrentState = SERVICE_STOPPED;
+ g_ServiceStatus.dwWin32ExitCode = GetLastError();
+ SetServiceStatus( g_ServiceStatusHandle, &g_ServiceStatus );
+}
+
+/** Install the windows service. This requires administrator privileges. */
+void InstallService()
+{
+ SC_HANDLE InspServiceHandle = 0, SCMHandle = 0;
+
+ try {
+ TCHAR tszBinaryPath[MAX_PATH];
+ if(!GetModuleFileName(NULL, tszBinaryPath, _countof(tszBinaryPath)))
+ {
+ throw CWin32Exception();
+ }
+
+ SCMHandle = OpenSCManager(0, 0, SC_MANAGER_ALL_ACCESS);
+ if (!SCMHandle)
+ {
+ throw CWin32Exception();
+ }
+
+ InspServiceHandle = CreateService(SCMHandle, TEXT("InspIRCd"),TEXT("InspIRCd Daemon"), SERVICE_CHANGE_CONFIG, SERVICE_WIN32_OWN_PROCESS,
+ SERVICE_AUTO_START, SERVICE_ERROR_NORMAL, tszBinaryPath, 0, 0, 0, TEXT("NT AUTHORITY\\NetworkService"), NULL);
+
+ if (!InspServiceHandle)
+ {
+ throw CWin32Exception();
+ }
+
+ TCHAR tszDescription[] = TEXT("The InspIRCd service hosts IRC channels and conversations. If this service is stopped, the IRC server will be unavailable.");
+ SERVICE_DESCRIPTION svDescription = { tszDescription };
+ if(!ChangeServiceConfig2(InspServiceHandle, SERVICE_CONFIG_DESCRIPTION, &svDescription))
+ {
+ throw CWin32Exception();
+ }
+
+ CloseServiceHandle(InspServiceHandle);
+ CloseServiceHandle(SCMHandle);
+ std::cout << "Service installed." << std::endl;
+ }
+ catch(CWin32Exception e)
+ {
+ if(InspServiceHandle)
+ CloseServiceHandle(InspServiceHandle);
+
+ if(SCMHandle)
+ CloseServiceHandle(SCMHandle);
+
+ std::cout << "Service installation failed: " << e.what() << std::endl;
+ }
+}
+
+/** Remove the windows service. This requires administrator privileges. */
+void UninstallService()
+{
+ SC_HANDLE InspServiceHandle = 0, SCMHandle = 0;
+
+ try
+ {
+ SCMHandle = OpenSCManager(NULL, SERVICES_ACTIVE_DATABASE, DELETE);
+ if (!SCMHandle)
+ throw CWin32Exception();
+
+ InspServiceHandle = OpenService(SCMHandle, TEXT("InspIRCd"), DELETE);
+ if (!InspServiceHandle)
+ throw CWin32Exception();
+
+ if (!DeleteService(InspServiceHandle) && GetLastError() != ERROR_SERVICE_MARKED_FOR_DELETE)
+ {
+ throw CWin32Exception();
+ }
+
+ CloseServiceHandle(InspServiceHandle);
+ CloseServiceHandle(SCMHandle);
+ std::cout << "Service removed." << std::endl;
+ }
+ catch(CWin32Exception e)
+ {
+ if(InspServiceHandle)
+ CloseServiceHandle(InspServiceHandle);
+
+ if(SCMHandle)
+ CloseServiceHandle(SCMHandle);
+
+ std::cout << "Service deletion failed: " << e.what() << std::endl;
+ }
+}
+
+/* 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 > 1)
+ {
+ for (int i = 1; i < argc; i++)
+ {
+ if(!_stricmp(argv[i], "--installservice"))
+ {
+ InstallService();
+ return 0;
+ }
+ if(!_stricmp(argv[i], "--uninstallservice") || !_stricmp(argv[i], "--removeservice"))
+ {
+ UninstallService();
+ return 0;
+ }
+ }
+ }
+
+ SERVICE_TABLE_ENTRY serviceTable[] =
+ {
+ { TEXT("InspIRCd"), (LPSERVICE_MAIN_FUNCTION)ServiceMain },
+ { NULL, NULL }
+ };
+
+ g_bRunningAsService = true;
+ if( !StartServiceCtrlDispatcher(serviceTable) )
+ {
+ // This error means that the program was not started as service.
+ if( GetLastError() == ERROR_FAILED_SERVICE_CONTROLLER_CONNECT )
+ {
+ g_bRunningAsService = false;
+ return smain(argc, argv);
+ }
+ else
+ {
+ return EXIT_STATUS_SERVICE;
+ }
+ }
+ return 0;
+}