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