1 /* +------------------------------------+
2 * | Inspire Internet Relay Chat Daemon |
3 * +------------------------------------+
5 * InspIRCd: (C) 2002-2008 InspIRCd Development Team
6 * See: http://www.inspircd.org/wiki/index.php/Credits
8 * This program is free but copyrighted software; see
9 * the file COPYING for details.
11 * ---------------------------------------------------
13 #include "inspircd_config.h"
20 static SERVICE_STATUS_HANDLE serviceStatusHandle;
21 static HANDLE hThreadEvent;
22 static HANDLE killServiceEvent;
23 static int serviceCurrentStatus;
25 /** This is used to define ChangeServiceConf2() as we can't link
26 * directly against this symbol (see below where it is used)
28 typedef BOOL (CALLBACK* SETSERVDESC)(SC_HANDLE,DWORD,LPVOID);
30 BOOL UpdateSCMStatus (DWORD dwCurrentState, DWORD dwWin32ExitCode, DWORD dwServiceSpecificExitCode, DWORD dwCheckPoint, DWORD dwWaitHint);
31 void terminateService (int code, int wincode);
33 /* A commandline parameter handler for service specific commandline parameters */
34 typedef void (*CommandlineParameterHandler)(void);
36 /* Represents a commandline and its handler */
40 CommandlineParameterHandler Handler;
43 /* A function pointer for dynamic linking tricks */
44 SETSERVDESC ChangeServiceConf;
48 USEROBJECTFLAGS uoflags;
49 HWINSTA winstation = GetProcessWindowStation();
50 if (GetUserObjectInformation(winstation, UOI_FLAGS, &uoflags, sizeof(uoflags), NULL))
51 return ((uoflags.dwFlags & WSF_VISIBLE) == 0);
56 /* Kills the service by setting an event which the other thread picks up and exits */
59 SetEvent(hThreadEvent);
61 SetEvent(killServiceEvent);
64 /** The main part of inspircd runs within this thread function. This allows the service part to run
65 * seperately on its own and to be able to kill the worker thread when its time to quit.
67 DWORD WINAPI WorkerThread(LPDWORD param)
69 char modname[MAX_PATH];
70 GetModuleFileName(NULL, modname, sizeof(modname));
71 char* argv[] = { modname, "--nofork", "--debug" };
77 /* This is called when all startup is done */
78 void SetServiceRunning()
83 serviceCurrentStatus = SERVICE_RUNNING;
84 BOOL success = UpdateSCMStatus(SERVICE_RUNNING, NO_ERROR, 0, 0, 0);
87 terminateService(22, GetLastError());
93 /** Starts the worker thread above */
94 void StartServiceThread()
97 CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)WorkerThread,NULL,0,&dwd);
100 /** This function updates the status of the service in the SCM
101 * (service control manager, the services.msc applet)
103 BOOL UpdateSCMStatus (DWORD dwCurrentState, DWORD dwWin32ExitCode, DWORD dwServiceSpecificExitCode, DWORD dwCheckPoint, DWORD dwWaitHint)
106 SERVICE_STATUS serviceStatus;
107 serviceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
108 serviceStatus.dwCurrentState = dwCurrentState;
110 if (dwCurrentState == SERVICE_START_PENDING)
112 serviceStatus.dwControlsAccepted = 0;
116 serviceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
119 if (dwServiceSpecificExitCode == 0)
121 serviceStatus.dwWin32ExitCode = dwWin32ExitCode;
125 serviceStatus.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;
127 serviceStatus.dwServiceSpecificExitCode = dwServiceSpecificExitCode;
128 serviceStatus.dwCheckPoint = dwCheckPoint;
129 serviceStatus.dwWaitHint = dwWaitHint;
131 success = SetServiceStatus (serviceStatusHandle, &serviceStatus);
139 /** This function is called by us when the service is being shut down or when it can't be started */
140 void terminateService (int code, int wincode)
142 UpdateSCMStatus(SERVICE_STOPPED, wincode ? wincode : ERROR_SERVICE_SPECIFIC_ERROR, wincode ? 0 : code, 0, 0);
146 /* In windows we hook this to exit() */
147 void SetServiceStopped(int status)
152 /* Are we running as a service? If so, trigger the service specific exit code */
153 terminateService(status, 0);
158 /** This callback is called by windows when the state of the service has been changed */
159 VOID ServiceCtrlHandler (DWORD controlCode)
163 case SERVICE_CONTROL_INTERROGATE:
165 case SERVICE_CONTROL_SHUTDOWN:
166 case SERVICE_CONTROL_STOP:
167 serviceCurrentStatus = SERVICE_STOP_PENDING;
168 UpdateSCMStatus(SERVICE_STOP_PENDING, NO_ERROR, 0, 1, 5000);
170 UpdateSCMStatus(SERVICE_STOPPED, NO_ERROR, 0, 0, 0);
175 UpdateSCMStatus(serviceCurrentStatus, NO_ERROR, 0, 0, 0);
178 /** This callback is called by windows when the service is started */
179 VOID ServiceMain(DWORD argc, LPTSTR *argv)
182 DWORD type=0, size=0;
184 serviceStatusHandle = RegisterServiceCtrlHandler("InspIRCd", (LPHANDLER_FUNCTION)ServiceCtrlHandler);
185 if (!serviceStatusHandle)
187 terminateService(18, GetLastError());
191 success = UpdateSCMStatus(SERVICE_START_PENDING, NO_ERROR, 0, 1, 1000);
194 terminateService(19, GetLastError());
198 killServiceEvent = CreateEvent(NULL, true, false, NULL);
199 hThreadEvent = CreateEvent(NULL, true, false, NULL);
201 if (!killServiceEvent || !hThreadEvent)
203 terminateService(20, GetLastError());
207 success = UpdateSCMStatus(SERVICE_START_PENDING, NO_ERROR, 0, 2, 1000);
210 terminateService(21, GetLastError());
214 StartServiceThread();
215 WaitForSingleObject (killServiceEvent, INFINITE);
218 /** Install the windows service. This requires administrator privileges. */
219 void InstallService()
221 SC_HANDLE myService, scm;
222 SERVICE_DESCRIPTION svDesc;
225 char modname[MAX_PATH];
226 GetModuleFileName(NULL, modname, sizeof(modname));
228 scm = OpenSCManager(0,0,SC_MANAGER_CREATE_SERVICE);
231 printf("Unable to open service control manager: %s\n", dlerror());
235 myService = CreateService(scm,"InspIRCd","Inspire IRC Daemon", SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS,
236 SERVICE_AUTO_START, SERVICE_ERROR_NORMAL, modname, 0, 0, 0, NULL, NULL);
240 printf("Unable to create service: %s\n", dlerror());
241 CloseServiceHandle(scm);
245 // *** Set service description ***
246 // this is supported from 5.0 (win2k) onwards only, so we can't link to the definition of
247 // this function in advapi32.lib, otherwise the program will not run on windows NT 4. We
248 // must use LoadLibrary and GetProcAddress to export the function name from advapi32.dll
249 advapi32 = LoadLibrary("advapi32.dll");
252 ChangeServiceConf = (SETSERVDESC)GetProcAddress(advapi32,"ChangeServiceConfig2A");
253 if (ChangeServiceConf)
255 char desc[] = "The Inspire Internet Relay Chat Daemon hosts IRC channels and conversations.\
256 If this service is stopped, the IRC server will not run.";
257 svDesc.lpDescription = desc;
258 BOOL success = ChangeServiceConf(myService,SERVICE_CONFIG_DESCRIPTION, &svDesc);
261 printf("Unable to set service description: %s\n", dlerror());
262 CloseServiceHandle(myService);
263 CloseServiceHandle(scm);
267 FreeLibrary(advapi32);
270 printf("Service installed.\n");
271 CloseServiceHandle(myService);
272 CloseServiceHandle(scm);
275 /** Remove the windows service. This requires administrator privileges. */
278 SC_HANDLE myService, scm;
280 scm = OpenSCManager(0,0,SC_MANAGER_CREATE_SERVICE);
283 printf("Unable to open service control manager: %s\n", dlerror());
287 myService = OpenService(scm,"InspIRCd",SERVICE_ALL_ACCESS);
290 printf("Unable to open service: %s\n", dlerror());
291 CloseServiceHandle(scm);
295 if (!DeleteService(myService))
297 printf("Unable to delete service: %s\n", dlerror());
298 CloseServiceHandle(myService);
299 CloseServiceHandle(scm);
303 printf("Service removed.\n");
304 CloseServiceHandle(myService);
305 CloseServiceHandle(scm);
308 /* In windows, our main() flows through here, before calling the 'real' main, smain() in inspircd.cpp */
309 int main(int argc, char** argv)
311 /* List of parameters and handlers */
312 Commandline params[] = {
313 { "--installservice", InstallService },
314 { "--removeservice", RemoveService },
318 /* Check for parameters */
321 for (int z = 0; params[z].Switch; ++z)
323 if (!_stricmp(argv[1], params[z].Switch))
331 /* First, check if the service is installed.
332 * if it is not, or we're starting as non-administrator,
333 * just call smain() and start as normal non-service
336 SC_HANDLE myService, scm;
337 scm = OpenSCManager(0,0,SC_MANAGER_CREATE_SERVICE);
340 myService = OpenService(scm,"InspIRCd",SERVICE_ALL_ACCESS);
343 /* Service not installed or no permission to modify it */
344 CloseServiceHandle(scm);
345 return smain(argc, argv);
350 /* Not enough privileges to open the SCM */
351 return smain(argc, argv);
354 CloseServiceHandle(myService);
355 CloseServiceHandle(scm);
357 /* Check if the process is running interactively. InspIRCd does not run interactively
358 * as a service so if this is true, we just run the non-service inspircd.
361 return smain(argc, argv);
363 /* If we get here, we know the service is installed so we can start it */
365 SERVICE_TABLE_ENTRY serviceTable[] =
367 {"InspIRCd", (LPSERVICE_MAIN_FUNCTION) ServiceMain },
371 StartServiceCtrlDispatcher(serviceTable);