2 * InspIRCd -- Internet Relay Chat Daemon
4 * Copyright (C) 2013 Sadie Powell <sadie@witchery.services>
5 * Copyright (C) 2012-2013 ChrisTX <xpipe@hotmail.de>
6 * Copyright (C) 2012-2013 Attila Molnar <attilamolnar@hush.com>
7 * Copyright (C) 2012 Robby <robby@chatbelgie.be>
8 * Copyright (C) 2008 Craig Edwards <brain@inspircd.org>
10 * This file is part of InspIRCd. InspIRCd is free software: you can
11 * redistribute it and/or modify it under the terms of the GNU General Public
12 * License as published by the Free Software Foundation, version 2.
14 * This program is distributed in the hope that it will be useful, but WITHOUT
15 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
16 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
19 * You should have received a copy of the GNU General Public License
20 * along with this program. If not, see <http://www.gnu.org/licenses/>.
25 #include "exitcodes.h"
32 static SERVICE_STATUS_HANDLE g_ServiceStatusHandle;
33 static SERVICE_STATUS g_ServiceStatus;
34 static bool g_bRunningAsService;
41 static Service_Data g_ServiceData;
43 /** The main part of inspircd runs within this thread function. This allows the service part to run
44 * separately on its own and to be able to kill the worker thread when its time to quit.
46 DWORD WINAPI WorkerThread(LPVOID param)
48 smain(g_ServiceData.argc, g_ServiceData.argv);
52 /* This is called when all startup is done */
53 void SetServiceRunning()
55 if (!g_bRunningAsService)
58 g_ServiceStatus.dwCurrentState = SERVICE_RUNNING;
59 g_ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
61 if( !SetServiceStatus( g_ServiceStatusHandle, &g_ServiceStatus ) )
62 throw CWin32Exception();
65 /* In windows we hook this to InspIRCd::Exit() */
66 void SetServiceStopped(DWORD dwStatus)
68 if (!g_bRunningAsService)
71 g_ServiceStatus.dwCurrentState = SERVICE_STOPPED;
72 if(dwStatus != EXIT_STATUS_NOERROR)
74 g_ServiceStatus.dwServiceSpecificExitCode = dwStatus;
75 g_ServiceStatus.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;
79 g_ServiceStatus.dwWin32ExitCode = ERROR_SUCCESS;
81 SetServiceStatus( g_ServiceStatusHandle, &g_ServiceStatus );
84 /** This callback is called by windows when the state of the service has been changed */
85 VOID ServiceCtrlHandler(DWORD controlCode)
89 case SERVICE_CONTROL_SHUTDOWN:
90 case SERVICE_CONTROL_STOP:
91 g_ServiceStatus.dwCurrentState = SERVICE_STOPPED;
92 SetServiceStatus( g_ServiceStatusHandle, &g_ServiceStatus );
97 /** This callback is called by windows when the service is started */
98 VOID ServiceMain(DWORD argc, LPCSTR *argv)
100 g_ServiceStatusHandle = RegisterServiceCtrlHandler(TEXT("InspIRCd"), (LPHANDLER_FUNCTION)ServiceCtrlHandler);
101 if( !g_ServiceStatusHandle )
104 g_ServiceStatus.dwCheckPoint = 1;
105 g_ServiceStatus.dwControlsAccepted = 0;
106 g_ServiceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
107 g_ServiceStatus.dwWaitHint = 5000;
108 g_ServiceStatus.dwWin32ExitCode = NO_ERROR;
109 g_ServiceStatus.dwCurrentState = SERVICE_START_PENDING;
111 if( !SetServiceStatus( g_ServiceStatusHandle, &g_ServiceStatus ) )
114 char szModuleName[MAX_PATH];
115 if(GetModuleFileNameA(NULL, szModuleName, MAX_PATH))
120 g_ServiceData.argc = argc;
122 // Note: since this memory is going to stay allocated for the rest of the execution,
123 // it doesn't make sense to free it, as it's going to be "freed" on process termination
125 g_ServiceData.argv = new char*[argc];
127 uint32_t allocsize = strnlen_s(szModuleName, MAX_PATH) + 1;
128 g_ServiceData.argv[0] = new char[allocsize];
129 strcpy_s(g_ServiceData.argv[0], allocsize, szModuleName);
131 for(uint32_t i = 1; i < argc; i++)
133 allocsize = strnlen_s(argv[i], MAX_PATH) + 1;
134 g_ServiceData.argv[i] = new char[allocsize];
135 strcpy_s(g_ServiceData.argv[i], allocsize, argv[i]);
138 *(strrchr(szModuleName, '\\') + 1) = NULL;
139 SetCurrentDirectoryA(szModuleName);
141 HANDLE hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)WorkerThread, NULL, 0, NULL);
144 WaitForSingleObject(hThread, INFINITE);
145 CloseHandle(hThread);
150 g_ServiceStatus.dwCurrentState = SERVICE_STOPPED;
151 g_ServiceStatus.dwWin32ExitCode = ERROR_OUTOFMEMORY;
152 SetServiceStatus( g_ServiceStatusHandle, &g_ServiceStatus );
155 if(g_ServiceStatus.dwCurrentState == SERVICE_STOPPED)
158 g_ServiceStatus.dwCurrentState = SERVICE_STOPPED;
159 g_ServiceStatus.dwWin32ExitCode = GetLastError();
160 SetServiceStatus( g_ServiceStatusHandle, &g_ServiceStatus );
163 /** Install the windows service. This requires administrator privileges. */
164 void InstallService()
166 SC_HANDLE InspServiceHandle = 0, SCMHandle = 0;
169 TCHAR tszBinaryPath[MAX_PATH];
170 if(!GetModuleFileName(NULL, tszBinaryPath, _countof(tszBinaryPath)))
172 throw CWin32Exception();
175 SCMHandle = OpenSCManager(0, 0, SC_MANAGER_ALL_ACCESS);
178 throw CWin32Exception();
181 InspServiceHandle = CreateService(SCMHandle, TEXT("InspIRCd"),TEXT("InspIRCd Daemon"), SERVICE_CHANGE_CONFIG, SERVICE_WIN32_OWN_PROCESS,
182 SERVICE_AUTO_START, SERVICE_ERROR_NORMAL, tszBinaryPath, 0, 0, 0, TEXT("NT AUTHORITY\\NetworkService"), NULL);
184 if (!InspServiceHandle)
186 throw CWin32Exception();
189 TCHAR tszDescription[] = TEXT("The InspIRCd service hosts IRC channels and conversations. If this service is stopped, the IRC server will be unavailable.");
190 SERVICE_DESCRIPTION svDescription = { tszDescription };
191 if(!ChangeServiceConfig2(InspServiceHandle, SERVICE_CONFIG_DESCRIPTION, &svDescription))
193 throw CWin32Exception();
196 CloseServiceHandle(InspServiceHandle);
197 CloseServiceHandle(SCMHandle);
198 std::cout << "Service installed." << std::endl;
200 catch(CWin32Exception e)
202 if(InspServiceHandle)
203 CloseServiceHandle(InspServiceHandle);
206 CloseServiceHandle(SCMHandle);
208 std::cout << "Service installation failed: " << e.what() << std::endl;
212 /** Remove the windows service. This requires administrator privileges. */
213 void UninstallService()
215 SC_HANDLE InspServiceHandle = 0, SCMHandle = 0;
219 SCMHandle = OpenSCManager(NULL, SERVICES_ACTIVE_DATABASE, DELETE);
221 throw CWin32Exception();
223 InspServiceHandle = OpenService(SCMHandle, TEXT("InspIRCd"), DELETE);
224 if (!InspServiceHandle)
225 throw CWin32Exception();
227 if (!DeleteService(InspServiceHandle) && GetLastError() != ERROR_SERVICE_MARKED_FOR_DELETE)
229 throw CWin32Exception();
232 CloseServiceHandle(InspServiceHandle);
233 CloseServiceHandle(SCMHandle);
234 std::cout << "Service removed." << std::endl;
236 catch(CWin32Exception e)
238 if(InspServiceHandle)
239 CloseServiceHandle(InspServiceHandle);
242 CloseServiceHandle(SCMHandle);
244 std::cout << "Service deletion failed: " << e.what() << std::endl;
248 /* In windows, our main() flows through here, before calling the 'real' main, smain() in inspircd.cpp */
249 int main(int argc, char* argv[])
251 /* Check for parameters */
254 for (int i = 1; i < argc; i++)
256 if(!_stricmp(argv[i], "--installservice"))
261 if(!_stricmp(argv[i], "--uninstallservice") || !_stricmp(argv[i], "--removeservice"))
269 SERVICE_TABLE_ENTRY serviceTable[] =
271 { TEXT("InspIRCd"), (LPSERVICE_MAIN_FUNCTION)ServiceMain },
275 g_bRunningAsService = true;
276 if( !StartServiceCtrlDispatcher(serviceTable) )
278 // This error means that the program was not started as service.
279 if( GetLastError() == ERROR_FAILED_SERVICE_CONTROLLER_CONNECT )
281 g_bRunningAsService = false;
282 return smain(argc, argv);
286 return EXIT_STATUS_SERVICE;