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