proper shutdown
[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 <unistd.h>
20 #include <stdio.h>
21 #include <dirent.h>
22
23 #include <debug.h>
24
25 #include "dumm.h"
26
27 /**
28 * instances of dumm, used to deliver signals
29 */
30 static linked_list_t *instances = NULL;
31
32 typedef struct private_dumm_t private_dumm_t;
33
34 struct private_dumm_t {
35 /** public dumm interface */
36 dumm_t public;
37 /** working dir */
38 char *dir;
39 /** list of managed guests */
40 linked_list_t *guests;
41 /** list of managed bridges */
42 linked_list_t *bridges;
43 /** do not catch signals if we are destroying */
44 bool destroying;
45 };
46
47 /**
48 * Implementation of dumm_t.create_guest.
49 */
50 static guest_t* create_guest(private_dumm_t *this, char *name, char *kernel,
51 char *master, int mem)
52 {
53 guest_t *guest;
54
55 guest = guest_create(this->dir, name, kernel, master, mem);
56 if (guest)
57 {
58 this->guests->insert_last(this->guests, guest);
59 }
60 return guest;
61 }
62
63 /**
64 * Implementation of dumm_t.create_guest_iterator.
65 */
66 static iterator_t* create_guest_iterator(private_dumm_t *this)
67 {
68 return this->guests->create_iterator(this->guests, TRUE);
69 }
70
71 /**
72 * Implementation of dumm_t.create_bridge.
73 */
74 static bridge_t* create_bridge(private_dumm_t *this, char *name)
75 {
76 bridge_t *bridge;
77
78 bridge = bridge_create(name);
79 if (bridge)
80 {
81 this->bridges->insert_last(this->bridges, bridge);
82 }
83 return bridge;
84 }
85
86 /**
87 * Implementation of dumm_t.create_bridge_iterator.
88 */
89 static iterator_t* create_bridge_iterator(private_dumm_t *this)
90 {
91 return this->bridges->create_iterator(this->bridges, TRUE);
92 }
93
94 /**
95 * signal handler
96 */
97 void signal_handler(int sig, siginfo_t *info, void *ucontext)
98 {
99 private_dumm_t *this;
100 guest_t *guest;
101 iterator_t *iterator, *guests;
102
103 if (sig == SIGCHLD)
104 {
105 iterator = instances->create_iterator(instances, TRUE);
106 while (iterator->iterate(iterator, (void**)&this))
107 {
108 if (this->destroying)
109 {
110 continue;
111 }
112 switch (info->si_code)
113 {
114 case CLD_EXITED:
115 case CLD_KILLED:
116 case CLD_DUMPED:
117 {
118 guests = this->guests->create_iterator(this->guests, TRUE);
119 while (guests->iterate(guests, (void**)&guest))
120 {
121 if (guest->get_pid(guest) == info->si_pid)
122 {
123 guest->sigchild(guest);
124 break;
125 }
126 }
127 guests->destroy(guests);
128 break;
129 }
130 default:
131 break;
132 }
133 }
134 iterator->destroy(iterator);
135 }
136 /* SIGHUP is currently just ignored */
137 }
138
139 /**
140 * add a dumm instance
141 */
142 static void add_instance(private_dumm_t *this)
143 {
144 if (instances == NULL)
145 {
146 struct sigaction action;
147
148 instances = linked_list_create();
149
150 memset(&action, 0, sizeof(action));
151 action.sa_sigaction = signal_handler;
152 action.sa_flags = SA_SIGINFO;
153
154 if (sigaction(SIGCHLD, &action, NULL) != 0 ||
155 sigaction(SIGHUP, &action, NULL) != 0)
156 {
157 DBG1("installing signal handler failed!");
158 }
159 }
160 instances->insert_last(instances, this);
161 }
162
163 /**
164 * remove a dumm instance
165 */
166 static void remove_instance(private_dumm_t *this)
167 {
168 iterator_t *iterator;
169 private_dumm_t *current;
170
171 iterator = instances->create_iterator(instances, TRUE);
172 while (iterator->iterate(iterator, (void**)&current))
173 {
174 if (current == this)
175 {
176 iterator->remove(iterator);
177 break;
178 }
179 }
180 iterator->destroy(iterator);
181 if (instances->get_count(instances) == 0)
182 {
183 instances->destroy(instances);
184 instances = NULL;
185 }
186 }
187
188 /**
189 * Implementation of dumm_t.destroy
190 */
191 static void destroy(private_dumm_t *this)
192 {
193 iterator_t *iterator;
194 guest_t *guest;
195
196 this->bridges->destroy_offset(this->bridges, offsetof(bridge_t, destroy));
197
198 iterator = this->guests->create_iterator(this->guests, TRUE);
199 while (iterator->iterate(iterator, (void**)&guest))
200 {
201 guest->stop(guest);
202 }
203 iterator->destroy(iterator);
204
205 this->destroying = TRUE;
206 this->guests->destroy_offset(this->guests, offsetof(guest_t, destroy));
207 free(this->dir);
208 remove_instance(this);
209 free(this);
210 }
211
212 /**
213 * load all guests in our working dir
214 */
215 static void load_guests(private_dumm_t *this)
216 {
217 DIR *dir;
218 struct dirent *ent;
219 guest_t *guest;
220
221 dir = opendir(this->dir);
222 if (dir == NULL)
223 {
224 return;
225 }
226
227 while ((ent = readdir(dir)))
228 {
229 if (streq(ent->d_name, ".") || streq(ent->d_name, ".."))
230 {
231 continue;
232 }
233 guest = guest_load(this->dir, ent->d_name);
234 if (guest)
235 {
236 this->guests->insert_last(this->guests, guest);
237 }
238 else
239 {
240 DBG1("loading guest in directory '%s' failed, skipped", ent->d_name);
241 }
242 }
243 closedir(dir);
244 }
245
246 /**
247 * create a dumm instance
248 */
249 dumm_t *dumm_create(char *dir)
250 {
251 char cwd[PATH_MAX];
252 private_dumm_t *this = malloc_thing(private_dumm_t);
253
254 this->public.create_guest = (guest_t*(*)(dumm_t*,char*,char*,char*,int))create_guest;
255 this->public.create_guest_iterator = (iterator_t*(*)(dumm_t*))create_guest_iterator;
256 this->public.create_bridge = (bridge_t*(*)(dumm_t*, char *name))create_bridge;
257 this->public.create_bridge_iterator = (iterator_t*(*)(dumm_t*))create_bridge_iterator;
258 this->public.destroy = (void(*)(dumm_t*))destroy;
259
260 this->destroying = FALSE;
261 if (*dir == '/' || getcwd(cwd, sizeof(cwd)) == 0)
262 {
263 this->dir = strdup(dir);
264 }
265 else
266 {
267 asprintf(&this->dir, "%s/%s", cwd, dir);
268 }
269 this->guests = linked_list_create();
270 this->bridges = linked_list_create();
271
272 add_instance(this);
273
274 load_guests(this);
275 return &this->public;
276 }
277