fixed interface disconnection
[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 #include <termios.h>
28
29 #include <debug.h>
30 #include <utils/linked_list.h>
31
32 #include "dumm.h"
33 #include "guest.h"
34 #include "mconsole.h"
35 #include "cowfs.h"
36
37 #define PERME (S_IRWXU | S_IRWXG)
38 #define PERM (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP)
39
40 #define MASTER_DIR "master"
41 #define DIFF_DIR "diff"
42 #define UNION_DIR "union"
43 #define MEMORY_FILE "mem"
44 #define KERNEL_FILE "linux"
45 #define LOG_FILE "boot.log"
46 #define NOTIFY_FILE "notify"
47 #define PTYS 0
48
49 typedef struct private_guest_t private_guest_t;
50
51 struct private_guest_t {
52 /** implemented public interface */
53 guest_t public;
54 /** name of the guest */
55 char *name;
56 /** directory of guest */
57 int dir;
58 /** directory name of guest */
59 char *dirname;
60 /** amount of memory for guest, in MB */
61 int mem;
62 /** pid of guest child process */
63 int pid;
64 /** state of guest */
65 guest_state_t state;
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 enumerator_t *enumerator;
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 enumerator = this->ifaces->create_enumerator(this->ifaces);
105 while (enumerator->enumerate(enumerator, (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 enumerator->destroy(enumerator);
111 return NULL;
112 }
113 }
114 enumerator->destroy(enumerator);
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.destroy_iface.
126 */
127 static void destroy_iface(private_guest_t *this, iface_t *iface)
128 {
129 enumerator_t *enumerator;
130 iface_t *current;
131
132 enumerator = this->ifaces->create_enumerator(this->ifaces);
133 while (enumerator->enumerate(enumerator, (void**)&current))
134 {
135 if (current == iface)
136 {
137 this->ifaces->remove_at(this->ifaces, enumerator);
138 current->destroy(current);
139 break;
140 }
141 }
142 enumerator->destroy(enumerator);
143 }
144
145 /**
146 * Implementation of guest_t.create_iface_enumerator.
147 */
148 static enumerator_t* create_iface_enumerator(private_guest_t *this)
149 {
150 return this->ifaces->create_enumerator(this->ifaces);
151 }
152
153 /**
154 * Implementation of guest_t.get_state.
155 */
156 static guest_state_t get_state(private_guest_t *this)
157 {
158 return this->state;
159 }
160
161 /**
162 * Implementation of guest_t.get_pid.
163 */
164 static pid_t get_pid(private_guest_t *this)
165 {
166 return this->pid;
167 }
168
169 /**
170 * write format string to a buffer, and advance buffer position
171 */
172 static char* write_arg(char **pos, size_t *left, char *format, ...)
173 {
174 size_t len;
175 char *res = NULL;
176 va_list args;
177
178 va_start(args, format);
179 len = vsnprintf(*pos, *left, format, args);
180 va_end(args);
181 if (len < *left)
182 {
183 res = *pos;
184 len++;
185 *pos += len + 1;
186 *left -= len + 1;
187 }
188 return res;
189 }
190
191 /**
192 * Implementation of guest_t.stop.
193 */
194 static void stop(private_guest_t *this, idle_function_t idle)
195 {
196 if (this->state != GUEST_STOPPED)
197 {
198 this->state = GUEST_STOPPING;
199 this->ifaces->destroy_offset(this->ifaces, offsetof(iface_t, destroy));
200 this->ifaces = linked_list_create();
201 kill(this->pid, SIGINT);
202 while (this->state != GUEST_STOPPED)
203 {
204 if (idle)
205 {
206 idle();
207 }
208 else
209 {
210 usleep(50000);
211 }
212 }
213 }
214 }
215
216 /**
217 * Implementation of guest_t.start.
218 */
219 static bool start(private_guest_t *this, invoke_function_t invoke, void* data,
220 idle_function_t idle)
221 {
222 char buf[2048];
223 char *notify;
224 char *pos = buf;
225 char *args[32];
226 int i = 0;
227 size_t left = sizeof(buf);
228
229 memset(args, 0, sizeof(args));
230
231 if (this->state != GUEST_STOPPED)
232 {
233 DBG1("unable to start guest in state %N", guest_state_names, this->state);
234 return FALSE;
235 }
236 this->state = GUEST_STARTING;
237
238 notify = write_arg(&pos, &left, "%s/%s", this->dirname, NOTIFY_FILE);
239
240 args[i++] = write_arg(&pos, &left, "%s/%s", this->dirname, KERNEL_FILE);
241 args[i++] = write_arg(&pos, &left, "root=/dev/root");
242 args[i++] = write_arg(&pos, &left, "rootfstype=hostfs");
243 args[i++] = write_arg(&pos, &left, "rootflags=%s/%s", this->dirname, UNION_DIR);
244 args[i++] = write_arg(&pos, &left, "uml_dir=%s", this->dirname);
245 args[i++] = write_arg(&pos, &left, "umid=%s", this->name);
246 args[i++] = write_arg(&pos, &left, "mem=%dM", this->mem);
247 args[i++] = write_arg(&pos, &left, "mconsole=notify:%s", notify);
248 args[i++] = write_arg(&pos, &left, "con=null");
249
250 this->pid = invoke(data, &this->public, args, i);
251 if (!this->pid)
252 {
253 this->state = GUEST_STOPPED;
254 return FALSE;
255 }
256 /* open mconsole */
257 this->mconsole = mconsole_create(notify, idle);
258 if (this->mconsole == NULL)
259 {
260 DBG1("opening mconsole at '%s' failed, stopping guest", buf);
261 stop(this, NULL);
262 return FALSE;
263 }
264
265 this->state = GUEST_RUNNING;
266 return TRUE;
267 }
268
269 /**
270 * Implementation of guest_t.load_template.
271 */
272 static bool load_template(private_guest_t *this, char *path)
273 {
274 char dir[PATH_MAX];
275 size_t len;
276 iface_t *iface;
277
278 if (path == NULL)
279 {
280 return this->cowfs->set_overlay(this->cowfs, NULL);
281 }
282
283 len = snprintf(dir, sizeof(dir), "%s/%s", path, this->name);
284 if (len < 0 || len >= sizeof(dir))
285 {
286 return FALSE;
287 }
288 if (access(dir, F_OK) != 0)
289 {
290 if (mkdir(dir, PERME) != 0)
291 {
292 DBG1("creating overlay for guest '%s' failed: %m", this->name);
293 return FALSE;
294 }
295 }
296 if (!this->cowfs->set_overlay(this->cowfs, dir))
297 {
298 return FALSE;
299 }
300 while (this->ifaces->remove_last(this->ifaces, (void**)&iface) == SUCCESS)
301 {
302 iface->destroy(iface);
303 }
304 return TRUE;
305 }
306
307 /**
308 * Implementation of guest_t.sigchild.
309 */
310 static void sigchild(private_guest_t *this)
311 {
312 DESTROY_IF(this->mconsole);
313 this->mconsole = NULL;
314 this->state = GUEST_STOPPED;
315 }
316
317 /**
318 * umount the union filesystem
319 */
320 static bool umount_unionfs(private_guest_t *this)
321 {
322 if (this->cowfs)
323 {
324 this->cowfs->destroy(this->cowfs);
325 this->cowfs = NULL;
326 return TRUE;
327 }
328 return FALSE;
329 }
330
331 /**
332 * mount the union filesystem
333 */
334 static bool mount_unionfs(private_guest_t *this)
335 {
336 char master[PATH_MAX];
337 char diff[PATH_MAX];
338 char mount[PATH_MAX];
339
340 if (this->cowfs == NULL)
341 {
342 snprintf(master, sizeof(master), "%s/%s", this->dirname, MASTER_DIR);
343 snprintf(diff, sizeof(diff), "%s/%s", this->dirname, DIFF_DIR);
344 snprintf(mount, sizeof(mount), "%s/%s", this->dirname, UNION_DIR);
345
346 this->cowfs = cowfs_create(master, diff, mount);
347 if (this->cowfs)
348 {
349 return TRUE;
350 }
351 }
352 return FALSE;
353 }
354
355 /**
356 * load memory configuration from file
357 */
358 int loadmem(private_guest_t *this)
359 {
360 FILE *file;
361 int mem = 0;
362
363 file = fdopen(openat(this->dir, MEMORY_FILE, O_RDONLY, PERM), "r");
364 if (file)
365 {
366 if (fscanf(file, "%d", &mem) <= 0)
367 {
368 mem = 0;
369 }
370 fclose(file);
371 }
372 return mem;
373 }
374
375 /**
376 * save memory configuration to file
377 */
378 bool savemem(private_guest_t *this, int mem)
379 {
380 FILE *file;
381 bool retval = FALSE;
382
383 file = fdopen(openat(this->dir, MEMORY_FILE, O_RDWR | O_CREAT | O_TRUNC,
384 PERM), "w");
385 if (file)
386 {
387 if (fprintf(file, "%d", mem) > 0)
388 {
389 retval = TRUE;
390 }
391 fclose(file);
392 }
393 return retval;
394 }
395
396 /**
397 * Implementation of guest_t.destroy.
398 */
399 static void destroy(private_guest_t *this)
400 {
401 stop(this, NULL);
402 umount_unionfs(this);
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.destroy_iface = (void(*)(guest_t*,iface_t*))destroy_iface;
426 this->public.create_iface_enumerator = (enumerator_t*(*)(guest_t*))create_iface_enumerator;
427 this->public.start = (void*)start;
428 this->public.stop = (void*)stop;
429 this->public.load_template = (bool(*)(guest_t*, char *path))load_template;
430 this->public.sigchild = (void(*)(guest_t*))sigchild;
431 this->public.destroy = (void*)destroy;
432
433 if (*parent == '/' || getcwd(cwd, sizeof(cwd)) == NULL)
434 {
435 asprintf(&this->dirname, "%s/%s", parent, name);
436 }
437 else
438 {
439 asprintf(&this->dirname, "%s/%s/%s", cwd, parent, name);
440 }
441 if (create)
442 {
443 mkdir(this->dirname, PERME);
444 }
445 this->dir = open(this->dirname, O_DIRECTORY, PERME);
446 if (this->dir < 0)
447 {
448 DBG1("opening guest directory '%s' failed: %m", this->dirname);
449 free(this->dirname);
450 free(this);
451 return NULL;
452 }
453
454 this->pid = 0;
455 this->state = GUEST_STOPPED;
456 this->mconsole = NULL;
457 this->ifaces = linked_list_create();
458 this->mem = 0;
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