784891835066cf1b7fb4b9b9f1cc8509a420008c
[strongswan.git] / src / dumm / guest.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 <sys/uio.h>
23 #include <sys/types.h>
24 #include <fcntl.h>
25 #include <signal.h>
26
27 #include <debug.h>
28 #include <utils/linked_list.h>
29
30 #include "dumm.h"
31 #include "guest.h"
32 #include "mconsole.h"
33
34 typedef struct private_guest_t private_guest_t;
35
36 struct private_guest_t {
37 /** implemented public interface */
38 guest_t public;
39 /** name of the guest */
40 char *name;
41 /** read only master filesystem guest uses */
42 char *master;
43 /** amount of memory for guest, in MB */
44 int mem;
45 /** pid of guest child process */
46 int pid;
47 /** state of guest */
48 guest_state_t state;
49 /** log file for console 0 */
50 int bootlog;
51 /** mconsole to control running UML */
52 mconsole_t *mconsole;
53 /** list of interfaces attached to the guest */
54 linked_list_t *ifaces;
55 };
56
57 ENUM(guest_state_names, GUEST_STOPPED, GUEST_STOPPING,
58 "STOPPED",
59 "STARTING",
60 "RUNNING",
61 "PAUSED",
62 "STOPPING",
63 );
64
65 /**
66 * Implementation of guest_t.get_name.
67 */
68 static char* get_name(private_guest_t *this)
69 {
70 return this->name;
71 }
72
73 /**
74 * Implementation of guest_t.create_iface.
75 */
76 static iface_t* create_iface(private_guest_t *this, char *name)
77 {
78 iterator_t *iterator;
79 iface_t *iface;
80
81 if (this->state != GUEST_RUNNING)
82 {
83 DBG1("guest '%s' not running, unable to add interface", this->name);
84 return NULL;
85 }
86
87 iterator = this->ifaces->create_iterator(this->ifaces, TRUE);
88 while (iterator->iterate(iterator, (void**)&iface))
89 {
90 if (streq(name, iface->get_guestif(iface)))
91 {
92 DBG1("guest '%s' already has an interface '%s'", this->name, name);
93 iterator->destroy(iterator);
94 return NULL;
95 }
96 }
97 iterator->destroy(iterator);
98
99 iface = iface_create(this->name, name, this->mconsole);
100 if (iface)
101 {
102 this->ifaces->insert_last(this->ifaces, iface);
103 }
104 return iface;
105 }
106
107 /**
108 * Implementation of guest_t.create_iface_iterator.
109 */
110 static iterator_t* create_iface_iterator(private_guest_t *this)
111 {
112 return this->ifaces->create_iterator(this->ifaces, TRUE);
113 }
114
115 /**
116 * Implementation of guest_t.get_state.
117 */
118 static guest_state_t get_state(private_guest_t *this)
119 {
120 return this->state;
121 }
122
123 /**
124 * Implementation of guest_t.get_pid.
125 */
126 static pid_t get_pid(private_guest_t *this)
127 {
128 return this->pid;
129 }
130
131 /**
132 * write format string to a buffer, and advance buffer position
133 */
134 static char* write_arg(char **pos, size_t *left, char *format, ...)
135 {
136 size_t len;
137 char *res = NULL;
138 va_list args;
139
140 va_start(args, format);
141 len = vsnprintf(*pos, *left, format, args);
142 va_end(args);
143 if (len < *left)
144 {
145 res = *pos;
146 len++;
147 *pos += len + 1;
148 *left -= len + 1;
149 }
150 return res;
151 }
152
153 /**
154 * Implementation of guest_t.start.
155 */
156 static bool start(private_guest_t *this, char *kernel)
157 {
158 char buf[1024];
159 char cwd[512];
160 char *notify;
161 char *pos = buf;
162 char *args[16];
163 int i = 0;
164 size_t left = sizeof(buf);
165
166 if (this->state != GUEST_STOPPED)
167 {
168 DBG1("unable to start guest in state %N", guest_state_names, this->state);
169 return FALSE;
170 }
171 this->state = GUEST_STARTING;
172
173 notify = write_arg(&pos, &left, "%s/%s/notify", RUN_DIR, this->name);
174
175 args[i++] = kernel;
176 args[i++] = write_arg(&pos, &left, "root=/dev/root");
177 args[i++] = write_arg(&pos, &left, "rootfstype=hostfs");
178 args[i++] = write_arg(&pos, &left, "rootflags=%s/%s/%s",
179 getcwd(cwd, sizeof(cwd)), MOUNT_DIR, this->name);
180 args[i++] = write_arg(&pos, &left, "uml_dir=%s/%s", RUN_DIR, this->name);
181 args[i++] = write_arg(&pos, &left, "umid=%s", this->name);
182 args[i++] = write_arg(&pos, &left, "mem=%dM", this->mem);
183 args[i++] = write_arg(&pos, &left, "mconsole=notify:%s", notify);
184 /*args[i++] = write_arg(&pos, &left, "con=pts");*/
185 args[i++] = write_arg(&pos, &left, "con0=null,fd:%d", this->bootlog);
186 /*args[i++] = write_arg(&pos, &left, "con1=fd:0,fd:1");*/
187 args[i++] = write_arg(&pos, &left, "con2=null,null");
188 args[i++] = write_arg(&pos, &left, "con3=null,null");
189 args[i++] = write_arg(&pos, &left, "con4=null,null");
190 args[i++] = write_arg(&pos, &left, "con5=null,null");
191 args[i++] = write_arg(&pos, &left, "con6=null,null");
192 args[i++] = NULL;
193
194 this->pid = fork();
195 switch (this->pid)
196 {
197 case 0: /* child, */
198 dup2(open("/dev/null", 0), 0);
199 dup2(open("/dev/null", 0), 1);
200 dup2(open("/dev/null", 0), 2);
201 execvp(args[0], args);
202 DBG1("starting UML kernel '%s' failed", args[0]);
203 exit(1);
204 case -1:
205 this->pid = 0;
206 return FALSE;
207 default:
208 break;
209 }
210 /* open mconsole */
211 this->mconsole = mconsole_create(notify);
212 if (this->mconsole == NULL)
213 {
214 DBG1("opening mconsole at '%s' failed, stopping guest", buf);
215 kill(this->pid, SIGINT);
216 this->pid = 0;
217 return FALSE;
218 }
219 this->state = GUEST_RUNNING;
220 return TRUE;
221 }
222
223 /**
224 * Implementation of guest_t.stop.
225 */
226 static void stop(private_guest_t *this)
227 {
228 if (this->state != GUEST_STOPPED)
229 {
230 this->state = GUEST_STOPPING;
231 this->ifaces->destroy_offset(this->ifaces, offsetof(iface_t, destroy));
232 this->ifaces = linked_list_create();
233 kill(this->pid, SIGINT);
234 while (this->state == GUEST_STOPPING)
235 {
236 sched_yield();
237 }
238 }
239 }
240
241 /**
242 * Implementation of guest_t.sigchild.
243 */
244 static void sigchild(private_guest_t *this)
245 {
246 DESTROY_IF(this->mconsole);
247 this->mconsole = NULL;
248 this->state = GUEST_STOPPED;
249 this->pid = 0;
250 }
251
252 /**
253 * Check if directory exists, create otherwise
254 */
255 static bool makedir(char *dir, char *name)
256 {
257 struct stat st;
258 char buf[256];
259 size_t len;
260
261 len = snprintf(buf, sizeof(buf), "%s/%s", dir, name);
262 if (len < 0 || len >= sizeof(buf))
263 {
264 return FALSE;
265 }
266 if (stat(buf, &st) != 0)
267 {
268 return mkdir(buf, S_IRWXU) == 0;
269 }
270 return S_ISDIR(st.st_mode);
271 }
272
273 /**
274 * umount the union filesystem
275 */
276 static bool umount_unionfs(char *name)
277 {
278 char cmd[128];
279 size_t len;
280
281 len = snprintf(cmd, sizeof(cmd), "fusermount -u %s/%s", MOUNT_DIR, name);
282 if (len < 0 || len >= sizeof(cmd))
283 {
284 return FALSE;
285 }
286 if (system(cmd) != 0)
287 {
288 DBG1("unmounting guest unionfs for %s failed", name);
289 return FALSE;
290 }
291 return TRUE;
292 }
293
294 /**
295 * mount the union filesystem
296 */
297 static bool mount_unionfs(char *name, char *master)
298 {
299 char cmd[256];
300 size_t len;
301
302 len = snprintf(cmd, sizeof(cmd), "unionfs %s/%s:%s %s/%s",
303 HOST_DIR, name, master, MOUNT_DIR, name);
304 if (len < 0 || len >= sizeof(cmd))
305 {
306 return FALSE;
307 }
308 if (system(cmd) != 0)
309 {
310 DBG1("mounting guest unionfs for %s using '%s' failed", name, cmd);
311 return FALSE;
312 }
313 return TRUE;
314 }
315
316 /**
317 * open logfile for boot messages
318 */
319 static int open_bootlog(char *name)
320 {
321 char blg[256];
322 size_t len;
323 int fd;
324
325 len = snprintf(blg, sizeof(blg), "%s/%s/boot.log", RUN_DIR, name);
326 if (len < 0 || len >= sizeof(blg))
327 {
328 return 1;
329 }
330 fd = open(blg, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR);
331 if (fd == -1)
332 {
333 DBG1("opening bootlog '%s' for %s failed, using stdout", blg, name);
334 return 1;
335 }
336 return fd;
337 }
338
339 /**
340 * Implementation of guest_t.destroy.
341 */
342 static void destroy(private_guest_t *this)
343 {
344 stop(this);
345 umount_unionfs(this->name);
346 free(this->name);
347 free(this->master);
348 free(this);
349 }
350
351 /**
352 * create the guest instance, including required dirs and mounts
353 */
354 guest_t *guest_create(char *name, char *master, int mem)
355 {
356 private_guest_t *this = malloc_thing(private_guest_t);
357
358 this->public.get_name = (void*)get_name;
359 this->public.get_pid = (pid_t(*)(guest_t*))get_pid;
360 this->public.get_state = (guest_state_t(*)(guest_t*))get_state;
361 this->public.create_iface = (iface_t*(*)(guest_t*,char*))create_iface;
362 this->public.create_iface_iterator = (iterator_t*(*)(guest_t*))create_iface_iterator;
363 this->public.start = (void*)start;
364 this->public.stop = (void*)stop;
365 this->public.sigchild = (void(*)(guest_t*))sigchild;
366 this->public.destroy = (void*)destroy;
367
368 if (!makedir(HOST_DIR, name) || !makedir(MOUNT_DIR, name) ||
369 !makedir(RUN_DIR, name) || !mount_unionfs(name, master))
370 {
371 free(this);
372 return NULL;
373 }
374
375 this->name = strdup(name);
376 this->master = strdup(master);
377 this->mem = mem;
378 this->pid = 0;
379 this->state = GUEST_STOPPED;
380 this->bootlog = open_bootlog(name);
381 this->mconsole = NULL;
382 this->ifaces = linked_list_create();
383
384 return &this->public;
385 }
386