swanctl: Stop logging with Ctrl+C on Windows as well
[strongswan.git] / src / swanctl / commands / load_conns.c
1 /*
2 * Copyright (C) 2014 Martin Willi
3 * Copyright (C) 2014 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 #define _GNU_SOURCE
17 #include <stdio.h>
18 #include <errno.h>
19 #include <limits.h>
20
21 #include "command.h"
22 #include "swanctl.h"
23
24 /**
25 * Check if we should handle a key as a list of comma separated values
26 */
27 static bool is_list_key(char *key)
28 {
29 char *keys[] = {
30 "local_addrs",
31 "remote_addrs",
32 "proposals",
33 "esp_proposals",
34 "ah_proposals",
35 "local_ts",
36 "remote_ts",
37 "vips",
38 "pools",
39 "groups",
40 };
41 int i;
42
43 for (i = 0; i < countof(keys); i++)
44 {
45 if (strcaseeq(keys[i], key))
46 {
47 return TRUE;
48 }
49 }
50 return FALSE;
51 }
52
53 /**
54 * Check if we should handle a key as a list of comma separated files
55 */
56 static bool is_file_list_key(char *key)
57 {
58 char *keys[] = {
59 "certs",
60 "cacerts",
61 };
62 int i;
63
64 for (i = 0; i < countof(keys); i++)
65 {
66 if (strcaseeq(keys[i], key))
67 {
68 return TRUE;
69 }
70 }
71 return FALSE;
72 }
73
74 /**
75 * Add a vici list from a comma separated string value
76 */
77 static void add_list_key(vici_req_t *req, char *key, char *value)
78 {
79 enumerator_t *enumerator;
80 char *token;
81
82 vici_begin_list(req, key);
83 enumerator = enumerator_create_token(value, ",", " ");
84 while (enumerator->enumerate(enumerator, &token))
85 {
86 vici_add_list_itemf(req, "%s", token);
87 }
88 enumerator->destroy(enumerator);
89 vici_end_list(req);
90 }
91
92 /**
93 * Add a vici list of blobs from a comma separated file list
94 */
95 static void add_file_list_key(vici_req_t *req, char *key, char *value)
96 {
97 enumerator_t *enumerator;
98 chunk_t *map;
99 char *token, buf[PATH_MAX];
100
101 vici_begin_list(req, key);
102 enumerator = enumerator_create_token(value, ",", " ");
103 while (enumerator->enumerate(enumerator, &token))
104 {
105 if (!path_absolute(token))
106 {
107 if (streq(key, "certs"))
108 {
109 snprintf(buf, sizeof(buf), "%s%s%s",
110 SWANCTL_X509DIR, DIRECTORY_SEPARATOR, token);
111 token = buf;
112 }
113 if (streq(key, "cacerts"))
114 {
115 snprintf(buf, sizeof(buf), "%s%s%s",
116 SWANCTL_X509CADIR, DIRECTORY_SEPARATOR, token);
117 token = buf;
118 }
119 }
120
121 map = chunk_map(token, FALSE);
122 if (map)
123 {
124 vici_add_list_item(req, map->ptr, map->len);
125 chunk_unmap(map);
126 }
127 else
128 {
129 fprintf(stderr, "loading certificate '%s' failed: %s\n",
130 token, strerror(errno));
131 }
132 }
133 enumerator->destroy(enumerator);
134 vici_end_list(req);
135 }
136
137 /**
138 * Translate setting key/values from a section into vici key-values/lists
139 */
140 static void add_key_values(vici_req_t *req, settings_t *cfg, char *section)
141 {
142 enumerator_t *enumerator;
143 char *key, *value;
144
145 enumerator = cfg->create_key_value_enumerator(cfg, section);
146 while (enumerator->enumerate(enumerator, &key, &value))
147 {
148 if (is_list_key(key))
149 {
150 add_list_key(req, key, value);
151 }
152 else if (is_file_list_key(key))
153 {
154 add_file_list_key(req, key, value);
155 }
156 else
157 {
158 vici_add_key_valuef(req, key, "%s", value);
159 }
160 }
161 enumerator->destroy(enumerator);
162 }
163
164 /**
165 * Translate a settings section to a vici section
166 */
167 static void add_sections(vici_req_t *req, settings_t *cfg, char *section)
168 {
169 enumerator_t *enumerator;
170 char *name, buf[256];
171
172 enumerator = cfg->create_section_enumerator(cfg, section);
173 while (enumerator->enumerate(enumerator, &name))
174 {
175 vici_begin_section(req, name);
176 snprintf(buf, sizeof(buf), "%s.%s", section, name);
177 add_key_values(req, cfg, buf);
178 add_sections(req, cfg, buf);
179 vici_end_section(req);
180 }
181 enumerator->destroy(enumerator);
182 }
183
184 /**
185 * Load an IKE_SA config with CHILD_SA configs from a section
186 */
187 static bool load_conn(vici_conn_t *conn, settings_t *cfg,
188 char *section, bool raw)
189 {
190 vici_req_t *req;
191 vici_res_t *res;
192 bool ret = TRUE;
193 char buf[128];
194
195 snprintf(buf, sizeof(buf), "%s.%s", "connections", section);
196
197 req = vici_begin("load-conn");
198
199 vici_begin_section(req, section);
200 add_key_values(req, cfg, buf);
201 add_sections(req, cfg, buf);
202 vici_end_section(req);
203
204 res = vici_submit(req, conn);
205 if (!res)
206 {
207 fprintf(stderr, "load-conn request failed: %s\n", strerror(errno));
208 return FALSE;
209 }
210 if (raw)
211 {
212 vici_dump(res, "load-conn reply", stdout);
213 }
214 else if (!streq(vici_find_str(res, "no", "success"), "yes"))
215 {
216 fprintf(stderr, "loading connection '%s' failed: %s\n",
217 section, vici_find_str(res, "", "errmsg"));
218 ret = FALSE;
219 }
220 else
221 {
222 printf("loaded connection '%s'\n", section);
223 }
224 vici_free_res(res);
225 return ret;
226 }
227
228 CALLBACK(list_conn, int,
229 linked_list_t *list, vici_res_t *res, char *name, void *value, int len)
230 {
231 if (streq(name, "conns"))
232 {
233 char *str;
234
235 if (asprintf(&str, "%.*s", len, value) != -1)
236 {
237 list->insert_last(list, str);
238 }
239 }
240 return 0;
241 }
242
243 /**
244 * Create a list of currently loaded connections
245 */
246 static linked_list_t* list_conns(vici_conn_t *conn, bool raw)
247 {
248 linked_list_t *list;
249 vici_res_t *res;
250
251 list = linked_list_create();
252
253 res = vici_submit(vici_begin("get-conns"), conn);
254 if (res)
255 {
256 if (raw)
257 {
258 vici_dump(res, "get-conns reply", stdout);
259 }
260 vici_parse_cb(res, NULL, NULL, list_conn, list);
261 vici_free_res(res);
262 }
263 return list;
264 }
265
266 /**
267 * Remove and free a string from a list
268 */
269 static void remove_from_list(linked_list_t *list, char *str)
270 {
271 enumerator_t *enumerator;
272 char *current;
273
274 enumerator = list->create_enumerator(list);
275 while (enumerator->enumerate(enumerator, &current))
276 {
277 if (streq(current, str))
278 {
279 list->remove_at(list, enumerator);
280 free(current);
281 }
282 }
283 enumerator->destroy(enumerator);
284 }
285
286 /**
287 * Unload a connection by name
288 */
289 static bool unload_conn(vici_conn_t *conn, char *name, bool raw)
290 {
291 vici_req_t *req;
292 vici_res_t *res;
293 bool ret = TRUE;
294
295 req = vici_begin("unload-conn");
296 vici_add_key_valuef(req, "name", "%s", name);
297 res = vici_submit(req, conn);
298 if (!res)
299 {
300 fprintf(stderr, "unload-conn request failed: %s\n", strerror(errno));
301 return FALSE;
302 }
303 if (raw)
304 {
305 vici_dump(res, "unload-conn reply", stdout);
306 }
307 else if (!streq(vici_find_str(res, "no", "success"), "yes"))
308 {
309 fprintf(stderr, "unloading connection '%s' failed: %s\n",
310 name, vici_find_str(res, "", "errmsg"));
311 ret = FALSE;
312 }
313 vici_free_res(res);
314 return ret;
315 }
316
317 static int load_conns(vici_conn_t *conn)
318 {
319 bool raw = FALSE;
320 u_int found = 0, loaded = 0, unloaded = 0;
321 char *arg, *section;
322 enumerator_t *enumerator;
323 linked_list_t *conns;
324 settings_t *cfg;
325
326 while (TRUE)
327 {
328 switch (command_getopt(&arg))
329 {
330 case 'h':
331 return command_usage(NULL);
332 case 'r':
333 raw = TRUE;
334 continue;
335 case EOF:
336 break;
337 default:
338 return command_usage("invalid --load-conns option");
339 }
340 break;
341 }
342
343 cfg = settings_create(SWANCTL_CONF);
344 if (!cfg)
345 {
346 fprintf(stderr, "parsing '%s' failed\n", SWANCTL_CONF);
347 return EINVAL;
348 }
349
350 conns = list_conns(conn, raw);
351
352 enumerator = cfg->create_section_enumerator(cfg, "connections");
353 while (enumerator->enumerate(enumerator, &section))
354 {
355 remove_from_list(conns, section);
356 found++;
357 if (load_conn(conn, cfg, section, raw))
358 {
359 loaded++;
360 }
361 }
362 enumerator->destroy(enumerator);
363
364 cfg->destroy(cfg);
365
366 /* unload all connection in daemon, but not in file */
367 while (conns->remove_first(conns, (void**)&section) == SUCCESS)
368 {
369 if (unload_conn(conn, section, raw))
370 {
371 unloaded++;
372 }
373 free(section);
374 }
375 conns->destroy(conns);
376
377 if (raw)
378 {
379 return 0;
380 }
381 if (found == 0)
382 {
383 printf("no connections found, %u unloaded\n", unloaded);
384 return 0;
385 }
386 if (loaded == found)
387 {
388 printf("successfully loaded %u connections, %u unloaded\n",
389 loaded, unloaded);
390 return 0;
391 }
392 fprintf(stderr, "loaded %u of %u connections, %u failed to load, "
393 "%u unloaded\n", loaded, found, found - loaded, unloaded);
394 return EINVAL;
395 }
396
397 /**
398 * Register the command.
399 */
400 static void __attribute__ ((constructor))reg()
401 {
402 command_register((command_t) {
403 load_conns, 'c', "load-conns", "(re-)load connection configuration",
404 {"[--raw]"},
405 {
406 {"help", 'h', 0, "show usage information"},
407 {"raw", 'r', 0, "dump raw response message"},
408 }
409 });
410 }