usable without scenarios
[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 static void clear_scenario(private_dumm_t *this)
109 {
110 iterator_t *iterator;
111 guest_t *guest;
112
113 free(this->scenario);
114 this->scenario = NULL;
115
116 iterator = this->guests->create_iterator(this->guests, TRUE);
117 while (iterator->iterate(iterator, (void**)&guest))
118 {
119 guest->set_scenario(guest, NULL);
120 }
121 iterator->destroy(iterator);
122 }
123
124 /**
125 * Implementation of dumm_t.load_scenario.
126 */
127 static bool load_scenario(private_dumm_t *this, char *name)
128 {
129 iterator_t *iterator;
130 guest_t *guest;
131 char dir[PATH_MAX];
132 size_t len;
133
134 free(this->scenario);
135 asprintf(&this->scenario, "%s/%s", this->scenario_dir, name);
136
137 if (access(this->scenario, F_OK) == 0)
138 { /* exists, load scenario */
139 DBG1("scenario loading unimplemented.");
140 return FALSE;
141 }
142 else
143 { /* does not exist, create scenario */
144 if (mkdir(this->scenario, PERME) != 0)
145 {
146 DBG1("creating scenario directory '%s' failed: %m", this->scenario);
147 clear_scenario(this);
148 return FALSE;
149 }
150 len = snprintf(dir, sizeof(dir), "%s/%s/%s", this->scenario,
151 SCENARIO_DIR, SCENARIO_DIFF_DIR);
152 if (len < 0 || len >= sizeof(dir))
153 {
154 clear_scenario(this);
155 return FALSE;
156 }
157 iterator = this->guests->create_iterator(this->guests, TRUE);
158 while (iterator->iterate(iterator, (void**)&guest))
159 {
160 if (!guest->set_scenario(guest, dir))
161 {
162 iterator->destroy(iterator);
163 clear_scenario(this);
164 return FALSE;
165 }
166 }
167 iterator->destroy(iterator);
168 }
169 return TRUE;
170 }
171
172 /**
173 * Implementation of dumm_t.save_scenario.
174 */
175 static bool save_scenario(private_dumm_t *this)
176 {
177 DBG1("scenario loading unimplemented.");
178 return FALSE;
179 }
180
181 /**
182 * signal handler
183 */
184 void signal_handler(int sig, siginfo_t *info, void *ucontext)
185 {
186 if (sig == SIGCHLD)
187 {
188 switch (info->si_code)
189 {
190 case CLD_EXITED:
191 case CLD_KILLED:
192 case CLD_DUMPED:
193 {
194 private_dumm_t *this;
195 guest_t *guest;
196 iterator_t *iterator, *guests;
197
198 iterator = instances->create_iterator(instances, TRUE);
199 while (iterator->iterate(iterator, (void**)&this))
200 {
201 if (this->destroying)
202 {
203 continue;
204 }
205 guests = this->guests->create_iterator(this->guests, TRUE);
206 while (guests->iterate(guests, (void**)&guest))
207 {
208 if (guest->get_pid(guest) == info->si_pid)
209 {
210 guest->sigchild(guest);
211 break;
212 }
213 }
214 guests->destroy(guests);
215 }
216 iterator->destroy(iterator);
217 break;
218 }
219 default:
220 break;
221 }
222
223 }
224 /* SIGHUP is currently just ignored */
225 }
226
227 /**
228 * add a dumm instance
229 */
230 static void add_instance(private_dumm_t *this)
231 {
232 if (instances == NULL)
233 {
234 struct sigaction action;
235
236 instances = linked_list_create();
237
238 memset(&action, 0, sizeof(action));
239 action.sa_sigaction = signal_handler;
240 action.sa_flags = SA_SIGINFO;
241
242 if (sigaction(SIGCHLD, &action, NULL) != 0 ||
243 sigaction(SIGHUP, &action, NULL) != 0)
244 {
245 DBG1("installing signal handler failed!");
246 }
247 }
248 instances->insert_last(instances, this);
249 }
250
251 /**
252 * remove a dumm instance
253 */
254 static void remove_instance(private_dumm_t *this)
255 {
256 iterator_t *iterator;
257 private_dumm_t *current;
258
259 iterator = instances->create_iterator(instances, TRUE);
260 while (iterator->iterate(iterator, (void**)&current))
261 {
262 if (current == this)
263 {
264 iterator->remove(iterator);
265 break;
266 }
267 }
268 iterator->destroy(iterator);
269 if (instances->get_count(instances) == 0)
270 {
271 instances->destroy(instances);
272 instances = NULL;
273 }
274 }
275
276 /**
277 * Implementation of dumm_t.destroy
278 */
279 static void destroy(private_dumm_t *this)
280 {
281 iterator_t *iterator;
282 guest_t *guest;
283
284 this->bridges->destroy_offset(this->bridges, offsetof(bridge_t, destroy));
285
286 iterator = this->guests->create_iterator(this->guests, TRUE);
287 while (iterator->iterate(iterator, (void**)&guest))
288 {
289 guest->stop(guest);
290 }
291 iterator->destroy(iterator);
292
293 this->destroying = TRUE;
294 this->guests->destroy_offset(this->guests, offsetof(guest_t, destroy));
295 free(this->guest_dir);
296 free(this->scenario_dir);
297 free(this->scenario);
298 free(this->dir);
299 remove_instance(this);
300 free(this);
301 }
302
303 /**
304 * load all guests in our working dir
305 */
306 static void load_guests(private_dumm_t *this)
307 {
308 DIR *dir;
309 struct dirent *ent;
310 guest_t *guest;
311
312 dir = opendir(this->guest_dir);
313 if (dir == NULL)
314 {
315 return;
316 }
317
318 while ((ent = readdir(dir)))
319 {
320 if (streq(ent->d_name, ".") || streq(ent->d_name, ".."))
321 {
322 continue;
323 }
324 guest = guest_load(this->guest_dir, ent->d_name);
325 if (guest)
326 {
327 DBG1("loaded guest '%s'", ent->d_name);
328 this->guests->insert_last(this->guests, guest);
329 }
330 else
331 {
332 DBG1("loading guest in directory '%s' failed, skipped", ent->d_name);
333 }
334 }
335 closedir(dir);
336 }
337
338 /**
339 * create a dumm instance
340 */
341 dumm_t *dumm_create(char *dir)
342 {
343 char cwd[PATH_MAX];
344 private_dumm_t *this = malloc_thing(private_dumm_t);
345
346 this->public.create_guest = (guest_t*(*)(dumm_t*,char*,char*,char*,int))create_guest;
347 this->public.create_guest_iterator = (iterator_t*(*)(dumm_t*))create_guest_iterator;
348 this->public.create_bridge = (bridge_t*(*)(dumm_t*, char *name))create_bridge;
349 this->public.create_bridge_iterator = (iterator_t*(*)(dumm_t*))create_bridge_iterator;
350 this->public.load_scenario = (bool(*)(dumm_t*, char *name))load_scenario;
351 this->public.save_scenario = (bool(*)(dumm_t*))save_scenario;
352 this->public.destroy = (void(*)(dumm_t*))destroy;
353
354 this->destroying = FALSE;
355 if (*dir == '/' || getcwd(cwd, sizeof(cwd)) == 0)
356 {
357 this->dir = strdup(dir);
358 }
359 else
360 {
361 asprintf(&this->dir, "%s/%s", cwd, dir);
362 }
363 this->scenario = NULL;
364 asprintf(&this->guest_dir, "%s/%s", this->dir, GUEST_DIR);
365 asprintf(&this->scenario_dir, "%s/%s", this->dir, SCENARIO_DIR);
366 this->guests = linked_list_create();
367 this->bridges = linked_list_create();
368
369 add_instance(this);
370
371 if (mkdir(this->guest_dir, PERME) < 0 && errno != EEXIST)
372 {
373 DBG1("creating guest directory '%s' failed: %m", this->guest_dir);
374 destroy(this);
375 return NULL;
376 }
377
378 load_guests(this);
379 return &this->public;
380 }
381