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