]> git.netwichtig.de Git - user/henk/code/inspircd.git/blobdiff - win/win32service.cpp
Fix a bunch of really obvious unnecessary includes.
[user/henk/code/inspircd.git] / win / win32service.cpp
index 0f8a2010806f8eac9edaf137c79d31a1412b4691..45aba726824c0d3af6146934f159887c97a500fa 100644 (file)
-/*       +------------------------------------+
- *       | Inspire Internet Relay Chat Daemon |
- *       +------------------------------------+
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
  *
- *  InspIRCd: (C) 2002-2008 InspIRCd Development Team
- * See: http://www.inspircd.org/wiki/index.php/Credits
+ *   Copyright (C) 2013 Sadie Powell <sadie@witchery.services>
+ *   Copyright (C) 2012-2013 ChrisTX <xpipe@hotmail.de>
+ *   Copyright (C) 2012-2013 Attila Molnar <attilamolnar@hush.com>
+ *   Copyright (C) 2012 Robby <robby@chatbelgie.be>
+ *   Copyright (C) 2008 Craig Edwards <brain@inspircd.org>
  *
- * This program is free but copyrighted software; see
- *         the file COPYING for details.
+ * This file is part of InspIRCd.  InspIRCd is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, version 2.
  *
- * ---------------------------------------------------
- */\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
-extern const char* dlerror();\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
-/* A commandline parameter handler for service specific commandline parameters */\r
-typedef void (*CommandlineParameterHandler)(void);\r
-\r
-/* Represents a commandline and its handler */\r
-struct Commandline\r
-{\r
-       const char* Switch;\r
-       CommandlineParameterHandler Handler;\r
-};\r
-\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()\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: %s\n", dlerror());\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: %s\n", dlerror());\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: %s\n", dlerror());\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()\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: %s\n", dlerror());\r
-               return;\r
-       }\r
-\r
-       myService = OpenService(scm,"InspIRCd",SERVICE_ALL_ACCESS);\r
-       if (!myService)\r
-       {\r
-               printf("Unable to open service: %s\n", dlerror());\r
-               CloseServiceHandle(scm);\r
-               return;\r
-       }\r
-\r
-       if (!DeleteService(myService))\r
-       {\r
-               printf("Unable to delete service: %s\n", dlerror());\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
-\r
-       Commandline params[] = {\r
-               { "--installservice", InstallService },\r
-               { "--removeservice", RemoveService },\r
-               { NULL }\r
-       };\r
-\r
-       /* Check for parameters */\r
-       if (argc > 1)\r
-       {\r
-               for (int z = 0; params[z].Switch; ++z)\r
-               {\r
-                       if (!_stricmp(argv[1], params[z].Switch))\r
-                       {\r
-                               params[z].Handler();\r
-                               return 0;\r
-                       }\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
-                       return smain(argc, argv);\r
-               }\r
-       }\r
-       else\r
-       {\r
-               /* Not enough privileges to open the SCM */\r
-               return smain(argc, argv);\r
-       }\r
-\r
-       CloseServiceHandle(myService);\r
-       CloseServiceHandle(scm);\r
-\r
-       /* Check if the process is running interactively. InspIRCd does not run interactively\r
-        * as a service so if this is true, we just run the non-service inspircd.\r
-        */\r
-       USEROBJECTFLAGS uoflags;\r
-       HWINSTA winstation = GetProcessWindowStation();\r
-       if (GetUserObjectInformation(winstation, UOI_FLAGS, &uoflags, sizeof(uoflags), NULL))\r
-       {\r
-               if (uoflags.dwFlags == WSF_VISIBLE)\r
-                       return smain(argc, argv);\r
-       }\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 "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
+ * separately 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;
+}