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