loading ruby scripts specified at command line
[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 #include <stdarg.h>
29
30 #include <debug.h>
31 #include <utils/linked_list.h>
32
33 #include "dumm.h"
34 #include "guest.h"
35 #include "mconsole.h"
36 #include "cowfs.h"
37
38 #define PERME (S_IRWXU | S_IRWXG)
39 #define PERM (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP)
40
41 #define MASTER_DIR "master"
42 #define DIFF_DIR "diff"
43 #define UNION_DIR "union"
44 #define MEMORY_FILE "mem"
45 #define PID_FILE "pid"
46 #define KERNEL_FILE "linux"
47 #define LOG_FILE "boot.log"
48 #define NOTIFY_FILE "notify"
49 #define PTYS 0
50
51 typedef struct private_guest_t private_guest_t;
52
53 struct private_guest_t {
54 /** implemented public interface */
55 guest_t public;
56 /** name of the guest */
57 char *name;
58 /** directory of guest */
59 int dir;
60 /** directory name of guest */
61 char *dirname;
62 /** amount of memory for guest, in MB */
63 int mem;
64 /** pid of guest child process */
65 int pid;
66 /** state of guest */
67 guest_state_t state;
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 enumerator_t *enumerator;
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 enumerator = this->ifaces->create_enumerator(this->ifaces);
107 while (enumerator->enumerate(enumerator, (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 enumerator->destroy(enumerator);
113 return NULL;
114 }
115 }
116 enumerator->destroy(enumerator);
117
118 iface = iface_create(name, &this->public, 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.destroy_iface.
128 */
129 static void destroy_iface(private_guest_t *this, iface_t *iface)
130 {
131 enumerator_t *enumerator;
132 iface_t *current;
133
134 enumerator = this->ifaces->create_enumerator(this->ifaces);
135 while (enumerator->enumerate(enumerator, (void**)&current))
136 {
137 if (current == iface)
138 {
139 this->ifaces->remove_at(this->ifaces, enumerator);
140 current->destroy(current);
141 break;
142 }
143 }
144 enumerator->destroy(enumerator);
145 }
146
147 /**
148 * Implementation of guest_t.create_iface_enumerator.
149 */
150 static enumerator_t* create_iface_enumerator(private_guest_t *this)
151 {
152 return this->ifaces->create_enumerator(this->ifaces);
153 }
154
155 /**
156 * Implementation of guest_t.get_state.
157 */
158 static guest_state_t get_state(private_guest_t *this)
159 {
160 return this->state;
161 }
162
163 /**
164 * Implementation of guest_t.get_pid.
165 */
166 static pid_t get_pid(private_guest_t *this)
167 {
168 return this->pid;
169 }
170
171 /**
172 * write format string to a buffer, and advance buffer position
173 */
174 static char* write_arg(char **pos, size_t *left, char *format, ...)
175 {
176 size_t len;
177 char *res = NULL;
178 va_list args;
179
180 va_start(args, format);
181 len = vsnprintf(*pos, *left, format, args);
182 va_end(args);
183 if (len < *left)
184 {
185 res = *pos;
186 len++;
187 *pos += len + 1;
188 *left -= len + 1;
189 }
190 return res;
191 }
192
193 /**
194 * Implementation of guest_t.stop.
195 */
196 static void stop(private_guest_t *this, idle_function_t idle)
197 {
198 if (this->state != GUEST_STOPPED)
199 {
200 this->state = GUEST_STOPPING;
201 this->ifaces->destroy_offset(this->ifaces, offsetof(iface_t, destroy));
202 this->ifaces = linked_list_create();
203 kill(this->pid, SIGINT);
204 while (this->state != GUEST_STOPPED)
205 {
206 if (idle)
207 {
208 idle();
209 }
210 else
211 {
212 usleep(50000);
213 }
214 }
215 unlinkat(this->dir, PID_FILE, 0);
216 }
217 }
218
219 /**
220 * save pid in file
221 */
222 void savepid(private_guest_t *this)
223 {
224 FILE *file;
225
226 file = fdopen(openat(this->dir, PID_FILE, O_RDWR | O_CREAT | O_TRUNC,
227 PERM), "w");
228 if (file)
229 {
230 fprintf(file, "%d", this->pid);
231 fclose(file);
232 }
233 }
234
235 /**
236 * Implementation of guest_t.start.
237 */
238 static bool start(private_guest_t *this, invoke_function_t invoke, void* data,
239 idle_function_t idle)
240 {
241 char buf[2048];
242 char *notify;
243 char *pos = buf;
244 char *args[32];
245 int i = 0;
246 size_t left = sizeof(buf);
247
248 memset(args, 0, sizeof(args));
249
250 if (this->state != GUEST_STOPPED)
251 {
252 DBG1("unable to start guest in state %N", guest_state_names, this->state);
253 return FALSE;
254 }
255 this->state = GUEST_STARTING;
256
257 notify = write_arg(&pos, &left, "%s/%s", this->dirname, NOTIFY_FILE);
258
259 args[i++] = write_arg(&pos, &left, "%s/%s", this->dirname, KERNEL_FILE);
260 args[i++] = write_arg(&pos, &left, "root=/dev/root");
261 args[i++] = write_arg(&pos, &left, "rootfstype=hostfs");
262 args[i++] = write_arg(&pos, &left, "rootflags=%s/%s", this->dirname, UNION_DIR);
263 args[i++] = write_arg(&pos, &left, "uml_dir=%s", this->dirname);
264 args[i++] = write_arg(&pos, &left, "umid=%s", this->name);
265 args[i++] = write_arg(&pos, &left, "mem=%dM", this->mem);
266 args[i++] = write_arg(&pos, &left, "mconsole=notify:%s", notify);
267 args[i++] = write_arg(&pos, &left, "con=null");
268
269 this->pid = invoke(data, &this->public, args, i);
270 if (!this->pid)
271 {
272 this->state = GUEST_STOPPED;
273 return FALSE;
274 }
275 savepid(this);
276
277 /* open mconsole */
278 this->mconsole = mconsole_create(notify, idle);
279 if (this->mconsole == NULL)
280 {
281 DBG1("opening mconsole at '%s' failed, stopping guest", buf);
282 stop(this, NULL);
283 return FALSE;
284 }
285
286 this->state = GUEST_RUNNING;
287 return TRUE;
288 }
289
290 /**
291 * Implementation of guest_t.load_template.
292 */
293 static bool load_template(private_guest_t *this, char *path)
294 {
295 char dir[PATH_MAX];
296 size_t len;
297 iface_t *iface;
298
299 if (path == NULL)
300 {
301 return this->cowfs->set_overlay(this->cowfs, NULL);
302 }
303
304 len = snprintf(dir, sizeof(dir), "%s/%s", path, this->name);
305 if (len < 0 || len >= sizeof(dir))
306 {
307 return FALSE;
308 }
309 if (access(dir, F_OK) != 0)
310 {
311 if (mkdir(dir, PERME) != 0)
312 {
313 DBG1("creating overlay for guest '%s' failed: %m", this->name);
314 return FALSE;
315 }
316 }
317 if (!this->cowfs->set_overlay(this->cowfs, dir))
318 {
319 return FALSE;
320 }
321 while (this->ifaces->remove_last(this->ifaces, (void**)&iface) == SUCCESS)
322 {
323 iface->destroy(iface);
324 }
325 return TRUE;
326 }
327
328 /**
329 * Implementation of gues_t.exec
330 */
331 static int exec(private_guest_t *this, void(*cb)(void*,char*), void *data,
332 char *cmd, ...)
333 {
334 char buf[1024];
335 size_t len;
336 va_list args;
337
338 if (this->mconsole)
339 {
340 va_start(args, cmd);
341 len = vsnprintf(buf, sizeof(buf), cmd, args);
342 va_end(args);
343
344 if (len > 0 && len < sizeof(buf))
345 {
346 return this->mconsole->exec(this->mconsole, cb, data, buf);
347 }
348 }
349 return -1;
350 }
351
352 /**
353 * Implementation of guest_t.sigchild.
354 */
355 static void sigchild(private_guest_t *this)
356 {
357 DESTROY_IF(this->mconsole);
358 this->mconsole = NULL;
359 this->state = GUEST_STOPPED;
360 }
361
362 /**
363 * umount the union filesystem
364 */
365 static bool umount_unionfs(private_guest_t *this)
366 {
367 if (this->cowfs)
368 {
369 this->cowfs->destroy(this->cowfs);
370 this->cowfs = NULL;
371 return TRUE;
372 }
373 return FALSE;
374 }
375
376 /**
377 * mount the union filesystem
378 */
379 static bool mount_unionfs(private_guest_t *this)
380 {
381 char master[PATH_MAX];
382 char diff[PATH_MAX];
383 char mount[PATH_MAX];
384
385 if (this->cowfs == NULL)
386 {
387 snprintf(master, sizeof(master), "%s/%s", this->dirname, MASTER_DIR);
388 snprintf(diff, sizeof(diff), "%s/%s", this->dirname, DIFF_DIR);
389 snprintf(mount, sizeof(mount), "%s/%s", this->dirname, UNION_DIR);
390
391 this->cowfs = cowfs_create(master, diff, mount);
392 if (this->cowfs)
393 {
394 return TRUE;
395 }
396 }
397 return FALSE;
398 }
399
400 /**
401 * load memory configuration from file
402 */
403 int loadmem(private_guest_t *this)
404 {
405 FILE *file;
406 int mem = 0;
407
408 file = fdopen(openat(this->dir, MEMORY_FILE, O_RDONLY, PERM), "r");
409 if (file)
410 {
411 if (fscanf(file, "%d", &mem) <= 0)
412 {
413 mem = 0;
414 }
415 fclose(file);
416 }
417 return mem;
418 }
419
420 /**
421 * save memory configuration to file
422 */
423 bool savemem(private_guest_t *this, int mem)
424 {
425 FILE *file;
426 bool retval = FALSE;
427
428 file = fdopen(openat(this->dir, MEMORY_FILE, O_RDWR | O_CREAT | O_TRUNC,
429 PERM), "w");
430 if (file)
431 {
432 if (fprintf(file, "%d", mem) > 0)
433 {
434 retval = TRUE;
435 }
436 fclose(file);
437 }
438 return retval;
439 }
440
441 /**
442 * Implementation of guest_t.destroy.
443 */
444 static void destroy(private_guest_t *this)
445 {
446 stop(this, NULL);
447 umount_unionfs(this);
448 if (this->dir > 0)
449 {
450 close(this->dir);
451 }
452 this->ifaces->destroy(this->ifaces);
453 free(this->dirname);
454 free(this->name);
455 free(this);
456 }
457
458 /**
459 * generic guest constructor
460 */
461 static private_guest_t *guest_create_generic(char *parent, char *name,
462 bool create)
463 {
464 char cwd[PATH_MAX];
465 private_guest_t *this = malloc_thing(private_guest_t);
466
467 this->public.get_name = (void*)get_name;
468 this->public.get_pid = (pid_t(*)(guest_t*))get_pid;
469 this->public.get_state = (guest_state_t(*)(guest_t*))get_state;
470 this->public.create_iface = (iface_t*(*)(guest_t*,char*))create_iface;
471 this->public.destroy_iface = (void(*)(guest_t*,iface_t*))destroy_iface;
472 this->public.create_iface_enumerator = (enumerator_t*(*)(guest_t*))create_iface_enumerator;
473 this->public.start = (void*)start;
474 this->public.stop = (void*)stop;
475 this->public.load_template = (bool(*)(guest_t*, char *path))load_template;
476 this->public.exec = (int(*)(guest_t*, void(*cb)(void*,char*,size_t), void *data, char *cmd, ...))exec;
477 this->public.sigchild = (void(*)(guest_t*))sigchild;
478 this->public.destroy = (void*)destroy;
479
480 if (*parent == '/' || getcwd(cwd, sizeof(cwd)) == NULL)
481 {
482 asprintf(&this->dirname, "%s/%s", parent, name);
483 }
484 else
485 {
486 asprintf(&this->dirname, "%s/%s/%s", cwd, parent, name);
487 }
488 if (create)
489 {
490 mkdir(this->dirname, PERME);
491 }
492 this->dir = open(this->dirname, O_DIRECTORY, PERME);
493 if (this->dir < 0)
494 {
495 DBG1("opening guest directory '%s' failed: %m", this->dirname);
496 free(this->dirname);
497 free(this);
498 return NULL;
499 }
500 this->pid = 0;
501 this->state = GUEST_STOPPED;
502 this->mconsole = NULL;
503 this->ifaces = linked_list_create();
504 this->mem = 0;
505 this->name = strdup(name);
506 this->cowfs = NULL;
507
508 return this;
509 }
510
511 /**
512 * create a symlink to old called new in our working dir
513 */
514 static bool make_symlink(private_guest_t *this, char *old, char *new)
515 {
516 char cwd[PATH_MAX];
517 char buf[PATH_MAX];
518
519 if (*old == '/' || getcwd(cwd, sizeof(cwd)) == NULL)
520 {
521 snprintf(buf, sizeof(buf), "%s", old);
522 }
523 else
524 {
525 snprintf(buf, sizeof(buf), "%s/%s", cwd, old);
526 }
527 return symlinkat(buf, this->dir, new) == 0;
528 }
529
530
531 /**
532 * create the guest instance, including required dirs and mounts
533 */
534 guest_t *guest_create(char *parent, char *name, char *kernel,
535 char *master, int mem)
536 {
537 private_guest_t *this = guest_create_generic(parent, name, TRUE);
538
539 if (this == NULL)
540 {
541 return NULL;
542 }
543
544 if (!make_symlink(this, master, MASTER_DIR) ||
545 !make_symlink(this, kernel, KERNEL_FILE))
546 {
547 DBG1("creating master/kernel symlink failed: %m");
548 destroy(this);
549 return NULL;
550 }
551
552 if (mkdirat(this->dir, UNION_DIR, PERME) != 0 ||
553 mkdirat(this->dir, DIFF_DIR, PERME) != 0)
554 {
555 DBG1("unable to create directories for '%s': %m", name);
556 destroy(this);
557 return NULL;
558 }
559
560 this->mem = mem;
561 if (!savemem(this, mem))
562 {
563 destroy(this);
564 return NULL;
565 }
566
567 if (!mount_unionfs(this))
568 {
569 destroy(this);
570 return NULL;
571 }
572
573 return &this->public;
574 }
575
576 /**
577 * load an already created guest
578 */
579 guest_t *guest_load(char *parent, char *name)
580 {
581 private_guest_t *this = guest_create_generic(parent, name, FALSE);
582
583 if (this == NULL)
584 {
585 return NULL;
586 }
587
588 this->mem = loadmem(this);
589 if (this->mem == 0)
590 {
591 DBG1("unable to open memory configuration file: %m", name);
592 destroy(this);
593 return NULL;
594 }
595
596 if (!mount_unionfs(this))
597 {
598 destroy(this);
599 return NULL;
600 }
601
602 return &this->public;
603 }
604