added missing includes
[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 ARGS_FILE "args"
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 /** additional args to pass to guest */
64 char *args;
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 this->pid = 0;
218 }
219 }
220
221 /**
222 * save pid in file
223 */
224 void savepid(private_guest_t *this)
225 {
226 FILE *file;
227
228 file = fdopen(openat(this->dir, PID_FILE, O_RDWR | O_CREAT | O_TRUNC,
229 PERM), "w");
230 if (file)
231 {
232 fprintf(file, "%d", this->pid);
233 fclose(file);
234 }
235 }
236
237 /**
238 * Implementation of guest_t.start.
239 */
240 static bool start(private_guest_t *this, invoke_function_t invoke, void* data,
241 idle_function_t idle)
242 {
243 char buf[2048];
244 char *notify;
245 char *pos = buf;
246 char *args[32];
247 int i = 0;
248 size_t left = sizeof(buf);
249
250 memset(args, 0, sizeof(args));
251
252 if (this->state != GUEST_STOPPED)
253 {
254 DBG1("unable to start guest in state %N", guest_state_names, this->state);
255 return FALSE;
256 }
257 this->state = GUEST_STARTING;
258
259 notify = write_arg(&pos, &left, "%s/%s", this->dirname, NOTIFY_FILE);
260
261 args[i++] = write_arg(&pos, &left, "nice");
262 args[i++] = write_arg(&pos, &left, "%s/%s", this->dirname, KERNEL_FILE);
263 args[i++] = write_arg(&pos, &left, "root=/dev/root");
264 args[i++] = write_arg(&pos, &left, "rootfstype=hostfs");
265 args[i++] = write_arg(&pos, &left, "rootflags=%s/%s", this->dirname, UNION_DIR);
266 args[i++] = write_arg(&pos, &left, "uml_dir=%s", this->dirname);
267 args[i++] = write_arg(&pos, &left, "umid=%s", this->name);
268 args[i++] = write_arg(&pos, &left, "mconsole=notify:%s", notify);
269 args[i++] = write_arg(&pos, &left, "con=null");
270 if (this->args)
271 {
272 args[i++] = this->args;
273 }
274
275 this->pid = invoke(data, &this->public, args, i);
276 if (!this->pid)
277 {
278 this->state = GUEST_STOPPED;
279 return FALSE;
280 }
281 savepid(this);
282
283 /* open mconsole */
284 this->mconsole = mconsole_create(notify, idle);
285 if (this->mconsole == NULL)
286 {
287 DBG1("opening mconsole at '%s' failed, stopping guest", buf);
288 stop(this, NULL);
289 return FALSE;
290 }
291
292 this->state = GUEST_RUNNING;
293 return TRUE;
294 }
295
296 /**
297 * Implementation of guest_t.load_template.
298 */
299 static bool load_template(private_guest_t *this, char *path)
300 {
301 char dir[PATH_MAX];
302 size_t len;
303 iface_t *iface;
304
305 if (path == NULL)
306 {
307 return this->cowfs->set_overlay(this->cowfs, NULL);
308 }
309
310 len = snprintf(dir, sizeof(dir), "%s/%s", path, this->name);
311 if (len < 0 || len >= sizeof(dir))
312 {
313 return FALSE;
314 }
315 if (access(dir, F_OK) != 0)
316 {
317 if (!mkdir_p(dir, PERME))
318 {
319 DBG1("creating overlay for guest '%s' failed: %m", this->name);
320 return FALSE;
321 }
322 }
323 if (!this->cowfs->set_overlay(this->cowfs, dir))
324 {
325 return FALSE;
326 }
327 while (this->ifaces->remove_last(this->ifaces, (void**)&iface) == SUCCESS)
328 {
329 iface->destroy(iface);
330 }
331 return TRUE;
332 }
333
334 /**
335 * Variadic version of the exec function
336 */
337 static int vexec(private_guest_t *this, void(*cb)(void*,char*,size_t), void *data,
338 char *cmd, va_list args)
339 {
340 char buf[1024];
341 size_t len;
342
343 if (this->mconsole)
344 {
345 len = vsnprintf(buf, sizeof(buf), cmd, args);
346
347 if (len > 0 && len < sizeof(buf))
348 {
349 return this->mconsole->exec(this->mconsole, cb, data, buf);
350 }
351 }
352 return -1;
353 }
354
355 /**
356 * Implementation of guest_t.exec
357 */
358 static int exec(private_guest_t *this, void(*cb)(void*,char*,size_t), void *data,
359 char *cmd, ...)
360 {
361 int res;
362 va_list args;
363 va_start(args, cmd);
364 res = vexec(this, cb, data, cmd, args);
365 va_end(args);
366 return res;
367 }
368
369 typedef struct {
370 chunk_t buf;
371 void (*cb)(void*,char*);
372 void *data;
373 } exec_str_t;
374
375 /**
376 * callback that combines chunks to a string. if a callback is given, the string
377 * is split at newlines and the callback is called for each line.
378 */
379 static void exec_str_cb(exec_str_t *data, char *buf, size_t len)
380 {
381 if (!data->buf.ptr)
382 {
383 data->buf = chunk_alloc(len + 1);
384 memcpy(data->buf.ptr, buf, len);
385 data->buf.ptr[len] = '\0';
386 }
387 else
388 {
389 size_t newlen = strlen(data->buf.ptr) + len + 1;
390 if (newlen > data->buf.len)
391 {
392 data->buf.ptr = realloc(data->buf.ptr, newlen);
393 data->buf.len = newlen;
394 }
395 strncat(data->buf.ptr, buf, len);
396 }
397
398 if (data->cb)
399 {
400 char *nl;
401 while ((nl = strchr(data->buf.ptr, '\n')) != NULL)
402 {
403 *nl++ = '\0';
404 data->cb(data->data, data->buf.ptr);
405 memmove(data->buf.ptr, nl, strlen(nl) + 1);
406 }
407 }
408 }
409
410 /**
411 * Implementation of guest_t.exec_str
412 */
413 static int exec_str(private_guest_t *this, void(*cb)(void*,char*), bool lines,
414 void *data, char *cmd, ...)
415 {
416 int res;
417 va_list args;
418 va_start(args, cmd);
419 if (cb)
420 {
421 exec_str_t exec = { chunk_empty, NULL, NULL };
422 if (lines)
423 {
424 exec.cb = cb;
425 exec.data = data;
426 }
427 res = vexec(this, (void(*)(void*,char*,size_t))exec_str_cb, &exec, cmd, args);
428 if (exec.buf.ptr)
429 {
430 if (!lines || strlen(exec.buf.ptr) > 0)
431 {
432 /* return the complete string or the remaining stuff in the
433 * buffer (i.e. when there was no newline at the end) */
434 cb(data, exec.buf.ptr);
435 }
436 chunk_free(&exec.buf);
437 }
438 }
439 else
440 {
441 res = vexec(this, NULL, NULL, cmd, args);
442 }
443 va_end(args);
444 return res;
445 }
446
447 /**
448 * Implementation of guest_t.sigchild.
449 */
450 static void sigchild(private_guest_t *this)
451 {
452 DESTROY_IF(this->mconsole);
453 this->mconsole = NULL;
454 this->state = GUEST_STOPPED;
455 }
456
457 /**
458 * umount the union filesystem
459 */
460 static bool umount_unionfs(private_guest_t *this)
461 {
462 if (this->cowfs)
463 {
464 this->cowfs->destroy(this->cowfs);
465 this->cowfs = NULL;
466 return TRUE;
467 }
468 return FALSE;
469 }
470
471 /**
472 * mount the union filesystem
473 */
474 static bool mount_unionfs(private_guest_t *this)
475 {
476 char master[PATH_MAX];
477 char diff[PATH_MAX];
478 char mount[PATH_MAX];
479
480 if (this->cowfs == NULL)
481 {
482 snprintf(master, sizeof(master), "%s/%s", this->dirname, MASTER_DIR);
483 snprintf(diff, sizeof(diff), "%s/%s", this->dirname, DIFF_DIR);
484 snprintf(mount, sizeof(mount), "%s/%s", this->dirname, UNION_DIR);
485
486 this->cowfs = cowfs_create(master, diff, mount);
487 if (this->cowfs)
488 {
489 return TRUE;
490 }
491 }
492 return FALSE;
493 }
494
495 /**
496 * load args configuration from file
497 */
498 char *loadargs(private_guest_t *this)
499 {
500 FILE *file;
501 char buf[512], *args = NULL;
502
503 file = fdopen(openat(this->dir, ARGS_FILE, O_RDONLY, PERM), "r");
504 if (file)
505 {
506 if (fgets(buf, sizeof(buf), file))
507 {
508 args = strdup(buf);
509 }
510 fclose(file);
511 }
512 return args;
513 }
514
515 /**
516 * save args configuration to file
517 */
518 bool saveargs(private_guest_t *this, char *args)
519 {
520 FILE *file;
521 bool retval = FALSE;
522
523 file = fdopen(openat(this->dir, ARGS_FILE, O_RDWR | O_CREAT | O_TRUNC,
524 PERM), "w");
525 if (file)
526 {
527 if (fprintf(file, "%s", args) > 0)
528 {
529 retval = TRUE;
530 }
531 fclose(file);
532 }
533 return retval;
534 }
535
536 /**
537 * Implementation of guest_t.destroy.
538 */
539 static void destroy(private_guest_t *this)
540 {
541 stop(this, NULL);
542 umount_unionfs(this);
543 if (this->dir > 0)
544 {
545 close(this->dir);
546 }
547 this->ifaces->destroy(this->ifaces);
548 free(this->dirname);
549 free(this->args);
550 free(this->name);
551 free(this);
552 }
553
554 /**
555 * generic guest constructor
556 */
557 static private_guest_t *guest_create_generic(char *parent, char *name,
558 bool create)
559 {
560 char cwd[PATH_MAX];
561 private_guest_t *this = malloc_thing(private_guest_t);
562
563 this->public.get_name = (void*)get_name;
564 this->public.get_pid = (pid_t(*)(guest_t*))get_pid;
565 this->public.get_state = (guest_state_t(*)(guest_t*))get_state;
566 this->public.create_iface = (iface_t*(*)(guest_t*,char*))create_iface;
567 this->public.destroy_iface = (void(*)(guest_t*,iface_t*))destroy_iface;
568 this->public.create_iface_enumerator = (enumerator_t*(*)(guest_t*))create_iface_enumerator;
569 this->public.start = (void*)start;
570 this->public.stop = (void*)stop;
571 this->public.load_template = (bool(*)(guest_t*, char *path))load_template;
572 this->public.exec = (int(*)(guest_t*, void(*cb)(void*,char*,size_t),void*,char*,...))exec;
573 this->public.exec_str = (int(*)(guest_t*, void(*cb)(void*,char*),bool,void*,char*,...))exec_str;
574 this->public.sigchild = (void(*)(guest_t*))sigchild;
575 this->public.destroy = (void*)destroy;
576
577 if (*parent == '/' || getcwd(cwd, sizeof(cwd)) == NULL)
578 {
579 asprintf(&this->dirname, "%s/%s", parent, name);
580 }
581 else
582 {
583 asprintf(&this->dirname, "%s/%s/%s", cwd, parent, name);
584 }
585 if (create)
586 {
587 mkdir(this->dirname, PERME);
588 }
589 this->dir = open(this->dirname, O_DIRECTORY, PERME);
590 if (this->dir < 0)
591 {
592 DBG1("opening guest directory '%s' failed: %m", this->dirname);
593 free(this->dirname);
594 free(this);
595 return NULL;
596 }
597 this->pid = 0;
598 this->state = GUEST_STOPPED;
599 this->mconsole = NULL;
600 this->ifaces = linked_list_create();
601 this->args = NULL;
602 this->name = strdup(name);
603 this->cowfs = NULL;
604
605 return this;
606 }
607
608 /**
609 * create a symlink to old called new in our working dir
610 */
611 static bool make_symlink(private_guest_t *this, char *old, char *new)
612 {
613 char cwd[PATH_MAX];
614 char buf[PATH_MAX];
615
616 if (*old == '/' || getcwd(cwd, sizeof(cwd)) == NULL)
617 {
618 snprintf(buf, sizeof(buf), "%s", old);
619 }
620 else
621 {
622 snprintf(buf, sizeof(buf), "%s/%s", cwd, old);
623 }
624 return symlinkat(buf, this->dir, new) == 0;
625 }
626
627
628 /**
629 * create the guest instance, including required dirs and mounts
630 */
631 guest_t *guest_create(char *parent, char *name, char *kernel,
632 char *master, char *args)
633 {
634 private_guest_t *this = guest_create_generic(parent, name, TRUE);
635
636 if (this == NULL)
637 {
638 return NULL;
639 }
640
641 if (!make_symlink(this, master, MASTER_DIR) ||
642 !make_symlink(this, kernel, KERNEL_FILE))
643 {
644 DBG1("creating master/kernel symlink failed: %m");
645 destroy(this);
646 return NULL;
647 }
648
649 if (mkdirat(this->dir, UNION_DIR, PERME) != 0 ||
650 mkdirat(this->dir, DIFF_DIR, PERME) != 0)
651 {
652 DBG1("unable to create directories for '%s': %m", name);
653 destroy(this);
654 return NULL;
655 }
656
657 this->args = args;
658 if (args && !saveargs(this, args))
659 {
660 destroy(this);
661 return NULL;
662 }
663
664 if (!mount_unionfs(this))
665 {
666 destroy(this);
667 return NULL;
668 }
669
670 return &this->public;
671 }
672
673 /**
674 * load an already created guest
675 */
676 guest_t *guest_load(char *parent, char *name)
677 {
678 private_guest_t *this = guest_create_generic(parent, name, FALSE);
679
680 if (this == NULL)
681 {
682 return NULL;
683 }
684
685 this->args = loadargs(this);
686
687 if (!mount_unionfs(this))
688 {
689 destroy(this);
690 return NULL;
691 }
692
693 return &this->public;
694 }
695