child-delete: Reply as usual when concurrently rekeying the IKE_SA
[strongswan.git] / src / dumm / guest.c
1 /*
2 * Copyright (C) 2008-2009 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 <utils/debug.h>
32 #include <collections/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 | S_IROTH | S_IXOTH)
40 #define PERM (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH)
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 METHOD(guest_t, get_name, char*,
86 private_guest_t *this)
87 {
88 return this->name;
89 }
90
91 METHOD(guest_t, create_iface, iface_t*,
92 private_guest_t *this, char *name)
93 {
94 enumerator_t *enumerator;
95 iface_t *iface;
96
97 if (this->state != GUEST_RUNNING)
98 {
99 DBG1(DBG_LIB, "guest '%s' not running, unable to add interface",
100 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(DBG_LIB, "guest '%s' already has an interface '%s'",
110 this->name, name);
111 enumerator->destroy(enumerator);
112 return NULL;
113 }
114 }
115 enumerator->destroy(enumerator);
116
117 iface = iface_create(name, &this->public, this->mconsole);
118 if (iface)
119 {
120 this->ifaces->insert_last(this->ifaces, iface);
121 }
122 return iface;
123 }
124
125 METHOD(guest_t, destroy_iface, void,
126 private_guest_t *this, iface_t *iface)
127 {
128 enumerator_t *enumerator;
129 iface_t *current;
130
131 enumerator = this->ifaces->create_enumerator(this->ifaces);
132 while (enumerator->enumerate(enumerator, (void**)&current))
133 {
134 if (current == iface)
135 {
136 this->ifaces->remove_at(this->ifaces, enumerator);
137 current->destroy(current);
138 break;
139 }
140 }
141 enumerator->destroy(enumerator);
142 }
143
144 METHOD(guest_t, create_iface_enumerator, enumerator_t*,
145 private_guest_t *this)
146 {
147 return this->ifaces->create_enumerator(this->ifaces);
148 }
149
150 METHOD(guest_t, get_state, guest_state_t,
151 private_guest_t *this)
152 {
153 return this->state;
154 }
155
156 METHOD(guest_t, get_pid, pid_t,
157 private_guest_t *this)
158 {
159 return this->pid;
160 }
161
162 /**
163 * write format string to a buffer, and advance buffer position
164 */
165 static char* write_arg(char **pos, size_t *left, char *format, ...)
166 {
167 size_t len;
168 char *res = NULL;
169 va_list args;
170
171 va_start(args, format);
172 len = vsnprintf(*pos, *left, format, args);
173 va_end(args);
174 if (len < *left)
175 {
176 res = *pos;
177 len++;
178 *pos += len + 1;
179 *left -= len + 1;
180 }
181 return res;
182 }
183
184 METHOD(guest_t, stop, void,
185 private_guest_t *this, idle_function_t idle)
186 {
187 if (this->state != GUEST_STOPPED)
188 {
189 this->state = GUEST_STOPPING;
190 this->ifaces->destroy_offset(this->ifaces, offsetof(iface_t, destroy));
191 this->ifaces = linked_list_create();
192 kill(this->pid, SIGINT);
193 while (this->state != GUEST_STOPPED)
194 {
195 if (idle)
196 {
197 idle();
198 }
199 else
200 {
201 usleep(50000);
202 }
203 }
204 unlinkat(this->dir, PID_FILE, 0);
205 this->pid = 0;
206 }
207 }
208
209 /**
210 * save pid in file
211 */
212 void savepid(private_guest_t *this)
213 {
214 FILE *file;
215
216 file = fdopen(openat(this->dir, PID_FILE, O_RDWR | O_CREAT | O_TRUNC,
217 PERM), "w");
218 if (file)
219 {
220 fprintf(file, "%d", this->pid);
221 fclose(file);
222 }
223 }
224
225 METHOD(guest_t, start, bool,
226 private_guest_t *this, invoke_function_t invoke, void* data,
227 idle_function_t idle)
228 {
229 char buf[2048];
230 char *notify;
231 char *pos = buf;
232 char *args[32];
233 int i = 0;
234 size_t left = sizeof(buf);
235
236 memset(args, 0, sizeof(args));
237
238 if (this->state != GUEST_STOPPED)
239 {
240 DBG1(DBG_LIB, "unable to start guest in state %N", guest_state_names,
241 this->state);
242 return FALSE;
243 }
244 this->state = GUEST_STARTING;
245
246 notify = write_arg(&pos, &left, "%s/%s", this->dirname, NOTIFY_FILE);
247
248 args[i++] = write_arg(&pos, &left, "nice");
249 args[i++] = write_arg(&pos, &left, "%s/%s", this->dirname, KERNEL_FILE);
250 args[i++] = write_arg(&pos, &left, "root=/dev/root");
251 args[i++] = write_arg(&pos, &left, "rootfstype=hostfs");
252 args[i++] = write_arg(&pos, &left, "rootflags=%s/%s", this->dirname, UNION_DIR);
253 args[i++] = write_arg(&pos, &left, "uml_dir=%s", this->dirname);
254 args[i++] = write_arg(&pos, &left, "umid=%s", this->name);
255 args[i++] = write_arg(&pos, &left, "mconsole=notify:%s", notify);
256 args[i++] = write_arg(&pos, &left, "con=null");
257 if (this->args)
258 {
259 args[i++] = this->args;
260 }
261
262 this->pid = invoke(data, &this->public, args, i);
263 if (!this->pid)
264 {
265 this->state = GUEST_STOPPED;
266 return FALSE;
267 }
268 savepid(this);
269
270 /* open mconsole */
271 this->mconsole = mconsole_create(notify, idle);
272 if (this->mconsole == NULL)
273 {
274 DBG1(DBG_LIB, "opening mconsole at '%s' failed, stopping guest", buf);
275 stop(this, NULL);
276 return FALSE;
277 }
278
279 this->state = GUEST_RUNNING;
280 return TRUE;
281 }
282
283 METHOD(guest_t, add_overlay, bool,
284 private_guest_t *this, char *path)
285 {
286 if (path == NULL)
287 {
288 return FALSE;
289 }
290
291 if (access(path, F_OK) != 0)
292 {
293 if (!mkdir_p(path, PERME))
294 {
295 DBG1(DBG_LIB, "creating overlay for guest '%s' failed: %m",
296 this->name);
297 return FALSE;
298 }
299 }
300
301 return this->cowfs->add_overlay(this->cowfs, path);
302 }
303
304 METHOD(guest_t, del_overlay, bool,
305 private_guest_t *this, char *path)
306 {
307 return this->cowfs->del_overlay(this->cowfs, path);
308 }
309
310 METHOD(guest_t, pop_overlay, bool,
311 private_guest_t *this)
312 {
313 return this->cowfs->pop_overlay(this->cowfs);
314 }
315
316 /**
317 * Variadic version of the exec function
318 */
319 static int vexec(private_guest_t *this, void(*cb)(void*,char*,size_t), void *data,
320 char *cmd, va_list args)
321 {
322 char buf[1024];
323 size_t len;
324
325 if (this->mconsole)
326 {
327 len = vsnprintf(buf, sizeof(buf), cmd, args);
328
329 if (len > 0 && len < sizeof(buf))
330 {
331 return this->mconsole->exec(this->mconsole, cb, data, buf);
332 }
333 }
334 return -1;
335 }
336
337 METHOD(guest_t, exec, int,
338 private_guest_t *this, void(*cb)(void*,char*,size_t), void *data,
339 char *cmd, ...)
340 {
341 int res;
342 va_list args;
343 va_start(args, cmd);
344 res = vexec(this, cb, data, cmd, args);
345 va_end(args);
346 return res;
347 }
348
349 typedef struct {
350 chunk_t buf;
351 void (*cb)(void*,char*);
352 void *data;
353 } exec_str_t;
354
355 /**
356 * callback that combines chunks to a string. if a callback is given, the string
357 * is split at newlines and the callback is called for each line.
358 */
359 static void exec_str_cb(exec_str_t *data, char *buf, size_t len)
360 {
361 if (!data->buf.ptr)
362 {
363 data->buf = chunk_alloc(len + 1);
364 memcpy(data->buf.ptr, buf, len);
365 data->buf.ptr[len] = '\0';
366 }
367 else
368 {
369 size_t newlen = strlen(data->buf.ptr) + len + 1;
370 if (newlen > data->buf.len)
371 {
372 data->buf.ptr = realloc(data->buf.ptr, newlen);
373 data->buf.len = newlen;
374 }
375 strncat(data->buf.ptr, buf, len);
376 }
377
378 if (data->cb)
379 {
380 char *nl;
381 while ((nl = strchr(data->buf.ptr, '\n')) != NULL)
382 {
383 *nl++ = '\0';
384 data->cb(data->data, data->buf.ptr);
385 memmove(data->buf.ptr, nl, strlen(nl) + 1);
386 }
387 }
388 }
389
390 METHOD(guest_t, exec_str, int,
391 private_guest_t *this, void(*cb)(void*,char*), bool lines, void *data,
392 char *cmd, ...)
393 {
394 int res;
395 va_list args;
396 va_start(args, cmd);
397 if (cb)
398 {
399 exec_str_t exec = { chunk_empty, NULL, NULL };
400 if (lines)
401 {
402 exec.cb = cb;
403 exec.data = data;
404 }
405 res = vexec(this, (void(*)(void*,char*,size_t))exec_str_cb, &exec, cmd, args);
406 if (exec.buf.ptr)
407 {
408 if (!lines || strlen(exec.buf.ptr) > 0)
409 {
410 /* return the complete string or the remaining stuff in the
411 * buffer (i.e. when there was no newline at the end) */
412 cb(data, exec.buf.ptr);
413 }
414 chunk_free(&exec.buf);
415 }
416 }
417 else
418 {
419 res = vexec(this, NULL, NULL, cmd, args);
420 }
421 va_end(args);
422 return res;
423 }
424
425 METHOD(guest_t, sigchild, void,
426 private_guest_t *this)
427 {
428 DESTROY_IF(this->mconsole);
429 this->mconsole = NULL;
430 this->state = GUEST_STOPPED;
431 }
432
433 /**
434 * umount the union filesystem
435 */
436 static bool umount_unionfs(private_guest_t *this)
437 {
438 if (this->cowfs)
439 {
440 this->cowfs->destroy(this->cowfs);
441 this->cowfs = NULL;
442 return TRUE;
443 }
444 return FALSE;
445 }
446
447 /**
448 * mount the union filesystem
449 */
450 static bool mount_unionfs(private_guest_t *this)
451 {
452 char master[PATH_MAX];
453 char diff[PATH_MAX];
454 char mount[PATH_MAX];
455
456 if (this->cowfs == NULL)
457 {
458 snprintf(master, sizeof(master), "%s/%s", this->dirname, MASTER_DIR);
459 snprintf(diff, sizeof(diff), "%s/%s", this->dirname, DIFF_DIR);
460 snprintf(mount, sizeof(mount), "%s/%s", this->dirname, UNION_DIR);
461
462 this->cowfs = cowfs_create(master, diff, mount);
463 if (this->cowfs)
464 {
465 return TRUE;
466 }
467 }
468 return FALSE;
469 }
470
471 /**
472 * load args configuration from file
473 */
474 char *loadargs(private_guest_t *this)
475 {
476 FILE *file;
477 char buf[512], *args = NULL;
478
479 file = fdopen(openat(this->dir, ARGS_FILE, O_RDONLY, PERM), "r");
480 if (file)
481 {
482 if (fgets(buf, sizeof(buf), file))
483 {
484 args = strdup(buf);
485 }
486 fclose(file);
487 }
488 return args;
489 }
490
491 /**
492 * save args configuration to file
493 */
494 bool saveargs(private_guest_t *this, char *args)
495 {
496 FILE *file;
497 bool retval = FALSE;
498
499 file = fdopen(openat(this->dir, ARGS_FILE, O_RDWR | O_CREAT | O_TRUNC,
500 PERM), "w");
501 if (file)
502 {
503 if (fprintf(file, "%s", args) > 0)
504 {
505 retval = TRUE;
506 }
507 fclose(file);
508 }
509 return retval;
510 }
511
512 METHOD(guest_t, destroy, void,
513 private_guest_t *this)
514 {
515 stop(this, NULL);
516 umount_unionfs(this);
517 if (this->dir > 0)
518 {
519 close(this->dir);
520 }
521 this->ifaces->destroy(this->ifaces);
522 free(this->dirname);
523 free(this->args);
524 free(this->name);
525 free(this);
526 }
527
528 /**
529 * generic guest constructor
530 */
531 static private_guest_t *guest_create_generic(char *parent, char *name,
532 bool create)
533 {
534 char cwd[PATH_MAX];
535 private_guest_t *this;
536
537 INIT(this,
538 .public = {
539 .get_name = _get_name,
540 .get_pid = _get_pid,
541 .get_state = _get_state,
542 .create_iface = _create_iface,
543 .destroy_iface = _destroy_iface,
544 .create_iface_enumerator = _create_iface_enumerator,
545 .start = _start,
546 .stop = _stop,
547 .add_overlay = _add_overlay,
548 .del_overlay = _del_overlay,
549 .pop_overlay = _pop_overlay,
550 .exec = _exec,
551 .exec_str = _exec_str,
552 .sigchild = _sigchild,
553 .destroy = _destroy,
554 }
555 );
556
557 if (*parent == '/' || getcwd(cwd, sizeof(cwd)) == NULL)
558 {
559 if (asprintf(&this->dirname, "%s/%s", parent, name) < 0)
560 {
561 this->dirname = NULL;
562 }
563 }
564 else
565 {
566 if (asprintf(&this->dirname, "%s/%s/%s", cwd, parent, name) < 0)
567 {
568 this->dirname = NULL;
569 }
570 }
571 if (this->dirname == NULL)
572 {
573 free(this);
574 return NULL;
575 }
576 if (create)
577 {
578 mkdir(this->dirname, PERME);
579 }
580 this->dir = open(this->dirname, O_DIRECTORY, PERME);
581 if (this->dir < 0)
582 {
583 DBG1(DBG_LIB, "opening guest directory '%s' failed: %m", this->dirname);
584 free(this->dirname);
585 free(this);
586 return NULL;
587 }
588 this->state = GUEST_STOPPED;
589 this->ifaces = linked_list_create();
590 this->name = strdup(name);
591
592 return this;
593 }
594
595 /**
596 * create a symlink to old called new in our working dir
597 */
598 static bool make_symlink(private_guest_t *this, char *old, char *new)
599 {
600 char cwd[PATH_MAX];
601 char buf[PATH_MAX];
602
603 if (*old == '/' || getcwd(cwd, sizeof(cwd)) == NULL)
604 {
605 snprintf(buf, sizeof(buf), "%s", old);
606 }
607 else
608 {
609 snprintf(buf, sizeof(buf), "%s/%s", cwd, old);
610 }
611 return symlinkat(buf, this->dir, new) == 0;
612 }
613
614
615 /**
616 * create the guest instance, including required dirs and mounts
617 */
618 guest_t *guest_create(char *parent, char *name, char *kernel,
619 char *master, char *args)
620 {
621 private_guest_t *this = guest_create_generic(parent, name, TRUE);
622
623 if (this == NULL)
624 {
625 return NULL;
626 }
627
628 if (!make_symlink(this, master, MASTER_DIR) ||
629 !make_symlink(this, kernel, KERNEL_FILE))
630 {
631 DBG1(DBG_LIB, "creating master/kernel symlink failed: %m");
632 destroy(this);
633 return NULL;
634 }
635
636 if (mkdirat(this->dir, UNION_DIR, PERME) != 0 ||
637 mkdirat(this->dir, DIFF_DIR, PERME) != 0)
638 {
639 DBG1(DBG_LIB, "unable to create directories for '%s': %m", name);
640 destroy(this);
641 return NULL;
642 }
643
644 this->args = args;
645 if (args && !saveargs(this, args))
646 {
647 destroy(this);
648 return NULL;
649 }
650
651 if (!mount_unionfs(this))
652 {
653 destroy(this);
654 return NULL;
655 }
656
657 return &this->public;
658 }
659
660 /**
661 * load an already created guest
662 */
663 guest_t *guest_load(char *parent, char *name)
664 {
665 private_guest_t *this = guest_create_generic(parent, name, FALSE);
666
667 if (this == NULL)
668 {
669 return NULL;
670 }
671
672 this->args = loadargs(this);
673
674 if (!mount_unionfs(this))
675 {
676 destroy(this);
677 return NULL;
678 }
679
680 return &this->public;
681 }
682