2 * InspIRCd -- Internet Relay Chat Daemon
4 * Copyright (C) 2013 Sadie Powell <sadie@witchery.services>
5 * Copyright (C) 2013 Attila Molnar <attilamolnar@hush.com>
6 * Copyright (C) 2012-2013 ChrisTX <xpipe@hotmail.de>
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/>.
26 #include "exitcodes.h"
33 static SERVICE_STATUS_HANDLE g_ServiceStatusHandle;
34 static SERVICE_STATUS g_ServiceStatus;
35 static bool g_bRunningAsService;
42 static Service_Data g_ServiceData;
44 /** The main part of inspircd runs within this thread function. This allows the service part to run
45 * seperately on its own and to be able to kill the worker thread when its time to quit.
47 DWORD WINAPI WorkerThread(LPVOID param)
49 smain(g_ServiceData.argc, g_ServiceData.argv);
53 /* This is called when all startup is done */
54 void SetServiceRunning()
56 if (!g_bRunningAsService)
59 g_ServiceStatus.dwCurrentState = SERVICE_RUNNING;
60 g_ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
62 if( !SetServiceStatus( g_ServiceStatusHandle, &g_ServiceStatus ) )
63 throw CWin32Exception();
66 /* In windows we hook this to InspIRCd::Exit() */
67 void SetServiceStopped(DWORD dwStatus)
69 if (!g_bRunningAsService)
72 g_ServiceStatus.dwCurrentState = SERVICE_STOPPED;
73 if(dwStatus != EXIT_STATUS_NOERROR)
75 g_ServiceStatus.dwServiceSpecificExitCode = dwStatus;
76 g_ServiceStatus.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;
80 g_ServiceStatus.dwWin32ExitCode = ERROR_SUCCESS;
82 SetServiceStatus( g_ServiceStatusHandle, &g_ServiceStatus );
85 /** This callback is called by windows when the state of the service has been changed */
86 VOID ServiceCtrlHandler(DWORD controlCode)
90 case SERVICE_CONTROL_SHUTDOWN:
91 case SERVICE_CONTROL_STOP:
92 g_ServiceStatus.dwCurrentState = SERVICE_STOPPED;
93 SetServiceStatus( g_ServiceStatusHandle, &g_ServiceStatus );
98 /** This callback is called by windows when the service is started */
99 VOID ServiceMain(DWORD argc, LPCSTR *argv)
101 g_ServiceStatusHandle = RegisterServiceCtrlHandler(TEXT("InspIRCd"), (LPHANDLER_FUNCTION)ServiceCtrlHandler);
102 if( !g_ServiceStatusHandle )
105 g_ServiceStatus.dwCheckPoint = 1;
106 g_ServiceStatus.dwControlsAccepted = 0;
107 g_ServiceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
108 g_ServiceStatus.dwWaitHint = 5000;
109 g_ServiceStatus.dwWin32ExitCode = NO_ERROR;
110 g_ServiceStatus.dwCurrentState = SERVICE_START_PENDING;
112 if( !SetServiceStatus( g_ServiceStatusHandle, &g_ServiceStatus ) )
115 char szModuleName[MAX_PATH];
116 if(GetModuleFileNameA(NULL, szModuleName, MAX_PATH))
121 g_ServiceData.argc = argc;
123 // Note: since this memory is going to stay allocated for the rest of the execution,
124 // it doesn't make sense to free it, as it's going to be "freed" on process termination
126 g_ServiceData.argv = new char*[argc];
128 uint32_t allocsize = strnlen_s(szModuleName, MAX_PATH) + 1;
129 g_ServiceData.argv[0] = new char[allocsize];
130 strcpy_s(g_ServiceData.argv[0], allocsize, szModuleName);
132 for(uint32_t i = 1; i < argc; i++)
134 allocsize = strnlen_s(argv[i], MAX_PATH) + 1;
135 g_ServiceData.argv[i] = new char[allocsize];
136 strcpy_s(g_ServiceData.argv[i], allocsize, argv[i]);
139 *(strrchr(szModuleName, '\\') + 1) = NULL;
140 SetCurrentDirectoryA(szModuleName);
142 HANDLE hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)WorkerThread, NULL, 0, NULL);
145 WaitForSingleObject(hThread, INFINITE);
146 CloseHandle(hThread);
151 g_ServiceStatus.dwCurrentState = SERVICE_STOPPED;
152 g_ServiceStatus.dwWin32ExitCode = ERROR_OUTOFMEMORY;
153 SetServiceStatus( g_ServiceStatusHandle, &g_ServiceStatus );
156 if(g_ServiceStatus.dwCurrentState == SERVICE_STOPPED)
159 g_ServiceStatus.dwCurrentState = SERVICE_STOPPED;
160 g_ServiceStatus.dwWin32ExitCode = GetLastError();
161 SetServiceStatus( g_ServiceStatusHandle, &g_ServiceStatus );
164 /** Install the windows service. This requires administrator privileges. */
165 void InstallService()
167 SC_HANDLE InspServiceHandle = 0, SCMHandle = 0;
170 TCHAR tszBinaryPath[MAX_PATH];
171 if(!GetModuleFileName(NULL, tszBinaryPath, _countof(tszBinaryPath)))
173 throw CWin32Exception();
176 SCMHandle = OpenSCManager(0, 0, SC_MANAGER_ALL_ACCESS);
179 throw CWin32Exception();
182 InspServiceHandle = CreateService(SCMHandle, TEXT("InspIRCd"),TEXT("InspIRCd Daemon"), SERVICE_CHANGE_CONFIG, SERVICE_WIN32_OWN_PROCESS,
183 SERVICE_AUTO_START, SERVICE_ERROR_NORMAL, tszBinaryPath, 0, 0, 0, TEXT("NT AUTHORITY\\NetworkService"), NULL);
185 if (!InspServiceHandle)
187 throw CWin32Exception();
190 TCHAR tszDescription[] = TEXT("The InspIRCd service hosts IRC channels and conversations. If this service is stopped, the IRC server will be unavailable.");
191 SERVICE_DESCRIPTION svDescription = { tszDescription };
192 if(!ChangeServiceConfig2(InspServiceHandle, SERVICE_CONFIG_DESCRIPTION, &svDescription))
194 throw CWin32Exception();
197 CloseServiceHandle(InspServiceHandle);
198 CloseServiceHandle(SCMHandle);
199 std::cout << "Service installed." << std::endl;
201 catch(CWin32Exception e)
203 if(InspServiceHandle)
204 CloseServiceHandle(InspServiceHandle);
207 CloseServiceHandle(SCMHandle);
209 std::cout << "Service installation failed: " << e.what() << std::endl;
213 /** Remove the windows service. This requires administrator privileges. */
214 void UninstallService()
216 SC_HANDLE InspServiceHandle = 0, SCMHandle = 0;
220 SCMHandle = OpenSCManager(NULL, SERVICES_ACTIVE_DATABASE, DELETE);
222 throw CWin32Exception();
224 InspServiceHandle = OpenService(SCMHandle, TEXT("InspIRCd"), DELETE);
225 if (!InspServiceHandle)
226 throw CWin32Exception();
228 if (!DeleteService(InspServiceHandle) && GetLastError() != ERROR_SERVICE_MARKED_FOR_DELETE)
230 throw CWin32Exception();
233 CloseServiceHandle(InspServiceHandle);
234 CloseServiceHandle(SCMHandle);
235 std::cout << "Service removed." << std::endl;
237 catch(CWin32Exception e)
239 if(InspServiceHandle)
240 CloseServiceHandle(InspServiceHandle);
243 CloseServiceHandle(SCMHandle);
245 std::cout << "Service deletion failed: " << e.what() << std::endl;
249 /* In windows, our main() flows through here, before calling the 'real' main, smain() in inspircd.cpp */
250 int main(int argc, char* argv[])
252 /* Check for parameters */
255 for (int i = 1; i < argc; i++)
257 if(!_stricmp(argv[i], "--installservice"))
262 if(!_stricmp(argv[i], "--uninstallservice") || !_stricmp(argv[i], "--removeservice"))
270 SERVICE_TABLE_ENTRY serviceTable[] =
272 { TEXT("InspIRCd"), (LPSERVICE_MAIN_FUNCTION)ServiceMain },
276 g_bRunningAsService = true;
277 if( !StartServiceCtrlDispatcher(serviceTable) )
279 // This error means that the program was not started as service.
280 if( GetLastError() == ERROR_FAILED_SERVICE_CONTROLLER_CONNECT )
282 g_bRunningAsService = false;
283 return smain(argc, argv);
287 return EXIT_STATUS_SERVICE;