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"
\r
14 #include "inspircd.h"
\r
15 #include <windows.h>
\r
20 static SERVICE_STATUS_HANDLE serviceStatusHandle;
\r
21 static HANDLE hThreadEvent;
\r
22 static HANDLE killServiceEvent;
\r
23 static int serviceCurrentStatus;
\r
25 /** This is used to define ChangeServiceConf2() as we can't link
\r
26 * directly against this symbol (see below where it is used)
\r
28 typedef BOOL (CALLBACK* SETSERVDESC)(SC_HANDLE,DWORD,LPVOID);
\r
30 /* A commandline parameter handler for service specific commandline parameters */
\r
31 typedef void (*CommandlineParameterHandler)(void);
\r
33 /* Represents a commandline and its handler */
\r
37 CommandlineParameterHandler Handler;
\r
40 /* A function pointer for dynamic linking tricks */
\r
41 SETSERVDESC ChangeServiceConf;
\r
43 /* Kills the service by setting an event which the other thread picks up and exits */
\r
46 SetEvent(hThreadEvent);
\r
48 SetEvent(killServiceEvent);
\r
51 /** The main part of inspircd runs within this thread function. This allows the service part to run
\r
52 * seperately on its own and to be able to kill the worker thread when its time to quit.
\r
54 DWORD WINAPI WorkerThread(LPDWORD param)
\r
56 // *** REAL MAIN HERE ***
\r
57 char modname[MAX_PATH];
\r
58 GetModuleFileName(NULL, modname, sizeof(modname));
\r
59 char* argv[] = { modname, "--nofork", "--debug" };
\r
65 /** Starts the worker thread above */
\r
66 void StartServiceThread()
\r
69 CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)WorkerThread,NULL,0,&dwd);
\r
72 /** This function updates the status of the service in the SCM
\r
73 * (service control manager, the services.msc applet)
\r
75 BOOL UpdateSCMStatus (DWORD dwCurrentState, DWORD dwWin32ExitCode, DWORD dwServiceSpecificExitCode, DWORD dwCheckPoint, DWORD dwWaitHint)
\r
78 SERVICE_STATUS serviceStatus;
\r
79 serviceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
\r
80 serviceStatus.dwCurrentState = dwCurrentState;
\r
82 if (dwCurrentState == SERVICE_START_PENDING)
\r
84 serviceStatus.dwControlsAccepted = 0;
\r
88 serviceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
\r
91 if (dwServiceSpecificExitCode == 0)
\r
93 serviceStatus.dwWin32ExitCode = dwWin32ExitCode;
\r
97 serviceStatus.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;
\r
99 serviceStatus.dwServiceSpecificExitCode = dwServiceSpecificExitCode;
\r
100 serviceStatus.dwCheckPoint = dwCheckPoint;
\r
101 serviceStatus.dwWaitHint = dwWaitHint;
\r
103 success = SetServiceStatus (serviceStatusHandle, &serviceStatus);
\r
111 /** This function is called by us when the service is being shut down or when it can't be started */
\r
112 void terminateService (int code, int wincode)
\r
114 UpdateSCMStatus(SERVICE_STOPPED,wincode?wincode:ERROR_SERVICE_SPECIFIC_ERROR,(wincode)?0:code,0,0);
\r
118 /** This callback is called by windows when the state of the service has been changed */
\r
119 VOID ServiceCtrlHandler (DWORD controlCode)
\r
121 switch(controlCode)
\r
123 case SERVICE_CONTROL_INTERROGATE:
\r
125 case SERVICE_CONTROL_SHUTDOWN:
\r
126 case SERVICE_CONTROL_STOP:
\r
127 serviceCurrentStatus = SERVICE_STOP_PENDING;
\r
128 UpdateSCMStatus(SERVICE_STOP_PENDING, NO_ERROR, 0, 1, 5000);
\r
130 UpdateSCMStatus(SERVICE_STOPPED, NO_ERROR, 0, 0, 0);
\r
135 UpdateSCMStatus(serviceCurrentStatus, NO_ERROR, 0, 0, 0);
\r
138 /** This callback is called by windows when the service is started */
\r
139 VOID ServiceMain(DWORD argc, LPTSTR *argv)
\r
142 DWORD type=0, size=0;
\r
144 serviceStatusHandle = RegisterServiceCtrlHandler("InspIRCd", (LPHANDLER_FUNCTION)ServiceCtrlHandler);
\r
145 if (!serviceStatusHandle)
\r
147 terminateService(1, GetLastError());
\r
151 success = UpdateSCMStatus(SERVICE_START_PENDING, NO_ERROR, 0, 1, 1000);
\r
154 terminateService(2, GetLastError());
\r
158 killServiceEvent = CreateEvent(NULL, true, false, NULL);
\r
159 hThreadEvent = CreateEvent(NULL, true, false, NULL);
\r
161 if (!killServiceEvent || !hThreadEvent)
\r
163 terminateService(99, GetLastError());
\r
167 success = UpdateSCMStatus(SERVICE_START_PENDING, NO_ERROR, 0, 2, 1000);
\r
170 terminateService(2, GetLastError());
\r
174 StartServiceThread();
\r
175 serviceCurrentStatus = SERVICE_RUNNING;
\r
176 success = UpdateSCMStatus(SERVICE_RUNNING, NO_ERROR, 0, 0, 0);
\r
179 terminateService(6, GetLastError());
\r
182 WaitForSingleObject (killServiceEvent, INFINITE);
\r
185 /** Install the windows service. This requires administrator privileges. */
\r
186 void InstallService()
\r
188 SC_HANDLE myService, scm;
\r
189 SERVICE_DESCRIPTION svDesc;
\r
190 HINSTANCE advapi32;
\r
192 char modname[MAX_PATH];
\r
193 GetModuleFileName(NULL, modname, sizeof(modname));
\r
195 scm = OpenSCManager(0,0,SC_MANAGER_CREATE_SERVICE);
\r
198 printf("Unable to open service control manager: %s\n", dlerror());
\r
202 myService = CreateService(scm,"InspIRCd","Inspire IRC Daemon", SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS,
\r
203 SERVICE_AUTO_START, SERVICE_ERROR_NORMAL, modname, 0, 0, 0, NULL, NULL);
\r
207 printf("Unable to create service: %s\n", dlerror());
\r
208 CloseServiceHandle(scm);
\r
212 // *** Set service description ***
\r
213 // this is supported from 5.0 (win2k) onwards only, so we can't link to the definition of
\r
214 // this function in advapi32.lib, otherwise the program will not run on windows NT 4. We
\r
215 // must use LoadLibrary and GetProcAddress to export the function name from advapi32.dll
\r
216 advapi32 = LoadLibrary("advapi32.dll");
\r
219 ChangeServiceConf = (SETSERVDESC)GetProcAddress(advapi32,"ChangeServiceConfig2A");
\r
220 if (ChangeServiceConf)
\r
222 char desc[] = "The Inspire Internet Relay Chat Daemon hosts IRC channels and conversations.\
\r
223 If this service is stopped, the IRC server will not run.";
\r
224 svDesc.lpDescription = desc;
\r
225 BOOL success = ChangeServiceConf(myService,SERVICE_CONFIG_DESCRIPTION, &svDesc);
\r
228 printf("Unable to set service description: %s\n", dlerror());
\r
229 CloseServiceHandle(myService);
\r
230 CloseServiceHandle(scm);
\r
234 FreeLibrary(advapi32);
\r
237 printf("Service installed.\n");
\r
238 CloseServiceHandle(myService);
\r
239 CloseServiceHandle(scm);
\r
242 /** Remove the windows service. This requires administrator privileges. */
\r
243 void RemoveService()
\r
245 SC_HANDLE myService, scm;
\r
247 scm = OpenSCManager(0,0,SC_MANAGER_CREATE_SERVICE);
\r
250 printf("Unable to open service control manager: %s\n", dlerror());
\r
254 myService = OpenService(scm,"InspIRCd",SERVICE_ALL_ACCESS);
\r
257 printf("Unable to open service: %s\n", dlerror());
\r
258 CloseServiceHandle(scm);
\r
262 if (!DeleteService(myService))
\r
264 printf("Unable to delete service: %s\n", dlerror());
\r
265 CloseServiceHandle(myService);
\r
266 CloseServiceHandle(scm);
\r
270 printf("Service removed.\n");
\r
271 CloseServiceHandle(myService);
\r
272 CloseServiceHandle(scm);
\r
275 /* In windows, our main() flows through here, before calling the 'real' main, smain() in inspircd.cpp */
\r
276 int main(int argc, char** argv)
\r
278 /* List of parameters and handlers */
\r
279 Commandline params[] = {
\r
280 { "--installservice", InstallService },
\r
281 { "--removeservice", RemoveService },
\r
285 /* Check for parameters */
\r
288 for (int z = 0; params[z].Switch; ++z)
\r
290 if (!_stricmp(argv[1], params[z].Switch))
\r
292 params[z].Handler();
\r
298 /* First, check if the service is installed.
\r
299 * if it is not, or we're starting as non-administrator,
\r
300 * just call smain() and start as normal non-service
\r
303 SC_HANDLE myService, scm;
\r
304 scm = OpenSCManager(0,0,SC_MANAGER_CREATE_SERVICE);
\r
307 myService = OpenService(scm,"InspIRCd",SERVICE_ALL_ACCESS);
\r
310 /* Service not installed or no permission to modify it */
\r
311 CloseServiceHandle(scm);
\r
312 return smain(argc, argv);
\r
317 /* Not enough privileges to open the SCM */
\r
318 return smain(argc, argv);
\r
321 CloseServiceHandle(myService);
\r
322 CloseServiceHandle(scm);
\r
324 /* Check if the process is running interactively. InspIRCd does not run interactively
\r
325 * as a service so if this is true, we just run the non-service inspircd.
\r
327 USEROBJECTFLAGS uoflags;
\r
328 HWINSTA winstation = GetProcessWindowStation();
\r
329 if (GetUserObjectInformation(winstation, UOI_FLAGS, &uoflags, sizeof(uoflags), NULL))
\r
331 if (uoflags.dwFlags == WSF_VISIBLE)
\r
332 return smain(argc, argv);
\r
335 /* If we get here, we know the service is installed so we can start it */
\r
337 SERVICE_TABLE_ENTRY serviceTable[] =
\r
339 {"InspIRCd", (LPSERVICE_MAIN_FUNCTION) ServiceMain },
\r
343 StartServiceCtrlDispatcher(serviceTable);
\r