added a Dumm template enumerator
[strongswan.git] / src / dumm / dumm.c
1 /*
2 * Copyright (C) 2008 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 <debug.h>
27 #include <utils/linked_list.h>
28
29 #include "dumm.h"
30
31 #define PERME (S_IRWXU | S_IRWXG)
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 /**
53 * Implementation of dumm_t.create_guest.
54 */
55 static guest_t* create_guest(private_dumm_t *this, char *name, char *kernel,
56 char *master, char *args)
57 {
58 guest_t *guest;
59
60 guest = guest_create(this->guest_dir, name, kernel, master, args);
61 if (guest)
62 {
63 this->guests->insert_last(this->guests, guest);
64 }
65 return guest;
66 }
67
68 /**
69 * Implementation of dumm_t.create_guest_enumerator.
70 */
71 static enumerator_t* create_guest_enumerator(private_dumm_t *this)
72 {
73 return this->guests->create_enumerator(this->guests);
74 }
75
76 /**
77 * Implementation of dumm_t.delete_guest.
78 */
79 static void delete_guest(private_dumm_t *this, guest_t *guest)
80 {
81 if (this->guests->remove(this->guests, guest, NULL))
82 {
83 char buf[512];
84 int len;
85
86 len = snprintf(buf, sizeof(buf), "rm -Rf %s/%s",
87 this->guest_dir, guest->get_name(guest));
88 guest->destroy(guest);
89 if (len > 8 && len < 512)
90 {
91 ignore_result(system(buf));
92 }
93 }
94 }
95
96 /**
97 * Implementation of dumm_t.create_bridge.
98 */
99 static bridge_t* create_bridge(private_dumm_t *this, char *name)
100 {
101 bridge_t *bridge;
102
103 bridge = bridge_create(name);
104 if (bridge)
105 {
106 this->bridges->insert_last(this->bridges, bridge);
107 }
108 return bridge;
109 }
110
111 /**
112 * Implementation of dumm_t.create_bridge_enumerator.
113 */
114 static enumerator_t* create_bridge_enumerator(private_dumm_t *this)
115 {
116 return this->bridges->create_enumerator(this->bridges);
117 }
118
119 /**
120 * Implementation of dumm_t.delete_bridge.
121 */
122 static void delete_bridge(private_dumm_t *this, bridge_t *bridge)
123 {
124 if (this->bridges->remove(this->bridges, bridge, NULL))
125 {
126 bridge->destroy(bridge);
127 }
128 }
129
130 /**
131 * disable the currently enabled template
132 */
133 static void clear_template(private_dumm_t *this)
134 {
135 enumerator_t *enumerator;
136 guest_t *guest;
137
138 free(this->template);
139 this->template = NULL;
140
141 enumerator = this->guests->create_enumerator(this->guests);
142 while (enumerator->enumerate(enumerator, (void**)&guest))
143 {
144 guest->load_template(guest, NULL);
145 }
146 enumerator->destroy(enumerator);
147 }
148
149 /**
150 * Implementation of dumm_t.load_template.
151 */
152 static bool load_template(private_dumm_t *this, char *dir)
153 {
154 enumerator_t *enumerator;
155 guest_t *guest;
156
157 clear_template(this);
158
159 if (dir == NULL)
160 {
161 return TRUE;
162 }
163 if (strlen(dir) > PATH_MAX)
164 {
165 DBG1("template directory string '%s' is too long", dir);
166 return FALSE;
167 }
168
169 if (asprintf(&this->template, "%s/%s", TEMPLATE_DIR, dir) < 0)
170 {
171 this->template = NULL;
172 return FALSE;
173 }
174 if (access(this->template, F_OK) != 0)
175 { /* does not exist, create template */
176 if (!mkdir_p(this->template, PERME))
177 {
178 DBG1("creating template directory '%s' failed: %m", this->template);
179 return FALSE;
180 }
181 }
182 enumerator = this->guests->create_enumerator(this->guests);
183 while (enumerator->enumerate(enumerator, (void**)&guest))
184 {
185 if (!guest->load_template(guest, this->template))
186 {
187 enumerator->destroy(enumerator);
188 clear_template(this);
189 return FALSE;
190 }
191 }
192 enumerator->destroy(enumerator);
193 return TRUE;
194 }
195
196 /**
197 * Template directory enumerator
198 */
199 typedef struct {
200 /** implements enumerator_t */
201 enumerator_t public;
202 /** directory enumerator */
203 enumerator_t *inner;
204 } template_enumerator_t;
205
206 /**
207 * Implementation of template_enumerator_t.enumerate
208 */
209 static bool template_enumerate(template_enumerator_t *this, char **template)
210 {
211 struct stat st;
212 char *rel;
213
214 while (this->inner->enumerate(this->inner, &rel, NULL, &st))
215 {
216 if (S_ISDIR(st.st_mode) && *rel != '.')
217 {
218 *template = rel;
219 return TRUE;
220 }
221 }
222 return FALSE;
223 }
224
225 /**
226 * Implementation of template_enumerator_t.destroy
227 */
228 static void template_enumerator_destroy(template_enumerator_t *this)
229 {
230 this->inner->destroy(this->inner);
231 free(this);
232 }
233
234 /**
235 * Implementation of dumm_t.create_template_enumerator
236 */
237 static enumerator_t* create_template_enumerator(private_dumm_t *this)
238 {
239 template_enumerator_t *enumerator;
240
241 enumerator = malloc_thing(template_enumerator_t);
242 enumerator->public.enumerate = (void*)template_enumerate;
243 enumerator->public.destroy = (void*)template_enumerator_destroy;
244 enumerator->inner = enumerator_create_directory(TEMPLATE_DIR);
245
246 return &enumerator->public;
247 }
248
249 /**
250 * Implementation of dumm_t.destroy
251 */
252 static void destroy(private_dumm_t *this)
253 {
254 enumerator_t *enumerator;
255 guest_t *guest;
256
257 this->bridges->destroy_offset(this->bridges, offsetof(bridge_t, destroy));
258
259 enumerator = this->guests->create_enumerator(this->guests);
260 while (enumerator->enumerate(enumerator, (void**)&guest))
261 {
262 guest->stop(guest, NULL);
263 }
264 enumerator->destroy(enumerator);
265
266 while (this->guests->remove_last(this->guests, (void**)&guest) == SUCCESS)
267 {
268 guest->destroy(guest);
269 }
270 this->guests->destroy(this->guests);
271 free(this->guest_dir);
272 free(this->template);
273 free(this->dir);
274 free(this);
275 }
276
277 /**
278 * load all guests in our working dir
279 */
280 static void load_guests(private_dumm_t *this)
281 {
282 DIR *dir;
283 struct dirent *ent;
284 guest_t *guest;
285
286 dir = opendir(this->guest_dir);
287 if (dir == NULL)
288 {
289 return;
290 }
291
292 while ((ent = readdir(dir)))
293 {
294 if (*ent->d_name == '.')
295 { /* skip ".", ".." and hidden files (such as ".svn") */
296 continue;
297 }
298 guest = guest_load(this->guest_dir, ent->d_name);
299 if (guest)
300 {
301 this->guests->insert_last(this->guests, guest);
302 }
303 else
304 {
305 DBG1("loading guest in directory '%s' failed, skipped", ent->d_name);
306 }
307 }
308 closedir(dir);
309 }
310
311 /**
312 * create a dumm instance
313 */
314 dumm_t *dumm_create(char *dir)
315 {
316 char cwd[PATH_MAX];
317 private_dumm_t *this = malloc_thing(private_dumm_t);
318
319 this->public.create_guest = (guest_t*(*)(dumm_t*,char*,char*,char*,char*))create_guest;
320 this->public.create_guest_enumerator = (enumerator_t*(*)(dumm_t*))create_guest_enumerator;
321 this->public.delete_guest = (void(*)(dumm_t*,guest_t*))delete_guest;
322 this->public.create_bridge = (bridge_t*(*)(dumm_t*, char *name))create_bridge;
323 this->public.create_bridge_enumerator = (enumerator_t*(*)(dumm_t*))create_bridge_enumerator;
324 this->public.delete_bridge = (void(*)(dumm_t*,bridge_t*))delete_bridge;
325 this->public.load_template = (bool(*)(dumm_t*, char *name))load_template;
326 this->public.create_template_enumerator = (enumerator_t*(*)(dumm_t*))create_template_enumerator;
327 this->public.destroy = (void(*)(dumm_t*))destroy;
328
329 if (dir && *dir == '/')
330 {
331 this->dir = strdup(dir);
332 }
333 else
334 {
335 if (getcwd(cwd, sizeof(cwd)) == NULL)
336 {
337 free(this);
338 return NULL;
339 }
340 if (dir)
341 {
342 if (asprintf(&this->dir, "%s/%s", cwd, dir) < 0)
343 {
344 this->dir = NULL;
345 }
346 }
347 else
348 {
349 this->dir = strdup(cwd);
350 }
351 }
352 this->template = NULL;
353 if (asprintf(&this->guest_dir, "%s/%s", this->dir, GUEST_DIR) < 0)
354 {
355 this->guest_dir = NULL;
356 }
357 this->guests = linked_list_create();
358 this->bridges = linked_list_create();
359
360 if (this->dir == NULL || this->guest_dir == NULL ||
361 (mkdir(this->guest_dir, PERME) < 0 && errno != EEXIST))
362 {
363 DBG1("creating guest directory '%s' failed: %m", this->guest_dir);
364 destroy(this);
365 return NULL;
366 }
367
368 load_guests(this);
369 return &this->public;
370 }
371