2 * InspIRCd -- Internet Relay Chat Daemon
4 * Copyright (C) 2008 Craig Edwards <craigedwards@brainbox.cc>
6 * This file is part of InspIRCd. InspIRCd is free software: you can
7 * redistribute it and/or modify it under the terms of the GNU General Public
8 * License as published by the Free Software Foundation, version 2.
10 * This program is distributed in the hope that it will be useful, but WITHOUT
11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
12 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
22 #include "exitcodes.h"
29 static SERVICE_STATUS_HANDLE g_ServiceStatusHandle;
30 static SERVICE_STATUS g_ServiceStatus;
31 static bool g_bRunningAsService;
38 static Service_Data g_ServiceData;
40 /** The main part of inspircd runs within this thread function. This allows the service part to run
41 * seperately on its own and to be able to kill the worker thread when its time to quit.
43 DWORD WINAPI WorkerThread(LPVOID param)
45 smain(g_ServiceData.argc, g_ServiceData.argv);
49 /* This is called when all startup is done */
50 void SetServiceRunning()
52 if (!g_bRunningAsService)
55 g_ServiceStatus.dwCurrentState = SERVICE_RUNNING;
56 g_ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
58 if( !SetServiceStatus( g_ServiceStatusHandle, &g_ServiceStatus ) )
59 throw CWin32Exception();
62 /* In windows we hook this to InspIRCd::Exit() */
63 void SetServiceStopped(DWORD dwStatus)
65 if (!g_bRunningAsService)
68 g_ServiceStatus.dwCurrentState = SERVICE_STOPPED;
69 if(dwStatus != EXIT_STATUS_NOERROR)
71 g_ServiceStatus.dwServiceSpecificExitCode = dwStatus;
72 g_ServiceStatus.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;
76 g_ServiceStatus.dwWin32ExitCode = ERROR_SUCCESS;
78 SetServiceStatus( g_ServiceStatusHandle, &g_ServiceStatus );
81 /** This callback is called by windows when the state of the service has been changed */
82 VOID ServiceCtrlHandler(DWORD controlCode)
86 case SERVICE_CONTROL_SHUTDOWN:
87 case SERVICE_CONTROL_STOP:
88 g_ServiceStatus.dwCurrentState = SERVICE_STOPPED;
89 SetServiceStatus( g_ServiceStatusHandle, &g_ServiceStatus );
94 /** This callback is called by windows when the service is started */
95 VOID ServiceMain(DWORD argc, LPCSTR *argv)
97 g_ServiceStatusHandle = RegisterServiceCtrlHandler(TEXT("InspIRCd"), (LPHANDLER_FUNCTION)ServiceCtrlHandler);
98 if( !g_ServiceStatusHandle )
101 g_ServiceStatus.dwCheckPoint = 1;
102 g_ServiceStatus.dwControlsAccepted = 0;
103 g_ServiceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
104 g_ServiceStatus.dwWaitHint = 5000;
105 g_ServiceStatus.dwWin32ExitCode = NO_ERROR;
106 g_ServiceStatus.dwCurrentState = SERVICE_START_PENDING;
108 if( !SetServiceStatus( g_ServiceStatusHandle, &g_ServiceStatus ) )
111 char szModuleName[MAX_PATH];
112 if(GetModuleFileNameA(NULL, szModuleName, MAX_PATH))
117 g_ServiceData.argc = argc;
119 // Note: since this memory is going to stay allocated for the rest of the execution,
120 // it doesn't make sense to free it, as it's going to be "freed" on process termination
122 g_ServiceData.argv = new char*[argc];
124 uint32_t allocsize = strnlen_s(szModuleName, MAX_PATH) + 1;
125 g_ServiceData.argv[0] = new char[allocsize];
126 strcpy_s(g_ServiceData.argv[0], allocsize, szModuleName);
128 for(uint32_t i = 1; i < argc; i++)
130 allocsize = strnlen_s(argv[i], MAX_PATH) + 1;
131 g_ServiceData.argv[i] = new char[allocsize];
132 strcpy_s(g_ServiceData.argv[i], allocsize, argv[i]);
135 *(strrchr(szModuleName, '\\') + 1) = NULL;
136 SetCurrentDirectoryA(szModuleName);
138 HANDLE hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)WorkerThread, NULL, 0, NULL);
141 WaitForSingleObject(hThread, INFINITE);
142 CloseHandle(hThread);
147 g_ServiceStatus.dwCurrentState = SERVICE_STOPPED;
148 g_ServiceStatus.dwWin32ExitCode = ERROR_OUTOFMEMORY;
149 SetServiceStatus( g_ServiceStatusHandle, &g_ServiceStatus );
152 if(g_ServiceStatus.dwCurrentState == SERVICE_STOPPED)
155 g_ServiceStatus.dwCurrentState = SERVICE_STOPPED;
156 g_ServiceStatus.dwWin32ExitCode = GetLastError();
157 SetServiceStatus( g_ServiceStatusHandle, &g_ServiceStatus );
160 /** Install the windows service. This requires administrator privileges. */
161 void InstallService()
163 SC_HANDLE InspServiceHandle = 0, SCMHandle = 0;
166 TCHAR tszBinaryPath[MAX_PATH];
167 if(!GetModuleFileName(NULL, tszBinaryPath, _countof(tszBinaryPath)))
169 throw CWin32Exception();
172 SCMHandle = OpenSCManager(0, 0, SC_MANAGER_ALL_ACCESS);
175 throw CWin32Exception();
178 InspServiceHandle = CreateService(SCMHandle, TEXT("InspIRCd"),TEXT("InspIRCd Daemon"), SERVICE_CHANGE_CONFIG, SERVICE_WIN32_OWN_PROCESS,
179 SERVICE_AUTO_START, SERVICE_ERROR_NORMAL, tszBinaryPath, 0, 0, 0, TEXT("NT AUTHORITY\\NetworkService"), NULL);
181 if (!InspServiceHandle)
183 throw CWin32Exception();
186 TCHAR tszDescription[] = TEXT("The InspIRCd service hosts IRC channels and conversations. If this service is stopped, the IRC server will be unavailable.");
187 SERVICE_DESCRIPTION svDescription = { tszDescription };
188 if(!ChangeServiceConfig2(InspServiceHandle, SERVICE_CONFIG_DESCRIPTION, &svDescription))
190 throw CWin32Exception();
193 CloseServiceHandle(InspServiceHandle);
194 CloseServiceHandle(SCMHandle);
195 std::cout << "Service installed." << std::endl;
197 catch(CWin32Exception e)
199 if(InspServiceHandle)
200 CloseServiceHandle(InspServiceHandle);
203 CloseServiceHandle(SCMHandle);
205 std::cout << "Service installation failed: " << e.what() << std::endl;
209 /** Remove the windows service. This requires administrator privileges. */
210 void UninstallService()
212 SC_HANDLE InspServiceHandle = 0, SCMHandle = 0;
216 SCMHandle = OpenSCManager(NULL, SERVICES_ACTIVE_DATABASE, DELETE);
218 throw CWin32Exception();
220 InspServiceHandle = OpenService(SCMHandle, TEXT("InspIRCd"), DELETE);
221 if (!InspServiceHandle)
222 throw CWin32Exception();
224 if (!DeleteService(InspServiceHandle) && GetLastError() != ERROR_SERVICE_MARKED_FOR_DELETE)
226 throw CWin32Exception();
229 CloseServiceHandle(InspServiceHandle);
230 CloseServiceHandle(SCMHandle);
231 std::cout << "Service removed." << std::endl;
233 catch(CWin32Exception e)
235 if(InspServiceHandle)
236 CloseServiceHandle(InspServiceHandle);
239 CloseServiceHandle(SCMHandle);
241 std::cout << "Service deletion failed: " << e.what() << std::endl;
245 /* In windows, our main() flows through here, before calling the 'real' main, smain() in inspircd.cpp */
246 int main(int argc, char* argv[])
248 /* Check for parameters */
251 for (int i = 1; i < argc; i++)
253 if(!_stricmp(argv[i], "--installservice"))
258 if(!_stricmp(argv[i], "--uninstallservice") || !_stricmp(argv[i], "--removeservice"))
266 SERVICE_TABLE_ENTRY serviceTable[] =
268 { TEXT("InspIRCd"), (LPSERVICE_MAIN_FUNCTION)ServiceMain },
272 g_bRunningAsService = true;
273 if( !StartServiceCtrlDispatcher(serviceTable) )
275 // This error means that the program was not started as service.
276 if( GetLastError() == ERROR_FAILED_SERVICE_CONTROLLER_CONNECT )
278 g_bRunningAsService = false;
279 return smain(argc, argv);
283 return EXIT_STATUS_SERVICE;