]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - win/win32service.cpp
detect if the process has an interactive session (if its started as a service, the...
[user/henk/code/inspircd.git] / win / win32service.cpp
1 /*       +------------------------------------+
2  *       | Inspire Internet Relay Chat Daemon |
3  *       +------------------------------------+
4  *
5  *  InspIRCd: (C) 2002-2008 InspIRCd Development Team
6  * See: http://www.inspircd.org/wiki/index.php/Credits
7  *
8  * This program is free but copyrighted software; see
9  *          the file COPYING for details.
10  *
11  * ---------------------------------------------------
12  */\r
13 \r
14 #include <windows.h>\r
15 #include <stdlib.h>\r
16 #include <string.h>\r
17 #include <stdio.h>\r
18 \r
19 extern int smain(int argc, char** argv);\r
20 extern const char* dlerror();\r
21 \r
22 static SERVICE_STATUS_HANDLE serviceStatusHandle;\r
23 static HANDLE hThreadEvent;\r
24 static HANDLE killServiceEvent;\r
25 static int serviceCurrentStatus;\r
26 \r
27 // This is used to define ChangeServiceConf2() as we can't link\r
28 // directly against this symbol (see below where it is used)\r
29 typedef BOOL (CALLBACK* SETSERVDESC)(SC_HANDLE,DWORD,LPVOID);\r
30 \r
31 /* A commandline parameter handler for service specific commandline parameters */\r
32 typedef void (*CommandlineParameterHandler)(void);\r
33 \r
34 /* Represents a commandline and its handler */\r
35 struct Commandline\r
36 {\r
37         const char* Switch;\r
38         CommandlineParameterHandler Handler;\r
39 };\r
40 \r
41 \r
42 SETSERVDESC ChangeServiceConf;          // A function pointer for dynamic linking tricks\r
43 \r
44 \r
45 void KillService()\r
46 {\r
47         /* FIXME: This should set a flag in the mainloop for shutting down */\r
48         SetEvent(hThreadEvent);\r
49         Sleep(2000);\r
50         SetEvent(killServiceEvent);\r
51 }\r
52 \r
53 DWORD WINAPI WorkerThread(LPDWORD param)\r
54 {\r
55         // *** REAL MAIN HERE ***\r
56         char modname[MAX_PATH];\r
57         GetModuleFileName(NULL, modname, sizeof(modname));\r
58         char* argv[] = { modname, "--nofork", "--debug" };\r
59         smain(3, argv);\r
60         KillService();\r
61         return 0;\r
62 }\r
63 \r
64 void StartServiceThread()\r
65 {\r
66         DWORD dwd;\r
67         CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)WorkerThread,NULL,0,&dwd);\r
68 }\r
69 \r
70 \r
71 BOOL UpdateSCMStatus (DWORD dwCurrentState, DWORD dwWin32ExitCode, DWORD dwServiceSpecificExitCode, DWORD dwCheckPoint, DWORD dwWaitHint)\r
72 {\r
73         BOOL success;\r
74         SERVICE_STATUS serviceStatus;\r
75         serviceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;\r
76         serviceStatus.dwCurrentState = dwCurrentState;\r
77 \r
78         if (dwCurrentState == SERVICE_START_PENDING)\r
79         {\r
80                 serviceStatus.dwControlsAccepted = 0;\r
81         }\r
82         else\r
83         {\r
84                 serviceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;\r
85         }\r
86 \r
87         if (dwServiceSpecificExitCode == 0)\r
88         {\r
89                 serviceStatus.dwWin32ExitCode = dwWin32ExitCode;\r
90         }\r
91         else\r
92         {\r
93                 serviceStatus.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;\r
94         }\r
95         serviceStatus.dwServiceSpecificExitCode =   dwServiceSpecificExitCode;\r
96         serviceStatus.dwCheckPoint = dwCheckPoint;\r
97         serviceStatus.dwWaitHint = dwWaitHint;\r
98 \r
99         success = SetServiceStatus (serviceStatusHandle, &serviceStatus);\r
100         if (!success)\r
101         {\r
102                 KillService();\r
103         }\r
104         return success;\r
105 }\r
106 \r
107 \r
108 void terminateService (int code, int wincode)\r
109 {\r
110         UpdateSCMStatus(SERVICE_STOPPED,wincode?wincode:ERROR_SERVICE_SPECIFIC_ERROR,(wincode)?0:code,0,0);\r
111         return;\r
112 }\r
113 \r
114 \r
115 VOID ServiceCtrlHandler (DWORD controlCode)\r
116 {\r
117         switch(controlCode)\r
118         {\r
119                 case SERVICE_CONTROL_INTERROGATE:\r
120                 break;\r
121                 case SERVICE_CONTROL_SHUTDOWN:\r
122                 case SERVICE_CONTROL_STOP:\r
123                         serviceCurrentStatus = SERVICE_STOP_PENDING;\r
124                         UpdateSCMStatus(SERVICE_STOP_PENDING, NO_ERROR, 0, 1, 5000);\r
125                         KillService();\r
126                         UpdateSCMStatus(SERVICE_STOPPED, NO_ERROR, 0, 0, 0);\r
127                         return;\r
128                 default:\r
129                 break;\r
130         }\r
131         UpdateSCMStatus(serviceCurrentStatus, NO_ERROR, 0, 0, 0);\r
132 }\r
133 \r
134 \r
135 VOID ServiceMain(DWORD argc, LPTSTR *argv)\r
136 {\r
137         BOOL success;\r
138         DWORD type=0, size=0;\r
139 \r
140         serviceStatusHandle = RegisterServiceCtrlHandler("InspIRCd", (LPHANDLER_FUNCTION)ServiceCtrlHandler);\r
141         if (!serviceStatusHandle)\r
142         {\r
143                 terminateService(1, GetLastError());\r
144                 return;\r
145         }\r
146 \r
147         success = UpdateSCMStatus(SERVICE_START_PENDING, NO_ERROR, 0, 1, 1000);\r
148         if (!success)\r
149         {\r
150                 terminateService(2, GetLastError());\r
151                 return;\r
152         }\r
153 \r
154         killServiceEvent = CreateEvent(NULL,true,false,NULL);\r
155         hThreadEvent = CreateEvent(NULL,true,false,NULL);\r
156 \r
157         if (!killServiceEvent || !hThreadEvent)\r
158         {\r
159                 terminateService(99, GetLastError());\r
160                 return;\r
161         }\r
162 \r
163         success = UpdateSCMStatus(SERVICE_START_PENDING, NO_ERROR, 0, 2, 1000);\r
164         if (!success)\r
165         {\r
166                 terminateService(2, GetLastError());\r
167                 return;\r
168         }\r
169 \r
170         StartServiceThread();\r
171         serviceCurrentStatus = SERVICE_RUNNING;\r
172         success = UpdateSCMStatus(SERVICE_RUNNING, NO_ERROR, 0, 0, 0);\r
173         if (!success)\r
174         {\r
175                 terminateService(6, GetLastError());\r
176                 return;\r
177         }\r
178         WaitForSingleObject (killServiceEvent, INFINITE);\r
179 }\r
180 \r
181 void InstallService()\r
182 {\r
183         SC_HANDLE myService, scm;\r
184         SERVICE_DESCRIPTION svDesc;\r
185         HINSTANCE advapi32;\r
186 \r
187         char modname[MAX_PATH];\r
188         GetModuleFileName(NULL, modname, sizeof(modname));\r
189 \r
190         scm = OpenSCManager(0,0,SC_MANAGER_CREATE_SERVICE);\r
191         if (!scm)\r
192         {\r
193                 printf("Unable to open service control manager: %s\n", dlerror());\r
194                 return;\r
195         }\r
196 \r
197         myService = CreateService(scm,"InspIRCd","Inspire IRC Daemon", SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS,\r
198                 SERVICE_AUTO_START, SERVICE_ERROR_NORMAL, modname, 0, 0, 0, NULL, NULL);\r
199 \r
200         if (!myService)\r
201         {\r
202                 printf("Unable to create service: %s\n", dlerror());\r
203                 CloseServiceHandle(scm);\r
204                 return;\r
205         }\r
206 \r
207         // *** Set service description ***\r
208         // this is supported from 5.0 (win2k) onwards only, so we can't link to the definition of\r
209         // this function in advapi32.lib, otherwise the program will not run on windows NT 4. We\r
210         // must use LoadLibrary and GetProcAddress to export the function name from advapi32.dll\r
211         advapi32 = LoadLibrary("advapi32.dll");\r
212         if (advapi32)\r
213         {\r
214                 ChangeServiceConf = (SETSERVDESC)GetProcAddress(advapi32,"ChangeServiceConfig2A");\r
215                 if (ChangeServiceConf)\r
216                 {\r
217                         char desc[] = "The Inspire Internet Relay Chat Daemon hosts IRC channels and conversations.\\r
218  If this service is stopped, the IRC server will not run.";\r
219                         svDesc.lpDescription = desc;\r
220                         BOOL success = ChangeServiceConf(myService,SERVICE_CONFIG_DESCRIPTION, &svDesc);\r
221                         if (!success)\r
222                         {\r
223                                 printf("Unable to set service description: %s\n", dlerror());\r
224                                 CloseServiceHandle(myService);\r
225                                 CloseServiceHandle(scm);\r
226                                 return;\r
227                         }\r
228                 }\r
229                 FreeLibrary(advapi32);\r
230         }\r
231 \r
232         printf("Service installed.\n");\r
233         CloseServiceHandle(myService);\r
234         CloseServiceHandle(scm);\r
235 }\r
236 \r
237 void RemoveService()\r
238 {\r
239         SC_HANDLE myService, scm;\r
240 \r
241         scm = OpenSCManager(0,0,SC_MANAGER_CREATE_SERVICE);\r
242         if (!scm)\r
243         {\r
244                 printf("Unable to open service control manager: %s\n", dlerror());\r
245                 return;\r
246         }\r
247 \r
248         myService = OpenService(scm,"InspIRCd",SERVICE_ALL_ACCESS);\r
249         if (!myService)\r
250         {\r
251                 printf("Unable to open service: %s\n", dlerror());\r
252                 CloseServiceHandle(scm);\r
253                 return;\r
254         }\r
255 \r
256         if (!DeleteService(myService))\r
257         {\r
258                 printf("Unable to delete service: %s\n", dlerror());\r
259                 CloseServiceHandle(myService);\r
260                 CloseServiceHandle(scm);\r
261                 return;\r
262         }\r
263 \r
264         printf("Service removed.\n");\r
265         CloseServiceHandle(myService);\r
266         CloseServiceHandle(scm);\r
267 }\r
268 \r
269 /* In windows, our main() flows through here, before calling the 'real' main, smain() in inspircd.cpp */\r
270 int main(int argc, char** argv)\r
271 {\r
272 \r
273         Commandline params[] = {\r
274                 { "--installservice", InstallService },\r
275                 { "--removeservice", RemoveService },\r
276                 { NULL }\r
277         };\r
278 \r
279         /* Check for parameters */\r
280         if (argc > 1)\r
281         {\r
282                 for (int z = 0; params[z].Switch; ++z)\r
283                 {\r
284                         if (!_stricmp(argv[1], params[z].Switch))\r
285                         {\r
286                                 params[z].Handler();\r
287                                 return 0;\r
288                         }\r
289                 }\r
290         }\r
291 \r
292         /* First, check if the service is installed.\r
293          * if it is not, or we're starting as non-administrator,\r
294          * just call smain() and start as normal non-service\r
295          * process.\r
296          */\r
297         SC_HANDLE myService, scm;\r
298         scm = OpenSCManager(0,0,SC_MANAGER_CREATE_SERVICE);\r
299         if (scm)\r
300         {\r
301                 myService = OpenService(scm,"InspIRCd",SERVICE_ALL_ACCESS);\r
302                 if (!myService)\r
303                 {\r
304                         /* Service not installed or no permission to modify it */\r
305                         CloseServiceHandle(scm);\r
306                         return smain(argc, argv);\r
307                 }\r
308         }\r
309         else\r
310         {\r
311                 /* Not enough privileges to open the SCM */\r
312                 return smain(argc, argv);\r
313         }\r
314 \r
315         CloseServiceHandle(myService);\r
316         CloseServiceHandle(scm);\r
317 \r
318         /* Check if the process is running interactively. InspIRCd does not run interactively\r
319          * as a service so if this is true, we just run the non-service inspircd.\r
320          */\r
321         USEROBJECTFLAGS uoflags;\r
322         HWINSTA winstation = GetProcessWindowStation();\r
323         if (GetUserObjectInformation(winstation, UOI_FLAGS, &uoflags, sizeof(uoflags), NULL))\r
324         {\r
325                 if (uoflags.dwFlags == WSF_VISIBLE)\r
326                         return smain(argc, argv);\r
327         }\r
328 \r
329         /* If we get here, we know the service is installed so we can start it */\r
330 \r
331         SERVICE_TABLE_ENTRY serviceTable[] =\r
332         {\r
333                 {"InspIRCd", (LPSERVICE_MAIN_FUNCTION) ServiceMain },\r
334                 {NULL, NULL}\r
335         };\r
336 \r
337         StartServiceCtrlDispatcher(serviceTable);\r
338         return 0;\r
339 }\r