removed Makefile from svn
[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 <sys/wait.h>
21 #include <sys/uio.h>
22 #include <unistd.h>
23 #include <stdio.h>
24 #include <fcntl.h>
25 #include <signal.h>
26 #include <dirent.h>
27
28 #include <debug.h>
29 #include <utils/linked_list.h>
30
31 #include "dumm.h"
32 #include "guest.h"
33 #include "mconsole.h"
34 #include "cowfs.h"
35
36 #define PERME (S_IRWXU | S_IRWXG)
37 #define PERM (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP)
38
39 #define MASTER_DIR "master"
40 #define DIFF_DIR "diff"
41 #define UNION_DIR "union"
42 #define MEMORY_FILE "mem"
43 #define KERNEL_FILE "linux"
44 #define LOG_FILE "boot.log"
45 #define NOTIFY_FILE "notify"
46
47 typedef struct private_guest_t private_guest_t;
48
49 struct private_guest_t {
50 /** implemented public interface */
51 guest_t public;
52 /** name of the guest */
53 char *name;
54 /** directory of guest */
55 int dir;
56 /** directory name of guest */
57 char *dirname;
58 /** amount of memory for guest, in MB */
59 int mem;
60 /** pid of guest child process */
61 int pid;
62 /** state of guest */
63 guest_state_t state;
64 /** log file for console 0 */
65 int bootlog;
66 /** FUSE cowfs instance */
67 cowfs_t *cowfs;
68 /** mconsole to control running UML */
69 mconsole_t *mconsole;
70 /** list of interfaces attached to the guest */
71 linked_list_t *ifaces;
72 };
73
74 ENUM(guest_state_names, GUEST_STOPPED, GUEST_STOPPING,
75 "STOPPED",
76 "STARTING",
77 "RUNNING",
78 "PAUSED",
79 "STOPPING",
80 );
81
82 /**
83 * Implementation of guest_t.get_name.
84 */
85 static char* get_name(private_guest_t *this)
86 {
87 return this->name;
88 }
89
90 /**
91 * Implementation of guest_t.create_iface.
92 */
93 static iface_t* create_iface(private_guest_t *this, char *name)
94 {
95 iterator_t *iterator;
96 iface_t *iface;
97
98 if (this->state != GUEST_RUNNING)
99 {
100 DBG1("guest '%s' not running, unable to add interface", this->name);
101 return NULL;
102 }
103
104 iterator = this->ifaces->create_iterator(this->ifaces, TRUE);
105 while (iterator->iterate(iterator, (void**)&iface))
106 {
107 if (streq(name, iface->get_guestif(iface)))
108 {
109 DBG1("guest '%s' already has an interface '%s'", this->name, name);
110 iterator->destroy(iterator);
111 return NULL;
112 }
113 }
114 iterator->destroy(iterator);
115
116 iface = iface_create(this->name, name, this->mconsole);
117 if (iface)
118 {
119 this->ifaces->insert_last(this->ifaces, iface);
120 }
121 return iface;
122 }
123
124 /**
125 * Implementation of guest_t.create_iface_iterator.
126 */
127 static iterator_t* create_iface_iterator(private_guest_t *this)
128 {
129 return this->ifaces->create_iterator(this->ifaces, TRUE);
130 }
131
132 /**
133 * Implementation of guest_t.get_state.
134 */
135 static guest_state_t get_state(private_guest_t *this)
136 {
137 return this->state;
138 }
139
140 /**
141 * Implementation of guest_t.get_pid.
142 */
143 static pid_t get_pid(private_guest_t *this)
144 {
145 return this->pid;
146 }
147
148 /**
149 * write format string to a buffer, and advance buffer position
150 */
151 static char* write_arg(char **pos, size_t *left, char *format, ...)
152 {
153 size_t len;
154 char *res = NULL;
155 va_list args;
156
157 va_start(args, format);
158 len = vsnprintf(*pos, *left, format, args);
159 va_end(args);
160 if (len < *left)
161 {
162 res = *pos;
163 len++;
164 *pos += len + 1;
165 *left -= len + 1;
166 }
167 return res;
168 }
169
170 /**
171 * Implementation of guest_t.stop.
172 */
173 static void stop(private_guest_t *this)
174 {
175 if (this->state != GUEST_STOPPED)
176 {
177 this->state = GUEST_STOPPING;
178 this->ifaces->destroy_offset(this->ifaces, offsetof(iface_t, destroy));
179 this->ifaces = linked_list_create();
180 kill(this->pid, SIGINT);
181 waitpid(this->pid, NULL, 0);
182 this->state = GUEST_STOPPED;
183 }
184 }
185
186 /**
187 * Implementation of guest_t.start.
188 */
189 static bool start(private_guest_t *this)
190 {
191 char buf[2048];
192 char *notify;
193 char *pos = buf;
194 char *args[16];
195 int i = 0;
196 size_t left = sizeof(buf);
197
198 if (this->state != GUEST_STOPPED)
199 {
200 DBG1("unable to start guest in state %N", guest_state_names, this->state);
201 return FALSE;
202 }
203 this->state = GUEST_STARTING;
204
205 notify = write_arg(&pos, &left, "%s/%s", this->dirname, NOTIFY_FILE);
206
207 args[i++] = write_arg(&pos, &left, "%s/%s", this->dirname, KERNEL_FILE);
208 args[i++] = write_arg(&pos, &left, "root=/dev/root");
209 args[i++] = write_arg(&pos, &left, "rootfstype=hostfs");
210 args[i++] = write_arg(&pos, &left, "rootflags=%s/%s", this->dirname, UNION_DIR);
211 args[i++] = write_arg(&pos, &left, "uml_dir=%s", this->dirname);
212 args[i++] = write_arg(&pos, &left, "umid=%s", this->name);
213 args[i++] = write_arg(&pos, &left, "mem=%dM", this->mem);
214 args[i++] = write_arg(&pos, &left, "mconsole=notify:%s", notify);
215 /*args[i++] = write_arg(&pos, &left, "con=pts");*/
216 args[i++] = write_arg(&pos, &left, "con0=null,fd:%d", this->bootlog);
217 //args[i++] = write_arg(&pos, &left, "con0=fd:0,fd:1");
218 //args[i++] = write_arg(&pos, &left, "con1=null,null");
219 args[i++] = write_arg(&pos, &left, "con2=null,null");
220 args[i++] = write_arg(&pos, &left, "con3=null,null");
221 args[i++] = write_arg(&pos, &left, "con4=null,null");
222 args[i++] = write_arg(&pos, &left, "con5=null,null");
223 args[i++] = write_arg(&pos, &left, "con6=null,null");
224 args[i++] = NULL;
225
226 this->pid = fork();
227 switch (this->pid)
228 {
229 case 0: /* child, */
230 dup2(open("/dev/null", 0), 0);
231 dup2(this->bootlog, 1);
232 dup2(this->bootlog, 2);
233 execvp(args[0], args);
234 DBG1("starting UML kernel '%s' failed: %m", args[0]);
235 exit(1);
236 case -1:
237 this->state = GUEST_STOPPED;
238 return FALSE;
239 default:
240 break;
241 }
242 /* open mconsole */
243 this->mconsole = mconsole_create(notify);
244 if (this->mconsole == NULL)
245 {
246 DBG1("opening mconsole at '%s' failed, stopping guest", buf);
247 stop(this);
248 return FALSE;
249 }
250 this->state = GUEST_RUNNING;
251 return TRUE;
252 }
253
254 /**
255 * Implementation of guest_t.set_scenario.
256 */
257 static bool set_scenario(private_guest_t *this, char *path)
258 {
259 char dir[PATH_MAX];
260 size_t len;
261
262 if (path == NULL)
263 {
264 return this->cowfs->set_scenario(this->cowfs, NULL);
265 }
266
267 len = snprintf(dir, sizeof(dir), "%s/%s", path, this->name);
268 if (len < 0 || len >= sizeof(dir))
269 {
270 return FALSE;
271 }
272 if (access(dir, F_OK) != 0)
273 {
274 if (mkdir(dir, PERME) != 0)
275 {
276 DBG1("creating scenario overlay for guest '%s' failed: %m", this->name);
277 return FALSE;
278 }
279 }
280 return this->cowfs->set_scenario(this->cowfs, dir);
281 }
282
283 /**
284 * Implementation of guest_t.sigchild.
285 */
286 static void sigchild(private_guest_t *this)
287 {
288 if (this->state != GUEST_STOPPING)
289 { /* collect zombie if uml crashed */
290 waitpid(this->pid, NULL, WNOHANG);
291 }
292 DESTROY_IF(this->mconsole);
293 this->mconsole = NULL;
294 this->state = GUEST_STOPPED;
295 }
296
297 /**
298 * umount the union filesystem
299 */
300 static bool umount_unionfs(private_guest_t *this)
301 {
302 if (this->cowfs)
303 {
304 this->cowfs->destroy(this->cowfs);
305 this->cowfs = NULL;
306 return TRUE;
307 }
308 return FALSE;
309 }
310
311 /**
312 * mount the union filesystem
313 */
314 static bool mount_unionfs(private_guest_t *this)
315 {
316 char master[PATH_MAX];
317 char diff[PATH_MAX];
318 char mount[PATH_MAX];
319
320 if (this->cowfs == NULL)
321 {
322 snprintf(master, sizeof(master), "%s/%s", this->dirname, MASTER_DIR);
323 snprintf(diff, sizeof(diff), "%s/%s", this->dirname, DIFF_DIR);
324 snprintf(mount, sizeof(mount), "%s/%s", this->dirname, UNION_DIR);
325
326 this->cowfs = cowfs_create(master, diff, mount);
327 if (this->cowfs)
328 {
329 return TRUE;
330 }
331 }
332 return FALSE;
333 }
334
335 /**
336 * open logfile for boot messages
337 */
338 static int open_bootlog(private_guest_t *this)
339 {
340 int fd;
341
342 fd = openat(this->dir, LOG_FILE, O_WRONLY | O_CREAT, PERM);
343 if (fd == -1)
344 {
345 DBG1("opening bootlog failed, using stdout");
346 return 1;
347 }
348 return fd;
349 }
350
351 /**
352 * load memory configuration from file
353 */
354 int loadmem(private_guest_t *this)
355 {
356 FILE *file;
357 int mem = 0;
358
359 file = fdopen(openat(this->dir, MEMORY_FILE, O_RDONLY, PERM), "r");
360 if (file)
361 {
362 if (fscanf(file, "%d", &mem) <= 0)
363 {
364 mem = 0;
365 }
366 fclose(file);
367 }
368 return mem;
369 }
370
371 /**
372 * save memory configuration to file
373 */
374 bool savemem(private_guest_t *this, int mem)
375 {
376 FILE *file;
377 bool retval = FALSE;
378
379 file = fdopen(openat(this->dir, MEMORY_FILE, O_RDWR | O_CREAT | O_TRUNC,
380 PERM), "w");
381 if (file)
382 {
383 if (fprintf(file, "%d", mem) > 0)
384 {
385 retval = TRUE;
386 }
387 fclose(file);
388 }
389 return retval;
390 }
391
392 /**
393 * Implementation of guest_t.destroy.
394 */
395 static void destroy(private_guest_t *this)
396 {
397 stop(this);
398 umount_unionfs(this);
399 if (this->bootlog > 1)
400 {
401 close(this->bootlog);
402 }
403 if (this->dir > 0)
404 {
405 close(this->dir);
406 }
407 free(this->dirname);
408 free(this->name);
409 free(this);
410 }
411
412 /**
413 * generic guest constructor
414 */
415 static private_guest_t *guest_create_generic(char *parent, char *name,
416 bool create)
417 {
418 char cwd[PATH_MAX];
419 private_guest_t *this = malloc_thing(private_guest_t);
420
421 this->public.get_name = (void*)get_name;
422 this->public.get_pid = (pid_t(*)(guest_t*))get_pid;
423 this->public.get_state = (guest_state_t(*)(guest_t*))get_state;
424 this->public.create_iface = (iface_t*(*)(guest_t*,char*))create_iface;
425 this->public.create_iface_iterator = (iterator_t*(*)(guest_t*))create_iface_iterator;
426 this->public.start = (void*)start;
427 this->public.stop = (void*)stop;
428 this->public.set_scenario = (bool(*)(guest_t*, char *path))set_scenario;
429 this->public.sigchild = (void(*)(guest_t*))sigchild;
430 this->public.destroy = (void*)destroy;
431
432 if (*parent == '/' || getcwd(cwd, sizeof(cwd)) == NULL)
433 {
434 asprintf(&this->dirname, "%s/%s", parent, name);
435 }
436 else
437 {
438 asprintf(&this->dirname, "%s/%s/%s", cwd, parent, name);
439 }
440 if (create)
441 {
442 mkdir(this->dirname, PERME);
443 }
444 this->dir = open(this->dirname, O_DIRECTORY, PERME);
445 if (this->dir < 0)
446 {
447 DBG1("opening guest directory '%s' failed: %m", this->dirname);
448 free(this->dirname);
449 free(this);
450 return NULL;
451 }
452
453 this->pid = 0;
454 this->state = GUEST_STOPPED;
455 this->mconsole = NULL;
456 this->ifaces = linked_list_create();
457 this->mem = 0;
458 this->bootlog = open_bootlog(this);
459 this->name = strdup(name);
460 this->cowfs = NULL;
461
462 return this;
463 }
464
465 /**
466 * create a symlink to old called new in our working dir
467 */
468 static bool make_symlink(private_guest_t *this, char *old, char *new)
469 {
470 char cwd[PATH_MAX];
471 char buf[PATH_MAX];
472
473 if (*old == '/' || getcwd(cwd, sizeof(cwd)) == NULL)
474 {
475 snprintf(buf, sizeof(buf), "%s", old);
476 }
477 else
478 {
479 snprintf(buf, sizeof(buf), "%s/%s", cwd, old);
480 }
481 return symlinkat(buf, this->dir, new) == 0;
482 }
483
484
485 /**
486 * create the guest instance, including required dirs and mounts
487 */
488 guest_t *guest_create(char *parent, char *name, char *kernel,
489 char *master, int mem)
490 {
491 private_guest_t *this = guest_create_generic(parent, name, TRUE);
492
493 if (this == NULL)
494 {
495 return NULL;
496 }
497
498 if (!make_symlink(this, master, MASTER_DIR) ||
499 !make_symlink(this, kernel, KERNEL_FILE))
500 {
501 DBG1("creating master/kernel symlink failed: %m");
502 destroy(this);
503 return NULL;
504 }
505
506 if (mkdirat(this->dir, UNION_DIR, PERME) != 0 ||
507 mkdirat(this->dir, DIFF_DIR, PERME) != 0)
508 {
509 DBG1("unable to create directories for '%s': %m", name);
510 destroy(this);
511 return NULL;
512 }
513
514 this->mem = mem;
515 if (!savemem(this, mem))
516 {
517 destroy(this);
518 return NULL;
519 }
520
521 if (!mount_unionfs(this))
522 {
523 destroy(this);
524 return NULL;
525 }
526
527 return &this->public;
528 }
529
530 /**
531 * load an already created guest
532 */
533 guest_t *guest_load(char *parent, char *name)
534 {
535 private_guest_t *this = guest_create_generic(parent, name, FALSE);
536
537 if (this == NULL)
538 {
539 return NULL;
540 }
541
542 this->mem = loadmem(this);
543 if (this->mem == 0)
544 {
545 DBG1("unable to open memory configuration file: %m", name);
546 destroy(this);
547 return NULL;
548 }
549
550 if (!mount_unionfs(this))
551 {
552 destroy(this);
553 return NULL;
554 }
555
556 return &this->public;
557 }
558