daemon: Flush shunts before unloading plugins
[strongswan.git] / src / charon-svc / charon-svc.c
1 /*
2 * Copyright (C) 2013 Martin Willi
3 * Copyright (C) 2013 revosec AG
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 * for more details.
14 */
15
16 #include <library.h>
17 #include <hydra.h>
18 #include <daemon.h>
19
20 #include <utils/backtrace.h>
21 #include <threading/thread.h>
22
23 /**
24 * The name of our service, both internal and external
25 */
26 #define SERVICE_NAME "charon-svc"
27
28 /**
29 * Current service status
30 */
31 static SERVICE_STATUS status;
32
33 /**
34 * Handle for service status
35 */
36 static SERVICE_STATUS_HANDLE handle;
37
38 /**
39 * Wait event for main thread
40 */
41 static HANDLE event;
42
43 /**
44 * hook in library for debugging messages
45 */
46 extern void (*dbg) (debug_t group, level_t level, char *fmt, ...);
47
48 /**
49 * Forward declaration
50 */
51 static DWORD WINAPI service_handler(DWORD dwControl, DWORD dwEventType,
52 LPVOID lpEventData, LPVOID lpContext);
53
54 /**
55 * Logging hook for library logs, using stderr output
56 */
57 static void dbg_stderr(debug_t group, level_t level, char *fmt, ...)
58 {
59 va_list args;
60
61 if (level <= 1)
62 {
63 va_start(args, fmt);
64 fprintf(stderr, "00[%N] ", debug_names, group);
65 vfprintf(stderr, fmt, args);
66 fprintf(stderr, "\n");
67 va_end(args);
68 }
69 }
70
71 /**
72 * Log strongSwan/Windows version during startup
73 */
74 static void print_version()
75 {
76 OSVERSIONINFOEX osvie;
77
78 memset(&osvie, 0, sizeof(osvie));
79 osvie.dwOSVersionInfoSize = sizeof(osvie);
80
81 if (GetVersionEx((LPOSVERSIONINFO)&osvie))
82 {
83 DBG1(DBG_DMN, "Starting IKE service %s (strongSwan %s, "
84 "Windows %s %d.%d.%d (SP %d.%d)", SERVICE_NAME, VERSION,
85 osvie.wProductType == VER_NT_WORKSTATION ? "Client" : "Server",
86 osvie.dwMajorVersion, osvie.dwMinorVersion, osvie.dwBuildNumber,
87 osvie.wServicePackMajor, osvie.wServicePackMinor);
88 }
89 }
90
91 /**
92 * Update service state to SCM, increase check point if state didn't change
93 */
94 static void update_status(DWORD state)
95 {
96 if (state == status.dwCurrentState)
97 {
98 status.dwCheckPoint++;
99 }
100 else
101 {
102 status.dwCheckPoint = 0;
103 }
104 status.dwCurrentState = state;
105 if (handle)
106 {
107 SetServiceStatus(handle, &status);
108 }
109 }
110
111 /**
112 * Control handler for console
113 */
114 static BOOL WINAPI console_handler(DWORD dwCtrlType)
115 {
116 switch (dwCtrlType)
117 {
118 case CTRL_C_EVENT:
119 case CTRL_BREAK_EVENT:
120 case CTRL_CLOSE_EVENT:
121 DBG1(DBG_DMN, "application is stopping, cleaning up");
122 if (status.dwCurrentState == SERVICE_RUNNING)
123 {
124 charon->bus->alert(charon->bus, ALERT_SHUTDOWN_SIGNAL,
125 dwCtrlType);
126 }
127 /* signal main thread to clean up */
128 SetEvent(event);
129 return TRUE;
130 default:
131 return FALSE;
132 }
133 }
134
135 /**
136 * Service handler function
137 */
138 static DWORD WINAPI service_handler(DWORD dwControl, DWORD dwEventType,
139 LPVOID lpEventData, LPVOID lpContext)
140 {
141 switch (dwControl)
142 {
143 case SERVICE_CONTROL_STOP:
144 case SERVICE_CONTROL_SHUTDOWN:
145 DBG1(DBG_DMN, "service is stopping, cleaning up");
146 if (status.dwCurrentState == SERVICE_RUNNING)
147 {
148 charon->bus->alert(charon->bus, ALERT_SHUTDOWN_SIGNAL,
149 dwControl);
150 }
151 /* signal main thread to clean up */
152 SetEvent(event);
153 return NO_ERROR;
154 case SERVICE_CONTROL_INTERROGATE:
155 return NO_ERROR;
156 default:
157 return ERROR_CALL_NOT_IMPLEMENTED;
158 }
159 }
160
161 /**
162 * Wait for console program shutdown
163 */
164 static int console_wait()
165 {
166 update_status(SERVICE_RUNNING);
167
168 if (WaitForSingleObjectEx(event, INFINITE, TRUE) != WAIT_OBJECT_0)
169 {
170 return 2;
171 }
172 return 0;
173 }
174
175 /**
176 * Wait for service shutdown
177 */
178 static int service_wait()
179 {
180 /* service is initialized, we now accept control requests */
181 status.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
182 update_status(SERVICE_RUNNING);
183 status.dwControlsAccepted = 0;
184
185 if (WaitForSingleObjectEx(event, INFINITE, TRUE) != WAIT_OBJECT_0)
186 {
187 return 2;
188 }
189 return 0;
190 }
191
192 /**
193 * Initialize and run charon using a wait function
194 */
195 static void init_and_run(DWORD dwArgc, LPTSTR *lpszArgv, int (*wait)())
196 {
197 level_t levels[DBG_MAX];
198 int i;
199
200 for (i = 0; i < DBG_MAX; i++)
201 {
202 levels[i] = LEVEL_CTRL;
203 }
204
205 update_status(SERVICE_START_PENDING);
206 event = CreateEvent(NULL, FALSE, FALSE, NULL);
207 if (event)
208 {
209 update_status(SERVICE_START_PENDING);
210 if (library_init(NULL, SERVICE_NAME))
211 {
212 update_status(SERVICE_START_PENDING);
213 if (libhydra_init())
214 {
215 update_status(SERVICE_START_PENDING);
216 if (libcharon_init())
217 {
218 charon->load_loggers(charon, levels, TRUE);
219 print_version();
220 update_status(SERVICE_START_PENDING);
221 if (charon->initialize(charon, PLUGINS))
222 {
223 update_status(SERVICE_START_PENDING);
224 lib->plugins->status(lib->plugins, LEVEL_CTRL);
225
226 charon->start(charon);
227
228 status.dwWin32ExitCode = wait();
229 }
230 update_status(SERVICE_STOP_PENDING);
231 libcharon_deinit();
232 }
233 update_status(SERVICE_STOP_PENDING);
234 libhydra_deinit();
235 }
236 update_status(SERVICE_STOP_PENDING);
237 library_deinit();
238 }
239 update_status(SERVICE_STOP_PENDING);
240 CloseHandle(event);
241 }
242 update_status(SERVICE_STOPPED);
243 }
244
245 /**
246 * Main routine when running from console
247 */
248 static void console_main(DWORD dwArgc, LPTSTR *lpszArgv)
249 {
250 status.dwWin32ExitCode = 1;
251
252 if (SetConsoleCtrlHandler(console_handler, TRUE))
253 {
254 init_and_run(dwArgc, lpszArgv, console_wait);
255 SetConsoleCtrlHandler(console_handler, FALSE);
256 }
257 }
258
259 /**
260 * Switch the working directory to the executable directory
261 */
262 static bool switch_workingdir()
263 {
264 CHAR path[MAX_PATH], *pos;
265 HMODULE module;
266
267 module = GetModuleHandle(NULL);
268 if (!module)
269 {
270 return FALSE;
271 }
272 if (!GetModuleFileName(module, path, sizeof(path)))
273 {
274 return FALSE;
275 }
276 pos = strrchr(path, '\\');
277 if (!pos)
278 {
279 return FALSE;
280 }
281 *pos = 0;
282 return SetCurrentDirectory(path);
283 }
284
285 /**
286 * Service main routine when running as service
287 */
288 static void WINAPI service_main(DWORD dwArgc, LPTSTR *lpszArgv)
289 {
290 memset(&status, 0, sizeof(status));
291 status.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
292 status.dwWin32ExitCode = 1;
293
294 handle = RegisterServiceCtrlHandlerEx(SERVICE_NAME, service_handler, NULL);
295 if (handle)
296 {
297 if (switch_workingdir())
298 {
299 init_and_run(dwArgc, lpszArgv, service_wait);
300 }
301 }
302 }
303
304 /**
305 * Main function, starts the service
306 */
307 int main(int argc, char *argv[])
308 {
309 SERVICE_TABLE_ENTRY services[] = {
310 {
311 .lpServiceName = SERVICE_NAME,
312 .lpServiceProc = service_main,
313 },
314 { NULL, NULL },
315 };
316 DWORD err;
317
318 dbg = dbg_stderr;
319
320 if (!StartServiceCtrlDispatcher(services))
321 {
322 err = GetLastError();
323 if (err == ERROR_FAILED_SERVICE_CONTROLLER_CONNECT)
324 {
325 console_main(argc, argv);
326 }
327 else
328 {
329 return 2;
330 }
331 }
332 return status.dwWin32ExitCode;
333 }