fixed scenario loading
[strongswan.git] / src / dumm / dumm.c
1 /*
2 * Copyright (C) 2007 Martin Willi
3 * Hochschule fuer Technik Rapperswil
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
18 #include <sys/types.h>
19 #include <sys/stat.h>
20 #include <unistd.h>
21 #include <stdio.h>
22 #include <dirent.h>
23 #include <errno.h>
24 #include <libxml/xmlreader.h>
25 #include <libxml/xmlwriter.h>
26
27 #include <debug.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 #define TEMPLATE_DIR_DIR "diff"
35
36 /**
37 * instances of dumm, used to deliver signals
38 */
39 static linked_list_t *instances = NULL;
40
41 typedef struct private_dumm_t private_dumm_t;
42
43 struct private_dumm_t {
44 /** public dumm interface */
45 dumm_t public;
46 /** working dir */
47 char *dir;
48 /** directory of guests */
49 char *guest_dir;
50 /** directory of templates */
51 char *template_dir;
52 /** directory of loaded template */
53 char *template;
54 /** list of managed guests */
55 linked_list_t *guests;
56 /** list of managed bridges */
57 linked_list_t *bridges;
58 /** do not catch signals if we are destroying */
59 bool destroying;
60 };
61
62 /**
63 * Implementation of dumm_t.create_guest.
64 */
65 static guest_t* create_guest(private_dumm_t *this, char *name, char *kernel,
66 char *master, int mem)
67 {
68 guest_t *guest;
69
70 guest = guest_create(this->guest_dir, name, kernel, master, mem);
71 if (guest)
72 {
73 this->guests->insert_last(this->guests, guest);
74 }
75 return guest;
76 }
77
78 /**
79 * Implementation of dumm_t.create_guest_iterator.
80 */
81 static iterator_t* create_guest_iterator(private_dumm_t *this)
82 {
83 return this->guests->create_iterator(this->guests, TRUE);
84 }
85
86 /**
87 * Implementation of dumm_t.create_bridge.
88 */
89 static bridge_t* create_bridge(private_dumm_t *this, char *name)
90 {
91 bridge_t *bridge;
92
93 bridge = bridge_create(name);
94 if (bridge)
95 {
96 this->bridges->insert_last(this->bridges, bridge);
97 }
98 return bridge;
99 }
100
101 /**
102 * Implementation of dumm_t.create_bridge_iterator.
103 */
104 static iterator_t* create_bridge_iterator(private_dumm_t *this)
105 {
106 return this->bridges->create_iterator(this->bridges, TRUE);
107 }
108
109 /**
110 * disable the currently enabled template
111 */
112 static void clear_template(private_dumm_t *this)
113 {
114 iterator_t *iterator, *ifaces;
115 guest_t *guest;
116 iface_t *iface;
117
118 free(this->template);
119 this->template = NULL;
120
121 iterator = this->guests->create_iterator(this->guests, TRUE);
122 while (iterator->iterate(iterator, (void**)&guest))
123 {
124 guest->load_template(guest, NULL);
125 ifaces = guest->create_iface_iterator(guest);
126 while (ifaces->iterate(ifaces, (void**)&iface))
127 {
128 ifaces->remove(ifaces);
129 iface->destroy(iface);
130 }
131 ifaces->destroy(ifaces);
132 }
133 iterator->destroy(iterator);
134 }
135
136 /**
137 * Implementation of dumm_t.load_template.
138 */
139 static bool load_template(private_dumm_t *this, char *name)
140 {
141 iterator_t *iterator;
142 guest_t *guest;
143 char dir[PATH_MAX];
144 size_t len;
145
146 clear_template(this);
147
148 if (name == NULL)
149 {
150 return TRUE;
151 }
152
153 free(this->template);
154 asprintf(&this->template, "%s/%s", this->template_dir, name);
155 len = snprintf(dir, sizeof(dir), "%s/%s", this->template, TEMPLATE_DIR_DIR);
156 if (len < 0 || len >= sizeof(dir))
157 {
158 return FALSE;
159 }
160
161 if (access(this->template, F_OK) != 0)
162 { /* does not exist, create template */
163 if (mkdir(this->template, PERME) != 0)
164 {
165 DBG1("creating template directory '%s' failed: %m", this->template);
166 return FALSE;
167 }
168 if (mkdir(dir, PERME) != 0)
169 {
170 DBG1("creating template overlay directory '%s' failed: %m", dir);
171 return FALSE;
172 }
173 }
174 iterator = this->guests->create_iterator(this->guests, TRUE);
175 while (iterator->iterate(iterator, (void**)&guest))
176 {
177 if (!guest->load_template(guest, dir))
178 {
179 iterator->destroy(iterator);
180 clear_template(this);
181 return FALSE;
182 }
183 }
184 iterator->destroy(iterator);
185 return TRUE;
186 }
187
188 /**
189 * signal handler
190 */
191 void signal_handler(int sig, siginfo_t *info, void *ucontext)
192 {
193 if (sig == SIGCHLD)
194 {
195 switch (info->si_code)
196 {
197 case CLD_EXITED:
198 case CLD_KILLED:
199 case CLD_DUMPED:
200 {
201 private_dumm_t *this;
202 guest_t *guest;
203 iterator_t *iterator, *guests;
204
205 iterator = instances->create_iterator(instances, TRUE);
206 while (iterator->iterate(iterator, (void**)&this))
207 {
208 if (this->destroying)
209 {
210 continue;
211 }
212 guests = this->guests->create_iterator(this->guests, TRUE);
213 while (guests->iterate(guests, (void**)&guest))
214 {
215 if (guest->get_pid(guest) == info->si_pid)
216 {
217 guest->sigchild(guest);
218 break;
219 }
220 }
221 guests->destroy(guests);
222 }
223 iterator->destroy(iterator);
224 break;
225 }
226 default:
227 break;
228 }
229
230 }
231 /* SIGHUP is currently just ignored */
232 }
233
234 /**
235 * add a dumm instance
236 */
237 static void add_instance(private_dumm_t *this)
238 {
239 if (instances == NULL)
240 {
241 struct sigaction action;
242
243 instances = linked_list_create();
244
245 memset(&action, 0, sizeof(action));
246 action.sa_sigaction = signal_handler;
247 action.sa_flags = SA_SIGINFO;
248
249 if (sigaction(SIGCHLD, &action, NULL) != 0 ||
250 sigaction(SIGHUP, &action, NULL) != 0)
251 {
252 DBG1("installing signal handler failed!");
253 }
254 }
255 instances->insert_last(instances, this);
256 }
257
258 /**
259 * remove a dumm instance
260 */
261 static void remove_instance(private_dumm_t *this)
262 {
263 iterator_t *iterator;
264 private_dumm_t *current;
265
266 iterator = instances->create_iterator(instances, TRUE);
267 while (iterator->iterate(iterator, (void**)&current))
268 {
269 if (current == this)
270 {
271 iterator->remove(iterator);
272 break;
273 }
274 }
275 iterator->destroy(iterator);
276 if (instances->get_count(instances) == 0)
277 {
278 instances->destroy(instances);
279 instances = NULL;
280 }
281 }
282
283 /**
284 * Implementation of dumm_t.destroy
285 */
286 static void destroy(private_dumm_t *this)
287 {
288 iterator_t *iterator;
289 guest_t *guest;
290
291 this->bridges->destroy_offset(this->bridges, offsetof(bridge_t, destroy));
292
293 iterator = this->guests->create_iterator(this->guests, TRUE);
294 while (iterator->iterate(iterator, (void**)&guest))
295 {
296 guest->stop(guest);
297 }
298 iterator->destroy(iterator);
299
300 this->destroying = TRUE;
301 this->guests->destroy_offset(this->guests, offsetof(guest_t, destroy));
302 free(this->guest_dir);
303 free(this->template_dir);
304 free(this->template);
305 free(this->dir);
306 remove_instance(this);
307 free(this);
308 }
309
310 /**
311 * load all guests in our working dir
312 */
313 static void load_guests(private_dumm_t *this)
314 {
315 DIR *dir;
316 struct dirent *ent;
317 guest_t *guest;
318
319 dir = opendir(this->guest_dir);
320 if (dir == NULL)
321 {
322 return;
323 }
324
325 while ((ent = readdir(dir)))
326 {
327 if (streq(ent->d_name, ".") || streq(ent->d_name, ".."))
328 {
329 continue;
330 }
331 guest = guest_load(this->guest_dir, ent->d_name);
332 if (guest)
333 {
334 DBG1("loaded guest '%s'", ent->d_name);
335 this->guests->insert_last(this->guests, guest);
336 }
337 else
338 {
339 DBG1("loading guest in directory '%s' failed, skipped", ent->d_name);
340 }
341 }
342 closedir(dir);
343 }
344
345 /**
346 * create a dumm instance
347 */
348 dumm_t *dumm_create(char *dir)
349 {
350 char cwd[PATH_MAX];
351 private_dumm_t *this = malloc_thing(private_dumm_t);
352
353 this->public.create_guest = (guest_t*(*)(dumm_t*,char*,char*,char*,int))create_guest;
354 this->public.create_guest_iterator = (iterator_t*(*)(dumm_t*))create_guest_iterator;
355 this->public.create_bridge = (bridge_t*(*)(dumm_t*, char *name))create_bridge;
356 this->public.create_bridge_iterator = (iterator_t*(*)(dumm_t*))create_bridge_iterator;
357 this->public.load_template = (bool(*)(dumm_t*, char *name))load_template;
358 this->public.destroy = (void(*)(dumm_t*))destroy;
359
360 this->destroying = FALSE;
361 if (*dir == '/' || getcwd(cwd, sizeof(cwd)) == 0)
362 {
363 this->dir = strdup(dir);
364 }
365 else
366 {
367 asprintf(&this->dir, "%s/%s", cwd, dir);
368 }
369 this->template = NULL;
370 asprintf(&this->guest_dir, "%s/%s", this->dir, GUEST_DIR);
371 asprintf(&this->template_dir, "%s/%s", this->dir, TEMPLATE_DIR);
372 this->guests = linked_list_create();
373 this->bridges = linked_list_create();
374
375 add_instance(this);
376
377 if (mkdir(this->guest_dir, PERME) < 0 && errno != EEXIST)
378 {
379 DBG1("creating guest directory '%s' failed: %m", this->guest_dir);
380 destroy(this);
381 return NULL;
382 }
383 if (mkdir(this->template_dir, PERME) < 0 && errno != EEXIST)
384 {
385 DBG1("creating template directory '%s' failed: %m", this->template_dir);
386 destroy(this);
387 return NULL;
388 }
389
390 load_guests(this);
391 return &this->public;
392 }
393