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