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/>.
20 #include "inspircd_config.h"
22 #include "exitcodes.h"
28 static SERVICE_STATUS_HANDLE serviceStatusHandle;
29 static HANDLE hThreadEvent;
30 static HANDLE killServiceEvent;
31 static int serviceCurrentStatus;
33 /** This is used to define ChangeServiceConf2() as we can't link
34 * directly against this symbol (see below where it is used)
36 typedef BOOL (CALLBACK* SETSERVDESC)(SC_HANDLE,DWORD,LPVOID);
38 BOOL UpdateSCMStatus(DWORD dwCurrentState, DWORD dwWin32ExitCode, DWORD dwServiceSpecificExitCode, DWORD dwCheckPoint, DWORD dwWaitHint);
39 void terminateService(int code, int wincode);
41 /* A commandline parameter handler for service specific commandline parameters */
42 typedef void (*CommandlineParameterHandler)(void);
44 /* Represents a commandline and its handler */
48 CommandlineParameterHandler Handler;
51 /* A function pointer for dynamic linking tricks */
52 SETSERVDESC ChangeServiceConf;
54 /* Returns true if this program is running as a service, false if it is running interactive */
57 USEROBJECTFLAGS uoflags;
58 HWINSTA winstation = GetProcessWindowStation();
59 if (GetUserObjectInformation(winstation, UOI_FLAGS, &uoflags, sizeof(uoflags), NULL))
60 return ((uoflags.dwFlags & WSF_VISIBLE) == 0);
65 /* Kills the service by setting an event which the other thread picks up and exits */
68 SetEvent(hThreadEvent);
70 SetEvent(killServiceEvent);
73 /** The main part of inspircd runs within this thread function. This allows the service part to run
74 * seperately on its own and to be able to kill the worker thread when its time to quit.
76 DWORD WINAPI WorkerThread(LPDWORD param)
78 char modname[MAX_PATH];
79 GetModuleFileName(NULL, modname, sizeof(modname));
80 char* argv[] = { modname, "--nofork" };
86 /* This is called when all startup is done */
87 void SetServiceRunning()
92 serviceCurrentStatus = SERVICE_RUNNING;
93 BOOL success = UpdateSCMStatus(SERVICE_RUNNING, NO_ERROR, 0, 0, 0);
96 terminateService(EXIT_STATUS_UPDATESCM_FAILED, GetLastError());
102 /** Starts the worker thread above */
103 void StartServiceThread()
106 CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)WorkerThread,NULL,0,&dwd);
109 /** This function updates the status of the service in the SCM
110 * (service control manager, the services.msc applet)
112 BOOL UpdateSCMStatus(DWORD dwCurrentState, DWORD dwWin32ExitCode, DWORD dwServiceSpecificExitCode, DWORD dwCheckPoint, DWORD dwWaitHint)
115 SERVICE_STATUS serviceStatus;
116 serviceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
117 serviceStatus.dwCurrentState = dwCurrentState;
119 if (dwCurrentState == SERVICE_START_PENDING)
121 serviceStatus.dwControlsAccepted = 0;
125 serviceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
128 if (dwServiceSpecificExitCode == 0)
130 serviceStatus.dwWin32ExitCode = dwWin32ExitCode;
134 serviceStatus.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;
136 serviceStatus.dwServiceSpecificExitCode = dwServiceSpecificExitCode;
137 serviceStatus.dwCheckPoint = dwCheckPoint;
138 serviceStatus.dwWaitHint = dwWaitHint;
140 success = SetServiceStatus (serviceStatusHandle, &serviceStatus);
148 /** This function is called by us when the service is being shut down or when it can't be started */
149 void terminateService(int code, int wincode)
151 UpdateSCMStatus(SERVICE_STOPPED, wincode ? wincode : ERROR_SERVICE_SPECIFIC_ERROR, wincode ? 0 : code, 0, 0);
155 /* In windows we hook this to InspIRCd::Exit() */
156 void SetServiceStopped(int status)
161 /* Are we running as a service? If so, trigger the service specific exit code */
162 terminateService(status, 0);
167 /** This callback is called by windows when the state of the service has been changed */
168 VOID ServiceCtrlHandler(DWORD controlCode)
172 case SERVICE_CONTROL_INTERROGATE:
174 case SERVICE_CONTROL_SHUTDOWN:
175 case SERVICE_CONTROL_STOP:
176 serviceCurrentStatus = SERVICE_STOP_PENDING;
177 UpdateSCMStatus(SERVICE_STOP_PENDING, NO_ERROR, 0, 1, 5000);
179 UpdateSCMStatus(SERVICE_STOPPED, NO_ERROR, 0, 0, 0);
184 UpdateSCMStatus(serviceCurrentStatus, NO_ERROR, 0, 0, 0);
187 /** This callback is called by windows when the service is started */
188 VOID ServiceMain(DWORD argc, LPTSTR *argv)
192 serviceStatusHandle = RegisterServiceCtrlHandler("InspIRCd", (LPHANDLER_FUNCTION)ServiceCtrlHandler);
193 if (!serviceStatusHandle)
195 terminateService(EXIT_STATUS_RSCH_FAILED, GetLastError());
199 success = UpdateSCMStatus(SERVICE_START_PENDING, NO_ERROR, 0, 1, 1000);
202 terminateService(EXIT_STATUS_UPDATESCM_FAILED, GetLastError());
206 killServiceEvent = CreateEvent(NULL, true, false, NULL);
207 hThreadEvent = CreateEvent(NULL, true, false, NULL);
209 if (!killServiceEvent || !hThreadEvent)
211 terminateService(EXIT_STATUS_CREATE_EVENT_FAILED, GetLastError());
215 success = UpdateSCMStatus(SERVICE_START_PENDING, NO_ERROR, 0, 2, 1000);
218 terminateService(EXIT_STATUS_UPDATESCM_FAILED, GetLastError());
222 StartServiceThread();
223 WaitForSingleObject (killServiceEvent, INFINITE);
226 /** Install the windows service. This requires administrator privileges. */
227 void InstallService()
229 SC_HANDLE myService, scm;
230 SERVICE_DESCRIPTION svDesc;
233 char modname[MAX_PATH];
234 GetModuleFileName(NULL, modname, sizeof(modname));
236 scm = OpenSCManager(0,0,SC_MANAGER_CREATE_SERVICE);
239 printf("Unable to open service control manager: %s\n", dlerror());
243 myService = CreateService(scm,"InspIRCd","Inspire IRC Daemon", SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS,
244 SERVICE_AUTO_START, SERVICE_ERROR_NORMAL, modname, 0, 0, 0, NULL, NULL);
248 printf("Unable to create service: %s\n", dlerror());
249 CloseServiceHandle(scm);
253 // *** Set service description ***
254 // this is supported from 5.0 (win2k) onwards only, so we can't link to the definition of
255 // this function in advapi32.lib, otherwise the program will not run on windows NT 4. We
256 // must use LoadLibrary and GetProcAddress to export the function name from advapi32.dll
257 advapi32 = LoadLibrary("advapi32.dll");
260 ChangeServiceConf = (SETSERVDESC)GetProcAddress(advapi32,"ChangeServiceConfig2A");
261 if (ChangeServiceConf)
263 char desc[] = "The Inspire Internet Relay Chat Daemon hosts IRC channels and conversations.\
264 If this service is stopped, the IRC server will not run.";
265 svDesc.lpDescription = desc;
266 BOOL success = ChangeServiceConf(myService,SERVICE_CONFIG_DESCRIPTION, &svDesc);
269 printf("Unable to set service description: %s\n", dlerror());
270 CloseServiceHandle(myService);
271 CloseServiceHandle(scm);
275 FreeLibrary(advapi32);
278 printf("Service installed.\n");
279 CloseServiceHandle(myService);
280 CloseServiceHandle(scm);
283 /** Remove the windows service. This requires administrator privileges. */
286 SC_HANDLE myService, scm;
288 scm = OpenSCManager(0,0,SC_MANAGER_CREATE_SERVICE);
291 printf("Unable to open service control manager: %s\n", dlerror());
295 myService = OpenService(scm,"InspIRCd",SERVICE_ALL_ACCESS);
298 printf("Unable to open service: %s\n", dlerror());
299 CloseServiceHandle(scm);
303 if (!DeleteService(myService))
305 printf("Unable to delete service: %s\n", dlerror());
306 CloseServiceHandle(myService);
307 CloseServiceHandle(scm);
311 printf("Service removed.\n");
312 CloseServiceHandle(myService);
313 CloseServiceHandle(scm);
316 /* In windows, our main() flows through here, before calling the 'real' main, smain() in inspircd.cpp */
317 int main(int argc, char** argv)
319 /* List of parameters and handlers */
320 Commandline params[] = {
321 { "--installservice", InstallService },
322 { "--removeservice", RemoveService },
326 /* Check for parameters */
329 for (int z = 0; params[z].Switch; ++z)
331 if (!_stricmp(argv[1], params[z].Switch))
339 /* First, check if the service is installed.
340 * if it is not, or we're starting as non-administrator,
341 * just call smain() and start as normal non-service
344 SC_HANDLE myService, scm;
345 scm = OpenSCManager(0,0,SC_MANAGER_CREATE_SERVICE);
348 myService = OpenService(scm,"InspIRCd",SERVICE_ALL_ACCESS);
351 /* Service not installed or no permission to modify it */
352 CloseServiceHandle(scm);
353 return smain(argc, argv);
358 /* Not enough privileges to open the SCM */
359 return smain(argc, argv);
362 CloseServiceHandle(myService);
363 CloseServiceHandle(scm);
365 /* Check if the process is running interactively. InspIRCd does not run interactively
366 * as a service so if this is true, we just run the non-service inspircd.
369 return smain(argc, argv);
371 /* If we get here, we know the service is installed so we can start it */
373 SERVICE_TABLE_ENTRY serviceTable[] =
375 {"InspIRCd", (LPSERVICE_MAIN_FUNCTION) ServiceMain },
379 StartServiceCtrlDispatcher(serviceTable);