1 /* +------------------------------------+
2 * | Inspire Internet Relay Chat Daemon |
3 * +------------------------------------+
5 * InspIRCd: (C) 2002-2009 InspIRCd Development Team
6 * See: http://wiki.inspircd.org/Credits
8 * This program is free but copyrighted software; see
9 * the file COPYING for details.
11 * ---------------------------------------------------
13 #include "inspircd_config.h"
15 #include "exitcodes.h"
21 static SERVICE_STATUS_HANDLE serviceStatusHandle;
22 static HANDLE hThreadEvent;
23 static HANDLE killServiceEvent;
24 static int serviceCurrentStatus;
26 /** This is used to define ChangeServiceConf2() as we can't link
27 * directly against this symbol (see below where it is used)
29 typedef BOOL (CALLBACK* SETSERVDESC)(SC_HANDLE,DWORD,LPVOID);
31 BOOL UpdateSCMStatus(DWORD dwCurrentState, DWORD dwWin32ExitCode, DWORD dwServiceSpecificExitCode, DWORD dwCheckPoint, DWORD dwWaitHint);
32 void terminateService(int code, int wincode);
34 /* A commandline parameter handler for service specific commandline parameters */
35 typedef void (*CommandlineParameterHandler)(void);
37 /* Represents a commandline and its handler */
41 CommandlineParameterHandler Handler;
44 /* A function pointer for dynamic linking tricks */
45 SETSERVDESC ChangeServiceConf;
47 /* Returns true if this program is running as a service, false if it is running interactive */
50 USEROBJECTFLAGS uoflags;
51 HWINSTA winstation = GetProcessWindowStation();
52 if (GetUserObjectInformation(winstation, UOI_FLAGS, &uoflags, sizeof(uoflags), NULL))
53 return ((uoflags.dwFlags & WSF_VISIBLE) == 0);
58 /* Kills the service by setting an event which the other thread picks up and exits */
61 SetEvent(hThreadEvent);
63 SetEvent(killServiceEvent);
66 /** The main part of inspircd runs within this thread function. This allows the service part to run
67 * seperately on its own and to be able to kill the worker thread when its time to quit.
69 DWORD WINAPI WorkerThread(LPDWORD param)
71 char modname[MAX_PATH];
72 GetModuleFileName(NULL, modname, sizeof(modname));
73 char* argv[] = { modname, "--nofork" };
79 /* This is called when all startup is done */
80 void SetServiceRunning()
85 serviceCurrentStatus = SERVICE_RUNNING;
86 BOOL success = UpdateSCMStatus(SERVICE_RUNNING, NO_ERROR, 0, 0, 0);
89 terminateService(EXIT_STATUS_UPDATESCM_FAILED, GetLastError());
95 /** Starts the worker thread above */
96 void StartServiceThread()
99 CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)WorkerThread,NULL,0,&dwd);
102 /** This function updates the status of the service in the SCM
103 * (service control manager, the services.msc applet)
105 BOOL UpdateSCMStatus(DWORD dwCurrentState, DWORD dwWin32ExitCode, DWORD dwServiceSpecificExitCode, DWORD dwCheckPoint, DWORD dwWaitHint)
108 SERVICE_STATUS serviceStatus;
109 serviceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
110 serviceStatus.dwCurrentState = dwCurrentState;
112 if (dwCurrentState == SERVICE_START_PENDING)
114 serviceStatus.dwControlsAccepted = 0;
118 serviceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
121 if (dwServiceSpecificExitCode == 0)
123 serviceStatus.dwWin32ExitCode = dwWin32ExitCode;
127 serviceStatus.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;
129 serviceStatus.dwServiceSpecificExitCode = dwServiceSpecificExitCode;
130 serviceStatus.dwCheckPoint = dwCheckPoint;
131 serviceStatus.dwWaitHint = dwWaitHint;
133 success = SetServiceStatus (serviceStatusHandle, &serviceStatus);
141 /** This function is called by us when the service is being shut down or when it can't be started */
142 void terminateService(int code, int wincode)
144 UpdateSCMStatus(SERVICE_STOPPED, wincode ? wincode : ERROR_SERVICE_SPECIFIC_ERROR, wincode ? 0 : code, 0, 0);
148 /* In windows we hook this to InspIRCd::Exit() */
149 void SetServiceStopped(int status)
154 /* Are we running as a service? If so, trigger the service specific exit code */
155 terminateService(status, 0);
160 /** This callback is called by windows when the state of the service has been changed */
161 VOID ServiceCtrlHandler(DWORD controlCode)
165 case SERVICE_CONTROL_INTERROGATE:
167 case SERVICE_CONTROL_SHUTDOWN:
168 case SERVICE_CONTROL_STOP:
169 serviceCurrentStatus = SERVICE_STOP_PENDING;
170 UpdateSCMStatus(SERVICE_STOP_PENDING, NO_ERROR, 0, 1, 5000);
172 UpdateSCMStatus(SERVICE_STOPPED, NO_ERROR, 0, 0, 0);
177 UpdateSCMStatus(serviceCurrentStatus, NO_ERROR, 0, 0, 0);
180 /** This callback is called by windows when the service is started */
181 VOID ServiceMain(DWORD argc, LPTSTR *argv)
184 DWORD type=0, size=0;
186 serviceStatusHandle = RegisterServiceCtrlHandler("InspIRCd", (LPHANDLER_FUNCTION)ServiceCtrlHandler);
187 if (!serviceStatusHandle)
189 terminateService(EXIT_STATUS_RSCH_FAILED, GetLastError());
193 success = UpdateSCMStatus(SERVICE_START_PENDING, NO_ERROR, 0, 1, 1000);
196 terminateService(EXIT_STATUS_UPDATESCM_FAILED, GetLastError());
200 killServiceEvent = CreateEvent(NULL, true, false, NULL);
201 hThreadEvent = CreateEvent(NULL, true, false, NULL);
203 if (!killServiceEvent || !hThreadEvent)
205 terminateService(EXIT_STATUS_CREATE_EVENT_FAILED, GetLastError());
209 success = UpdateSCMStatus(SERVICE_START_PENDING, NO_ERROR, 0, 2, 1000);
212 terminateService(EXIT_STATUS_UPDATESCM_FAILED, GetLastError());
216 StartServiceThread();
217 WaitForSingleObject (killServiceEvent, INFINITE);
220 /** Install the windows service. This requires administrator privileges. */
221 void InstallService()
223 SC_HANDLE myService, scm;
224 SERVICE_DESCRIPTION svDesc;
227 char modname[MAX_PATH];
228 GetModuleFileName(NULL, modname, sizeof(modname));
230 scm = OpenSCManager(0,0,SC_MANAGER_CREATE_SERVICE);
233 printf("Unable to open service control manager: %s\n", dlerror());
237 myService = CreateService(scm,"InspIRCd","Inspire IRC Daemon", SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS,
238 SERVICE_AUTO_START, SERVICE_ERROR_NORMAL, modname, 0, 0, 0, NULL, NULL);
242 printf("Unable to create service: %s\n", dlerror());
243 CloseServiceHandle(scm);
247 // *** Set service description ***
248 // this is supported from 5.0 (win2k) onwards only, so we can't link to the definition of
249 // this function in advapi32.lib, otherwise the program will not run on windows NT 4. We
250 // must use LoadLibrary and GetProcAddress to export the function name from advapi32.dll
251 advapi32 = LoadLibrary("advapi32.dll");
254 ChangeServiceConf = (SETSERVDESC)GetProcAddress(advapi32,"ChangeServiceConfig2A");
255 if (ChangeServiceConf)
257 char desc[] = "The Inspire Internet Relay Chat Daemon hosts IRC channels and conversations.\
258 If this service is stopped, the IRC server will not run.";
259 svDesc.lpDescription = desc;
260 BOOL success = ChangeServiceConf(myService,SERVICE_CONFIG_DESCRIPTION, &svDesc);
263 printf("Unable to set service description: %s\n", dlerror());
264 CloseServiceHandle(myService);
265 CloseServiceHandle(scm);
269 FreeLibrary(advapi32);
272 printf("Service installed.\n");
273 CloseServiceHandle(myService);
274 CloseServiceHandle(scm);
277 /** Remove the windows service. This requires administrator privileges. */
280 SC_HANDLE myService, scm;
282 scm = OpenSCManager(0,0,SC_MANAGER_CREATE_SERVICE);
285 printf("Unable to open service control manager: %s\n", dlerror());
289 myService = OpenService(scm,"InspIRCd",SERVICE_ALL_ACCESS);
292 printf("Unable to open service: %s\n", dlerror());
293 CloseServiceHandle(scm);
297 if (!DeleteService(myService))
299 printf("Unable to delete service: %s\n", dlerror());
300 CloseServiceHandle(myService);
301 CloseServiceHandle(scm);
305 printf("Service removed.\n");
306 CloseServiceHandle(myService);
307 CloseServiceHandle(scm);
310 /* In windows, our main() flows through here, before calling the 'real' main, smain() in inspircd.cpp */
311 int main(int argc, char** argv)
313 /* List of parameters and handlers */
314 Commandline params[] = {
315 { "--installservice", InstallService },
316 { "--removeservice", RemoveService },
320 /* Check for parameters */
323 for (int z = 0; params[z].Switch; ++z)
325 if (!_stricmp(argv[1], params[z].Switch))
333 /* First, check if the service is installed.
334 * if it is not, or we're starting as non-administrator,
335 * just call smain() and start as normal non-service
338 SC_HANDLE myService, scm;
339 scm = OpenSCManager(0,0,SC_MANAGER_CREATE_SERVICE);
342 myService = OpenService(scm,"InspIRCd",SERVICE_ALL_ACCESS);
345 /* Service not installed or no permission to modify it */
346 CloseServiceHandle(scm);
347 return smain(argc, argv);
352 /* Not enough privileges to open the SCM */
353 return smain(argc, argv);
356 CloseServiceHandle(myService);
357 CloseServiceHandle(scm);
359 /* Check if the process is running interactively. InspIRCd does not run interactively
360 * as a service so if this is true, we just run the non-service inspircd.
363 return smain(argc, argv);
365 /* If we get here, we know the service is installed so we can start it */
367 SERVICE_TABLE_ENTRY serviceTable[] =
369 {"InspIRCd", (LPSERVICE_MAIN_FUNCTION) ServiceMain },
373 StartServiceCtrlDispatcher(serviceTable);