child-delete: Reply as usual when concurrently rekeying the IKE_SA
[strongswan.git] / src / dumm / dumm.c
1 /*
2 * Copyright (C) 2008-2009 Tobias Brunner
3 * Copyright (C) 2007 Martin Willi
4 * Hochschule fuer Technik Rapperswil
5 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation; either version 2 of the License, or (at your
9 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * for more details.
15 */
16
17 #define _GNU_SOURCE
18
19 #include <sys/types.h>
20 #include <sys/stat.h>
21 #include <unistd.h>
22 #include <stdio.h>
23 #include <dirent.h>
24 #include <errno.h>
25
26 #include <utils/debug.h>
27 #include <collections/linked_list.h>
28
29 #include "dumm.h"
30
31 #define PERME (S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH)
32 #define GUEST_DIR "guests"
33 #define TEMPLATE_DIR "templates"
34
35 typedef struct private_dumm_t private_dumm_t;
36
37 struct private_dumm_t {
38 /** public dumm interface */
39 dumm_t public;
40 /** working dir */
41 char *dir;
42 /** directory of guests */
43 char *guest_dir;
44 /** directory of loaded template */
45 char *template;
46 /** list of managed guests */
47 linked_list_t *guests;
48 /** list of managed bridges */
49 linked_list_t *bridges;
50 };
51
52 METHOD(dumm_t, create_guest, guest_t*,
53 private_dumm_t *this, char *name, char *kernel, char *master, char *args)
54 {
55 guest_t *guest;
56
57 guest = guest_create(this->guest_dir, name, kernel, master, args);
58 if (guest)
59 {
60 this->guests->insert_last(this->guests, guest);
61 }
62 return guest;
63 }
64
65 METHOD(dumm_t, create_guest_enumerator, enumerator_t*,
66 private_dumm_t *this)
67 {
68 return this->guests->create_enumerator(this->guests);
69 }
70
71 METHOD(dumm_t, delete_guest, void,
72 private_dumm_t *this, guest_t *guest)
73 {
74 if (this->guests->remove(this->guests, guest, NULL))
75 {
76 char buf[512];
77 int len;
78
79 len = snprintf(buf, sizeof(buf), "rm -Rf %s/%s",
80 this->guest_dir, guest->get_name(guest));
81 guest->destroy(guest);
82 if (len > 8 && len < 512)
83 {
84 ignore_result(system(buf));
85 }
86 }
87 }
88
89 METHOD(dumm_t, create_bridge, bridge_t*,
90 private_dumm_t *this, char *name)
91 {
92 bridge_t *bridge;
93
94 bridge = bridge_create(name);
95 if (bridge)
96 {
97 this->bridges->insert_last(this->bridges, bridge);
98 }
99 return bridge;
100 }
101
102 METHOD(dumm_t, create_bridge_enumerator, enumerator_t*,
103 private_dumm_t *this)
104 {
105 return this->bridges->create_enumerator(this->bridges);
106 }
107
108 METHOD(dumm_t, delete_bridge, void,
109 private_dumm_t *this, bridge_t *bridge)
110 {
111 if (this->bridges->remove(this->bridges, bridge, NULL))
112 {
113 bridge->destroy(bridge);
114 }
115 }
116
117 METHOD(dumm_t, add_overlay, bool,
118 private_dumm_t *this, char *dir)
119 {
120 enumerator_t *enumerator;
121 guest_t *guest;
122
123 if (dir == NULL)
124 {
125 return TRUE;
126 }
127 if (strlen(dir) > PATH_MAX)
128 {
129 DBG1(DBG_LIB, "overlay directory string '%s' is too long", dir);
130 return FALSE;
131 }
132 if (access(dir, F_OK) != 0)
133 {
134 if (!mkdir_p(dir, PERME))
135 {
136 DBG1(DBG_LIB, "creating overlay directory '%s' failed: %m", dir);
137 return FALSE;
138 }
139 }
140 enumerator = this->guests->create_enumerator(this->guests);
141 while (enumerator->enumerate(enumerator, (void**)&guest))
142 {
143 char guest_dir[PATH_MAX];
144 int len = snprintf(guest_dir, sizeof(guest_dir), "%s/%s", dir,
145 guest->get_name(guest));
146 if (len < 0 || len >= sizeof(guest_dir))
147 {
148 goto error;
149 }
150 if (access(guest_dir, F_OK) != 0)
151 {
152 if (!mkdir_p(guest_dir, PERME))
153 {
154 DBG1(DBG_LIB, "creating overlay directory for guest '%s' failed: %m",
155 guest->get_name(guest));
156 goto error;
157 }
158 }
159 if (!guest->add_overlay(guest, guest_dir))
160 {
161 goto error;
162 }
163 }
164 enumerator->destroy(enumerator);
165 return TRUE;
166 error:
167 enumerator->destroy(enumerator);
168 this->public.del_overlay(&this->public, dir);
169 return FALSE;
170 }
171
172 METHOD(dumm_t, del_overlay, bool,
173 private_dumm_t *this, char *dir)
174 {
175 bool ret = FALSE;
176 enumerator_t *enumerator;
177 guest_t *guest;
178
179 enumerator = this->guests->create_enumerator(this->guests);
180 while (enumerator->enumerate(enumerator, (void**)&guest))
181 {
182 char guest_dir[PATH_MAX];
183 int len = snprintf(guest_dir, sizeof(guest_dir), "%s/%s", dir,
184 guest->get_name(guest));
185 if (len < 0 || len >= sizeof(guest_dir))
186 {
187 continue;
188 }
189 ret = guest->del_overlay(guest, guest_dir) || ret;
190 }
191 enumerator->destroy(enumerator);
192 return ret;
193 }
194
195 METHOD(dumm_t, pop_overlay, bool,
196 private_dumm_t *this)
197 {
198 bool ret = FALSE;
199 enumerator_t *enumerator;
200 guest_t *guest;
201
202 enumerator = this->guests->create_enumerator(this->guests);
203 while (enumerator->enumerate(enumerator, (void**)&guest))
204 {
205 ret = guest->pop_overlay(guest) || ret;
206 }
207 enumerator->destroy(enumerator);
208 return ret;
209 }
210
211 /**
212 * disable the currently enabled template
213 */
214 static void clear_template(private_dumm_t *this)
215 {
216 if (this->template)
217 {
218 del_overlay(this, this->template);
219 free(this->template);
220 this->template = NULL;
221 }
222 }
223
224 METHOD(dumm_t, load_template, bool,
225 private_dumm_t *this, char *name)
226 {
227 clear_template(this);
228 if (name == NULL)
229 {
230 return TRUE;
231 }
232 if (strlen(name) > PATH_MAX)
233 {
234 DBG1(DBG_LIB, "template name '%s' is too long", name);
235 return FALSE;
236 }
237 if (strchr(name, '/') != NULL)
238 {
239 DBG1(DBG_LIB, "template name '%s' must not contain '/' characters", name);
240 return FALSE;
241 }
242 if (asprintf(&this->template, "%s/%s", TEMPLATE_DIR, name) < 0)
243 {
244 this->template = NULL;
245 return FALSE;
246 }
247 if (access(this->template, F_OK) != 0)
248 {
249 if (!mkdir_p(this->template, PERME))
250 {
251 DBG1(DBG_LIB, "creating template directory '%s' failed: %m",
252 this->template);
253 return FALSE;
254 }
255 }
256 return add_overlay(this, this->template);
257 }
258
259 /**
260 * Template directory enumerator
261 */
262 typedef struct {
263 /** implements enumerator_t */
264 enumerator_t public;
265 /** directory enumerator */
266 enumerator_t *inner;
267 } template_enumerator_t;
268
269 METHOD(enumerator_t, template_enumerate, bool,
270 template_enumerator_t *this, char **template)
271 {
272 struct stat st;
273 char *rel;
274
275 while (this->inner->enumerate(this->inner, &rel, NULL, &st))
276 {
277 if (S_ISDIR(st.st_mode) && *rel != '.')
278 {
279 *template = rel;
280 return TRUE;
281 }
282 }
283 return FALSE;
284 }
285
286 METHOD(enumerator_t, template_enumerator_destroy, void,
287 template_enumerator_t *this)
288 {
289 this->inner->destroy(this->inner);
290 free(this);
291 }
292
293 METHOD(dumm_t, create_template_enumerator, enumerator_t*,
294 private_dumm_t *this)
295 {
296 template_enumerator_t *enumerator;
297 INIT(enumerator,
298 .public = {
299 .enumerate = (void*)_template_enumerate,
300 .destroy = (void*)_template_enumerator_destroy,
301 },
302 .inner = enumerator_create_directory(TEMPLATE_DIR),
303 );
304 if (!enumerator->inner)
305 {
306 free(enumerator);
307 return enumerator_create_empty();
308 }
309 return &enumerator->public;
310 }
311
312 METHOD(dumm_t, destroy, void,
313 private_dumm_t *this)
314 {
315 enumerator_t *enumerator;
316 guest_t *guest;
317
318 this->bridges->destroy_offset(this->bridges, offsetof(bridge_t, destroy));
319
320 enumerator = this->guests->create_enumerator(this->guests);
321 while (enumerator->enumerate(enumerator, (void**)&guest))
322 {
323 guest->stop(guest, NULL);
324 }
325 enumerator->destroy(enumerator);
326
327 while (this->guests->remove_last(this->guests, (void**)&guest) == SUCCESS)
328 {
329 guest->destroy(guest);
330 }
331 this->guests->destroy(this->guests);
332 free(this->guest_dir);
333 free(this->template);
334 free(this->dir);
335 free(this);
336 }
337
338 /**
339 * load all guests in our working dir
340 */
341 static void load_guests(private_dumm_t *this)
342 {
343 DIR *dir;
344 struct dirent *ent;
345 guest_t *guest;
346
347 dir = opendir(this->guest_dir);
348 if (dir == NULL)
349 {
350 return;
351 }
352
353 while ((ent = readdir(dir)))
354 {
355 if (*ent->d_name == '.')
356 { /* skip ".", ".." and hidden files (such as ".svn") */
357 continue;
358 }
359 guest = guest_load(this->guest_dir, ent->d_name);
360 if (guest)
361 {
362 this->guests->insert_last(this->guests, guest);
363 }
364 else
365 {
366 DBG1(DBG_LIB, "loading guest in directory '%s' failed, skipped",
367 ent->d_name);
368 }
369 }
370 closedir(dir);
371 }
372
373 /**
374 * create a dumm instance
375 */
376 dumm_t *dumm_create(char *dir)
377 {
378 char cwd[PATH_MAX];
379 private_dumm_t *this;
380
381 INIT(this,
382 .public = {
383 .create_guest = _create_guest,
384 .create_guest_enumerator = _create_guest_enumerator,
385 .delete_guest = _delete_guest,
386 .create_bridge = _create_bridge,
387 .create_bridge_enumerator = _create_bridge_enumerator,
388 .delete_bridge = _delete_bridge,
389 .add_overlay = _add_overlay,
390 .del_overlay = _del_overlay,
391 .pop_overlay = _pop_overlay,
392 .load_template = _load_template,
393 .create_template_enumerator = _create_template_enumerator,
394 .destroy = _destroy,
395 },
396 );
397
398 if (dir && *dir == '/')
399 {
400 this->dir = strdup(dir);
401 }
402 else
403 {
404 if (getcwd(cwd, sizeof(cwd)) == NULL)
405 {
406 free(this);
407 return NULL;
408 }
409 if (dir)
410 {
411 if (asprintf(&this->dir, "%s/%s", cwd, dir) < 0)
412 {
413 this->dir = NULL;
414 }
415 }
416 else
417 {
418 this->dir = strdup(cwd);
419 }
420 }
421 if (asprintf(&this->guest_dir, "%s/%s", this->dir, GUEST_DIR) < 0)
422 {
423 this->guest_dir = NULL;
424 }
425
426 this->guests = linked_list_create();
427 this->bridges = linked_list_create();
428
429 if (this->dir == NULL || this->guest_dir == NULL ||
430 (mkdir(this->guest_dir, PERME) < 0 && errno != EEXIST))
431 {
432 DBG1(DBG_LIB, "creating guest directory '%s' failed: %m",
433 this->guest_dir);
434 destroy(this);
435 return NULL;
436 }
437
438 load_guests(this);
439 return &this->public;
440 }
441