* ruby extension extracted from irdumm
[strongswan.git] / src / dumm / guest.c
1 /*
2 * Copyright (C) 2008 Tobias Brunner
3 * Copyright (C) 2007 Martin Willi
4 * Hochschule fuer Technik Rapperswil
5 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation; either version 2 of the License, or (at your
9 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * for more details.
15 */
16
17 #define _GNU_SOURCE
18
19 #include <sys/types.h>
20 #include <sys/stat.h>
21 #include <sys/wait.h>
22 #include <sys/uio.h>
23 #include <unistd.h>
24 #include <stdio.h>
25 #include <fcntl.h>
26 #include <signal.h>
27 #include <dirent.h>
28 #include <termios.h>
29 #include <stdarg.h>
30
31 #include <debug.h>
32 #include <utils/linked_list.h>
33
34 #include "dumm.h"
35 #include "guest.h"
36 #include "mconsole.h"
37 #include "cowfs.h"
38
39 #define PERME (S_IRWXU | S_IRWXG)
40 #define PERM (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP)
41
42 #define MASTER_DIR "master"
43 #define DIFF_DIR "diff"
44 #define UNION_DIR "union"
45 #define MEMORY_FILE "mem"
46 #define PID_FILE "pid"
47 #define KERNEL_FILE "linux"
48 #define LOG_FILE "boot.log"
49 #define NOTIFY_FILE "notify"
50 #define PTYS 0
51
52 typedef struct private_guest_t private_guest_t;
53
54 struct private_guest_t {
55 /** implemented public interface */
56 guest_t public;
57 /** name of the guest */
58 char *name;
59 /** directory of guest */
60 int dir;
61 /** directory name of guest */
62 char *dirname;
63 /** amount of memory for guest, in MB */
64 int mem;
65 /** pid of guest child process */
66 int pid;
67 /** state of guest */
68 guest_state_t state;
69 /** FUSE cowfs instance */
70 cowfs_t *cowfs;
71 /** mconsole to control running UML */
72 mconsole_t *mconsole;
73 /** list of interfaces attached to the guest */
74 linked_list_t *ifaces;
75 };
76
77 ENUM(guest_state_names, GUEST_STOPPED, GUEST_STOPPING,
78 "STOPPED",
79 "STARTING",
80 "RUNNING",
81 "PAUSED",
82 "STOPPING",
83 );
84
85 /**
86 * Implementation of guest_t.get_name.
87 */
88 static char* get_name(private_guest_t *this)
89 {
90 return this->name;
91 }
92
93 /**
94 * Implementation of guest_t.create_iface.
95 */
96 static iface_t* create_iface(private_guest_t *this, char *name)
97 {
98 enumerator_t *enumerator;
99 iface_t *iface;
100
101 if (this->state != GUEST_RUNNING)
102 {
103 DBG1("guest '%s' not running, unable to add interface", this->name);
104 return NULL;
105 }
106
107 enumerator = this->ifaces->create_enumerator(this->ifaces);
108 while (enumerator->enumerate(enumerator, (void**)&iface))
109 {
110 if (streq(name, iface->get_guestif(iface)))
111 {
112 DBG1("guest '%s' already has an interface '%s'", this->name, name);
113 enumerator->destroy(enumerator);
114 return NULL;
115 }
116 }
117 enumerator->destroy(enumerator);
118
119 iface = iface_create(name, &this->public, this->mconsole);
120 if (iface)
121 {
122 this->ifaces->insert_last(this->ifaces, iface);
123 }
124 return iface;
125 }
126
127 /**
128 * Implementation of guest_t.destroy_iface.
129 */
130 static void destroy_iface(private_guest_t *this, iface_t *iface)
131 {
132 enumerator_t *enumerator;
133 iface_t *current;
134
135 enumerator = this->ifaces->create_enumerator(this->ifaces);
136 while (enumerator->enumerate(enumerator, (void**)&current))
137 {
138 if (current == iface)
139 {
140 this->ifaces->remove_at(this->ifaces, enumerator);
141 current->destroy(current);
142 break;
143 }
144 }
145 enumerator->destroy(enumerator);
146 }
147
148 /**
149 * Implementation of guest_t.create_iface_enumerator.
150 */
151 static enumerator_t* create_iface_enumerator(private_guest_t *this)
152 {
153 return this->ifaces->create_enumerator(this->ifaces);
154 }
155
156 /**
157 * Implementation of guest_t.get_state.
158 */
159 static guest_state_t get_state(private_guest_t *this)
160 {
161 return this->state;
162 }
163
164 /**
165 * Implementation of guest_t.get_pid.
166 */
167 static pid_t get_pid(private_guest_t *this)
168 {
169 return this->pid;
170 }
171
172 /**
173 * write format string to a buffer, and advance buffer position
174 */
175 static char* write_arg(char **pos, size_t *left, char *format, ...)
176 {
177 size_t len;
178 char *res = NULL;
179 va_list args;
180
181 va_start(args, format);
182 len = vsnprintf(*pos, *left, format, args);
183 va_end(args);
184 if (len < *left)
185 {
186 res = *pos;
187 len++;
188 *pos += len + 1;
189 *left -= len + 1;
190 }
191 return res;
192 }
193
194 /**
195 * Implementation of guest_t.stop.
196 */
197 static void stop(private_guest_t *this, idle_function_t idle)
198 {
199 if (this->state != GUEST_STOPPED)
200 {
201 this->state = GUEST_STOPPING;
202 this->ifaces->destroy_offset(this->ifaces, offsetof(iface_t, destroy));
203 this->ifaces = linked_list_create();
204 kill(this->pid, SIGINT);
205 while (this->state != GUEST_STOPPED)
206 {
207 if (idle)
208 {
209 idle();
210 }
211 else
212 {
213 usleep(50000);
214 }
215 }
216 unlinkat(this->dir, PID_FILE, 0);
217 }
218 }
219
220 /**
221 * save pid in file
222 */
223 void savepid(private_guest_t *this)
224 {
225 FILE *file;
226
227 file = fdopen(openat(this->dir, PID_FILE, O_RDWR | O_CREAT | O_TRUNC,
228 PERM), "w");
229 if (file)
230 {
231 fprintf(file, "%d", this->pid);
232 fclose(file);
233 }
234 }
235
236 /**
237 * Implementation of guest_t.start.
238 */
239 static bool start(private_guest_t *this, invoke_function_t invoke, void* data,
240 idle_function_t idle)
241 {
242 char buf[2048];
243 char *notify;
244 char *pos = buf;
245 char *args[32];
246 int i = 0;
247 size_t left = sizeof(buf);
248
249 memset(args, 0, sizeof(args));
250
251 if (this->state != GUEST_STOPPED)
252 {
253 DBG1("unable to start guest in state %N", guest_state_names, this->state);
254 return FALSE;
255 }
256 this->state = GUEST_STARTING;
257
258 notify = write_arg(&pos, &left, "%s/%s", this->dirname, NOTIFY_FILE);
259
260 args[i++] = write_arg(&pos, &left, "%s/%s", this->dirname, KERNEL_FILE);
261 args[i++] = write_arg(&pos, &left, "root=/dev/root");
262 args[i++] = write_arg(&pos, &left, "rootfstype=hostfs");
263 args[i++] = write_arg(&pos, &left, "rootflags=%s/%s", this->dirname, UNION_DIR);
264 args[i++] = write_arg(&pos, &left, "uml_dir=%s", this->dirname);
265 args[i++] = write_arg(&pos, &left, "umid=%s", this->name);
266 args[i++] = write_arg(&pos, &left, "mem=%dM", this->mem);
267 args[i++] = write_arg(&pos, &left, "mconsole=notify:%s", notify);
268 args[i++] = write_arg(&pos, &left, "con=null");
269
270 this->pid = invoke(data, &this->public, args, i);
271 if (!this->pid)
272 {
273 this->state = GUEST_STOPPED;
274 return FALSE;
275 }
276 savepid(this);
277
278 /* open mconsole */
279 this->mconsole = mconsole_create(notify, idle);
280 if (this->mconsole == NULL)
281 {
282 DBG1("opening mconsole at '%s' failed, stopping guest", buf);
283 stop(this, NULL);
284 return FALSE;
285 }
286
287 this->state = GUEST_RUNNING;
288 return TRUE;
289 }
290
291 /**
292 * Implementation of guest_t.load_template.
293 */
294 static bool load_template(private_guest_t *this, char *path)
295 {
296 char dir[PATH_MAX];
297 size_t len;
298 iface_t *iface;
299
300 if (path == NULL)
301 {
302 return this->cowfs->set_overlay(this->cowfs, NULL);
303 }
304
305 len = snprintf(dir, sizeof(dir), "%s/%s", path, this->name);
306 if (len < 0 || len >= sizeof(dir))
307 {
308 return FALSE;
309 }
310 if (access(dir, F_OK) != 0)
311 {
312 if (mkdir(dir, PERME) != 0)
313 {
314 DBG1("creating overlay for guest '%s' failed: %m", this->name);
315 return FALSE;
316 }
317 }
318 if (!this->cowfs->set_overlay(this->cowfs, dir))
319 {
320 return FALSE;
321 }
322 while (this->ifaces->remove_last(this->ifaces, (void**)&iface) == SUCCESS)
323 {
324 iface->destroy(iface);
325 }
326 return TRUE;
327 }
328
329 /**
330 * Variadic version of the exec function
331 */
332 static int vexec(private_guest_t *this, void(*cb)(void*,char*,size_t), void *data,
333 char *cmd, va_list args)
334 {
335 char buf[1024];
336 size_t len;
337
338 if (this->mconsole)
339 {
340 len = vsnprintf(buf, sizeof(buf), cmd, args);
341
342 if (len > 0 && len < sizeof(buf))
343 {
344 return this->mconsole->exec(this->mconsole, cb, data, buf);
345 }
346 }
347 return -1;
348 }
349
350 /**
351 * Implementation of guest_t.exec
352 */
353 static int exec(private_guest_t *this, void(*cb)(void*,char*,size_t), void *data,
354 char *cmd, ...)
355 {
356 int res;
357 va_list args;
358 va_start(args, cmd);
359 res = vexec(this, cb, data, cmd, args);
360 va_end(args);
361 return res;
362 }
363
364 typedef struct {
365 chunk_t buf;
366 void (*cb)(void*,char*);
367 void *data;
368 } exec_str_t;
369
370 /**
371 * callback that combines chunks to a string. if a callback is given, the string
372 * is split at newlines and the callback is called for each line.
373 */
374 static void exec_str_cb(exec_str_t *data, char *buf, size_t len)
375 {
376 if (!data->buf.ptr)
377 {
378 data->buf = chunk_alloc(len + 1);
379 memcpy(data->buf.ptr, buf, len);
380 data->buf.ptr[len] = '\0';
381 }
382 else
383 {
384 size_t newlen = strlen(data->buf.ptr) + len + 1;
385 if (newlen > data->buf.len)
386 {
387 data->buf.ptr = realloc(data->buf.ptr, newlen);
388 data->buf.len = newlen;
389 }
390 strncat(data->buf.ptr, buf, len);
391 }
392
393 if (data->cb)
394 {
395 char *nl;
396 while ((nl = strchr(data->buf.ptr, '\n')) != NULL)
397 {
398 *nl++ = '\0';
399 data->cb(data->data, data->buf.ptr);
400 memmove(data->buf.ptr, nl, strlen(nl) + 1);
401 }
402 }
403 }
404
405 /**
406 * Implementation of guest_t.exec_str
407 */
408 static int exec_str(private_guest_t *this, void(*cb)(void*,char*), bool lines,
409 void *data, char *cmd, ...)
410 {
411 int res;
412 va_list args;
413 va_start(args, cmd);
414 if (cb)
415 {
416 exec_str_t exec = { chunk_empty, NULL, NULL };
417 if (lines)
418 {
419 exec.cb = cb;
420 exec.data = data;
421 }
422 res = vexec(this, (void(*)(void*,char*,size_t))exec_str_cb, &exec, cmd, args);
423 if (exec.buf.ptr)
424 {
425 if (!lines || strlen(exec.buf.ptr) > 0)
426 {
427 /* return the complete string or the remaining stuff in the
428 * buffer (i.e. when there was no newline at the end) */
429 cb(data, exec.buf.ptr);
430 }
431 chunk_free(&exec.buf);
432 }
433 }
434 else
435 {
436 res = vexec(this, NULL, NULL, cmd, args);
437 }
438 va_end(args);
439 return res;
440 }
441
442 /**
443 * Implementation of guest_t.sigchild.
444 */
445 static void sigchild(private_guest_t *this)
446 {
447 DESTROY_IF(this->mconsole);
448 this->mconsole = NULL;
449 this->state = GUEST_STOPPED;
450 }
451
452 /**
453 * umount the union filesystem
454 */
455 static bool umount_unionfs(private_guest_t *this)
456 {
457 if (this->cowfs)
458 {
459 this->cowfs->destroy(this->cowfs);
460 this->cowfs = NULL;
461 return TRUE;
462 }
463 return FALSE;
464 }
465
466 /**
467 * mount the union filesystem
468 */
469 static bool mount_unionfs(private_guest_t *this)
470 {
471 char master[PATH_MAX];
472 char diff[PATH_MAX];
473 char mount[PATH_MAX];
474
475 if (this->cowfs == NULL)
476 {
477 snprintf(master, sizeof(master), "%s/%s", this->dirname, MASTER_DIR);
478 snprintf(diff, sizeof(diff), "%s/%s", this->dirname, DIFF_DIR);
479 snprintf(mount, sizeof(mount), "%s/%s", this->dirname, UNION_DIR);
480
481 this->cowfs = cowfs_create(master, diff, mount);
482 if (this->cowfs)
483 {
484 return TRUE;
485 }
486 }
487 return FALSE;
488 }
489
490 /**
491 * load memory configuration from file
492 */
493 int loadmem(private_guest_t *this)
494 {
495 FILE *file;
496 int mem = 0;
497
498 file = fdopen(openat(this->dir, MEMORY_FILE, O_RDONLY, PERM), "r");
499 if (file)
500 {
501 if (fscanf(file, "%d", &mem) <= 0)
502 {
503 mem = 0;
504 }
505 fclose(file);
506 }
507 return mem;
508 }
509
510 /**
511 * save memory configuration to file
512 */
513 bool savemem(private_guest_t *this, int mem)
514 {
515 FILE *file;
516 bool retval = FALSE;
517
518 file = fdopen(openat(this->dir, MEMORY_FILE, O_RDWR | O_CREAT | O_TRUNC,
519 PERM), "w");
520 if (file)
521 {
522 if (fprintf(file, "%d", mem) > 0)
523 {
524 retval = TRUE;
525 }
526 fclose(file);
527 }
528 return retval;
529 }
530
531 /**
532 * Implementation of guest_t.destroy.
533 */
534 static void destroy(private_guest_t *this)
535 {
536 stop(this, NULL);
537 umount_unionfs(this);
538 if (this->dir > 0)
539 {
540 close(this->dir);
541 }
542 this->ifaces->destroy(this->ifaces);
543 free(this->dirname);
544 free(this->name);
545 free(this);
546 }
547
548 /**
549 * generic guest constructor
550 */
551 static private_guest_t *guest_create_generic(char *parent, char *name,
552 bool create)
553 {
554 char cwd[PATH_MAX];
555 private_guest_t *this = malloc_thing(private_guest_t);
556
557 this->public.get_name = (void*)get_name;
558 this->public.get_pid = (pid_t(*)(guest_t*))get_pid;
559 this->public.get_state = (guest_state_t(*)(guest_t*))get_state;
560 this->public.create_iface = (iface_t*(*)(guest_t*,char*))create_iface;
561 this->public.destroy_iface = (void(*)(guest_t*,iface_t*))destroy_iface;
562 this->public.create_iface_enumerator = (enumerator_t*(*)(guest_t*))create_iface_enumerator;
563 this->public.start = (void*)start;
564 this->public.stop = (void*)stop;
565 this->public.load_template = (bool(*)(guest_t*, char *path))load_template;
566 this->public.exec = (int(*)(guest_t*, void(*cb)(void*,char*,size_t),void*,char*,...))exec;
567 this->public.exec_str = (int(*)(guest_t*, void(*cb)(void*,char*),bool,void*,char*,...))exec_str;
568 this->public.sigchild = (void(*)(guest_t*))sigchild;
569 this->public.destroy = (void*)destroy;
570
571 if (*parent == '/' || getcwd(cwd, sizeof(cwd)) == NULL)
572 {
573 asprintf(&this->dirname, "%s/%s", parent, name);
574 }
575 else
576 {
577 asprintf(&this->dirname, "%s/%s/%s", cwd, parent, name);
578 }
579 if (create)
580 {
581 mkdir(this->dirname, PERME);
582 }
583 this->dir = open(this->dirname, O_DIRECTORY, PERME);
584 if (this->dir < 0)
585 {
586 DBG1("opening guest directory '%s' failed: %m", this->dirname);
587 free(this->dirname);
588 free(this);
589 return NULL;
590 }
591 this->pid = 0;
592 this->state = GUEST_STOPPED;
593 this->mconsole = NULL;
594 this->ifaces = linked_list_create();
595 this->mem = 0;
596 this->name = strdup(name);
597 this->cowfs = NULL;
598
599 return this;
600 }
601
602 /**
603 * create a symlink to old called new in our working dir
604 */
605 static bool make_symlink(private_guest_t *this, char *old, char *new)
606 {
607 char cwd[PATH_MAX];
608 char buf[PATH_MAX];
609
610 if (*old == '/' || getcwd(cwd, sizeof(cwd)) == NULL)
611 {
612 snprintf(buf, sizeof(buf), "%s", old);
613 }
614 else
615 {
616 snprintf(buf, sizeof(buf), "%s/%s", cwd, old);
617 }
618 return symlinkat(buf, this->dir, new) == 0;
619 }
620
621
622 /**
623 * create the guest instance, including required dirs and mounts
624 */
625 guest_t *guest_create(char *parent, char *name, char *kernel,
626 char *master, int mem)
627 {
628 private_guest_t *this = guest_create_generic(parent, name, TRUE);
629
630 if (this == NULL)
631 {
632 return NULL;
633 }
634
635 if (!make_symlink(this, master, MASTER_DIR) ||
636 !make_symlink(this, kernel, KERNEL_FILE))
637 {
638 DBG1("creating master/kernel symlink failed: %m");
639 destroy(this);
640 return NULL;
641 }
642
643 if (mkdirat(this->dir, UNION_DIR, PERME) != 0 ||
644 mkdirat(this->dir, DIFF_DIR, PERME) != 0)
645 {
646 DBG1("unable to create directories for '%s': %m", name);
647 destroy(this);
648 return NULL;
649 }
650
651 this->mem = mem;
652 if (!savemem(this, mem))
653 {
654 destroy(this);
655 return NULL;
656 }
657
658 if (!mount_unionfs(this))
659 {
660 destroy(this);
661 return NULL;
662 }
663
664 return &this->public;
665 }
666
667 /**
668 * load an already created guest
669 */
670 guest_t *guest_load(char *parent, char *name)
671 {
672 private_guest_t *this = guest_create_generic(parent, name, FALSE);
673
674 if (this == NULL)
675 {
676 return NULL;
677 }
678
679 this->mem = loadmem(this);
680 if (this->mem == 0)
681 {
682 DBG1("unable to open memory configuration file: %m", name);
683 destroy(this);
684 return NULL;
685 }
686
687 if (!mount_unionfs(this))
688 {
689 destroy(this);
690 return NULL;
691 }
692
693 return &this->public;
694 }
695