2 * InspIRCd -- Internet Relay Chat Daemon
4 * Copyright (C) 2012-2013 ChrisTX <xpipe@hotmail.de>
5 * Copyright (C) 2012-2013 Attila Molnar <attilamolnar@hush.com>
6 * Copyright (C) 2012 Robby <robby@chatbelgie.be>
7 * Copyright (C) 2008 Craig Edwards <brain@inspircd.org>
9 * This file is part of InspIRCd. InspIRCd is free software: you can
10 * redistribute it and/or modify it under the terms of the GNU General Public
11 * License as published by the Free Software Foundation, version 2.
13 * This program is distributed in the hope that it will be useful, but WITHOUT
14 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
15 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see <http://www.gnu.org/licenses/>.
24 #include "exitcodes.h"
31 static SERVICE_STATUS_HANDLE g_ServiceStatusHandle;
32 static SERVICE_STATUS g_ServiceStatus;
33 static bool g_bRunningAsService;
40 static Service_Data g_ServiceData;
42 /** The main part of inspircd runs within this thread function. This allows the service part to run
43 * separately on its own and to be able to kill the worker thread when its time to quit.
45 DWORD WINAPI WorkerThread(LPVOID param)
47 smain(g_ServiceData.argc, g_ServiceData.argv);
51 /* This is called when all startup is done */
52 void SetServiceRunning()
54 if (!g_bRunningAsService)
57 g_ServiceStatus.dwCurrentState = SERVICE_RUNNING;
58 g_ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
60 if( !SetServiceStatus( g_ServiceStatusHandle, &g_ServiceStatus ) )
61 throw CWin32Exception();
64 /* In windows we hook this to InspIRCd::Exit() */
65 void SetServiceStopped(DWORD dwStatus)
67 if (!g_bRunningAsService)
70 g_ServiceStatus.dwCurrentState = SERVICE_STOPPED;
71 if(dwStatus != EXIT_STATUS_NOERROR)
73 g_ServiceStatus.dwServiceSpecificExitCode = dwStatus;
74 g_ServiceStatus.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;
78 g_ServiceStatus.dwWin32ExitCode = ERROR_SUCCESS;
80 SetServiceStatus( g_ServiceStatusHandle, &g_ServiceStatus );
83 /** This callback is called by windows when the state of the service has been changed */
84 VOID ServiceCtrlHandler(DWORD controlCode)
88 case SERVICE_CONTROL_SHUTDOWN:
89 case SERVICE_CONTROL_STOP:
90 g_ServiceStatus.dwCurrentState = SERVICE_STOPPED;
91 SetServiceStatus( g_ServiceStatusHandle, &g_ServiceStatus );
96 /** This callback is called by windows when the service is started */
97 VOID ServiceMain(DWORD argc, LPCSTR *argv)
99 g_ServiceStatusHandle = RegisterServiceCtrlHandler(TEXT("InspIRCd"), (LPHANDLER_FUNCTION)ServiceCtrlHandler);
100 if( !g_ServiceStatusHandle )
103 g_ServiceStatus.dwCheckPoint = 1;
104 g_ServiceStatus.dwControlsAccepted = 0;
105 g_ServiceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
106 g_ServiceStatus.dwWaitHint = 5000;
107 g_ServiceStatus.dwWin32ExitCode = NO_ERROR;
108 g_ServiceStatus.dwCurrentState = SERVICE_START_PENDING;
110 if( !SetServiceStatus( g_ServiceStatusHandle, &g_ServiceStatus ) )
113 char szModuleName[MAX_PATH];
114 if(GetModuleFileNameA(NULL, szModuleName, MAX_PATH))
119 g_ServiceData.argc = argc;
121 // Note: since this memory is going to stay allocated for the rest of the execution,
122 // it doesn't make sense to free it, as it's going to be "freed" on process termination
124 g_ServiceData.argv = new char*[argc];
126 uint32_t allocsize = strnlen_s(szModuleName, MAX_PATH) + 1;
127 g_ServiceData.argv[0] = new char[allocsize];
128 strcpy_s(g_ServiceData.argv[0], allocsize, szModuleName);
130 for(uint32_t i = 1; i < argc; i++)
132 allocsize = strnlen_s(argv[i], MAX_PATH) + 1;
133 g_ServiceData.argv[i] = new char[allocsize];
134 strcpy_s(g_ServiceData.argv[i], allocsize, argv[i]);
137 *(strrchr(szModuleName, '\\') + 1) = NULL;
138 SetCurrentDirectoryA(szModuleName);
140 HANDLE hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)WorkerThread, NULL, 0, NULL);
143 WaitForSingleObject(hThread, INFINITE);
144 CloseHandle(hThread);
149 g_ServiceStatus.dwCurrentState = SERVICE_STOPPED;
150 g_ServiceStatus.dwWin32ExitCode = ERROR_OUTOFMEMORY;
151 SetServiceStatus( g_ServiceStatusHandle, &g_ServiceStatus );
154 if(g_ServiceStatus.dwCurrentState == SERVICE_STOPPED)
157 g_ServiceStatus.dwCurrentState = SERVICE_STOPPED;
158 g_ServiceStatus.dwWin32ExitCode = GetLastError();
159 SetServiceStatus( g_ServiceStatusHandle, &g_ServiceStatus );
162 /** Install the windows service. This requires administrator privileges. */
163 void InstallService()
165 SC_HANDLE InspServiceHandle = 0, SCMHandle = 0;
168 TCHAR tszBinaryPath[MAX_PATH];
169 if(!GetModuleFileName(NULL, tszBinaryPath, _countof(tszBinaryPath)))
171 throw CWin32Exception();
174 SCMHandle = OpenSCManager(0, 0, SC_MANAGER_ALL_ACCESS);
177 throw CWin32Exception();
180 InspServiceHandle = CreateService(SCMHandle, TEXT("InspIRCd"),TEXT("InspIRCd Daemon"), SERVICE_CHANGE_CONFIG, SERVICE_WIN32_OWN_PROCESS,
181 SERVICE_AUTO_START, SERVICE_ERROR_NORMAL, tszBinaryPath, 0, 0, 0, TEXT("NT AUTHORITY\\NetworkService"), NULL);
183 if (!InspServiceHandle)
185 throw CWin32Exception();
188 TCHAR tszDescription[] = TEXT("The InspIRCd service hosts IRC channels and conversations. If this service is stopped, the IRC server will be unavailable.");
189 SERVICE_DESCRIPTION svDescription = { tszDescription };
190 if(!ChangeServiceConfig2(InspServiceHandle, SERVICE_CONFIG_DESCRIPTION, &svDescription))
192 throw CWin32Exception();
195 CloseServiceHandle(InspServiceHandle);
196 CloseServiceHandle(SCMHandle);
197 std::cout << "Service installed." << std::endl;
199 catch(CWin32Exception e)
201 if(InspServiceHandle)
202 CloseServiceHandle(InspServiceHandle);
205 CloseServiceHandle(SCMHandle);
207 std::cout << "Service installation failed: " << e.what() << std::endl;
211 /** Remove the windows service. This requires administrator privileges. */
212 void UninstallService()
214 SC_HANDLE InspServiceHandle = 0, SCMHandle = 0;
218 SCMHandle = OpenSCManager(NULL, SERVICES_ACTIVE_DATABASE, DELETE);
220 throw CWin32Exception();
222 InspServiceHandle = OpenService(SCMHandle, TEXT("InspIRCd"), DELETE);
223 if (!InspServiceHandle)
224 throw CWin32Exception();
226 if (!DeleteService(InspServiceHandle) && GetLastError() != ERROR_SERVICE_MARKED_FOR_DELETE)
228 throw CWin32Exception();
231 CloseServiceHandle(InspServiceHandle);
232 CloseServiceHandle(SCMHandle);
233 std::cout << "Service removed." << std::endl;
235 catch(CWin32Exception e)
237 if(InspServiceHandle)
238 CloseServiceHandle(InspServiceHandle);
241 CloseServiceHandle(SCMHandle);
243 std::cout << "Service deletion failed: " << e.what() << std::endl;
247 /* In windows, our main() flows through here, before calling the 'real' main, smain() in inspircd.cpp */
248 int main(int argc, char* argv[])
250 /* Check for parameters */
253 for (int i = 1; i < argc; i++)
255 if(!_stricmp(argv[i], "--installservice"))
260 if(!_stricmp(argv[i], "--uninstallservice") || !_stricmp(argv[i], "--removeservice"))
268 SERVICE_TABLE_ENTRY serviceTable[] =
270 { TEXT("InspIRCd"), (LPSERVICE_MAIN_FUNCTION)ServiceMain },
274 g_bRunningAsService = true;
275 if( !StartServiceCtrlDispatcher(serviceTable) )
277 // This error means that the program was not started as service.
278 if( GetLastError() == ERROR_FAILED_SERVICE_CONTROLLER_CONNECT )
280 g_bRunningAsService = false;
281 return smain(argc, argv);
285 return EXIT_STATUS_SERVICE;