2 * Copyright (C) 2008 Tobias Brunner
3 * Copyright (C) 2007 Martin Willi
4 * Hochschule fuer Technik Rapperswil
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>.
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
19 #include <sys/types.h>
32 #include <utils/linked_list.h>
39 #define PERME (S_IRWXU | S_IRWXG)
40 #define PERM (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP)
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"
52 typedef struct private_guest_t private_guest_t
;
54 struct private_guest_t
{
55 /** implemented public interface */
57 /** name of the guest */
59 /** directory of guest */
61 /** directory name of guest */
63 /** amount of memory for guest, in MB */
65 /** pid of guest child process */
69 /** FUSE cowfs instance */
71 /** mconsole to control running UML */
73 /** list of interfaces attached to the guest */
74 linked_list_t
*ifaces
;
77 ENUM(guest_state_names
, GUEST_STOPPED
, GUEST_STOPPING
,
86 * Implementation of guest_t.get_name.
88 static char* get_name(private_guest_t
*this)
94 * Implementation of guest_t.create_iface.
96 static iface_t
* create_iface(private_guest_t
*this, char *name
)
98 enumerator_t
*enumerator
;
101 if (this->state
!= GUEST_RUNNING
)
103 DBG1("guest '%s' not running, unable to add interface", this->name
);
107 enumerator
= this->ifaces
->create_enumerator(this->ifaces
);
108 while (enumerator
->enumerate(enumerator
, (void**)&iface
))
110 if (streq(name
, iface
->get_guestif(iface
)))
112 DBG1("guest '%s' already has an interface '%s'", this->name
, name
);
113 enumerator
->destroy(enumerator
);
117 enumerator
->destroy(enumerator
);
119 iface
= iface_create(name
, &this->public, this->mconsole
);
122 this->ifaces
->insert_last(this->ifaces
, iface
);
128 * Implementation of guest_t.destroy_iface.
130 static void destroy_iface(private_guest_t
*this, iface_t
*iface
)
132 enumerator_t
*enumerator
;
135 enumerator
= this->ifaces
->create_enumerator(this->ifaces
);
136 while (enumerator
->enumerate(enumerator
, (void**)¤t
))
138 if (current
== iface
)
140 this->ifaces
->remove_at(this->ifaces
, enumerator
);
141 current
->destroy(current
);
145 enumerator
->destroy(enumerator
);
149 * Implementation of guest_t.create_iface_enumerator.
151 static enumerator_t
* create_iface_enumerator(private_guest_t
*this)
153 return this->ifaces
->create_enumerator(this->ifaces
);
157 * Implementation of guest_t.get_state.
159 static guest_state_t
get_state(private_guest_t
*this)
165 * Implementation of guest_t.get_pid.
167 static pid_t
get_pid(private_guest_t
*this)
173 * write format string to a buffer, and advance buffer position
175 static char* write_arg(char **pos
, size_t *left
, char *format
, ...)
181 va_start(args
, format
);
182 len
= vsnprintf(*pos
, *left
, format
, args
);
195 * Implementation of guest_t.stop.
197 static void stop(private_guest_t
*this, idle_function_t idle
)
199 if (this->state
!= GUEST_STOPPED
)
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
)
216 unlinkat(this->dir
, PID_FILE
, 0);
224 void savepid(private_guest_t
*this)
228 file
= fdopen(openat(this->dir
, PID_FILE
, O_RDWR
| O_CREAT
| O_TRUNC
,
232 fprintf(file
, "%d", this->pid
);
238 * Implementation of guest_t.start.
240 static bool start(private_guest_t
*this, invoke_function_t invoke
, void* data
,
241 idle_function_t idle
)
248 size_t left
= sizeof(buf
);
250 memset(args
, 0, sizeof(args
));
252 if (this->state
!= GUEST_STOPPED
)
254 DBG1("unable to start guest in state %N", guest_state_names
, this->state
);
257 this->state
= GUEST_STARTING
;
259 notify
= write_arg(&pos
, &left
, "%s/%s", this->dirname
, NOTIFY_FILE
);
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
, "mem=%dM", this->mem
);
269 args
[i
++] = write_arg(&pos
, &left
, "mconsole=notify:%s", notify
);
270 args
[i
++] = write_arg(&pos
, &left
, "con=null");
272 this->pid
= invoke(data
, &this->public, args
, i
);
275 this->state
= GUEST_STOPPED
;
281 this->mconsole
= mconsole_create(notify
, idle
);
282 if (this->mconsole
== NULL
)
284 DBG1("opening mconsole at '%s' failed, stopping guest", buf
);
289 this->state
= GUEST_RUNNING
;
294 * Implementation of guest_t.load_template.
296 static bool load_template(private_guest_t
*this, char *path
)
304 return this->cowfs
->set_overlay(this->cowfs
, NULL
);
307 len
= snprintf(dir
, sizeof(dir
), "%s/%s", path
, this->name
);
308 if (len
< 0 || len
>= sizeof(dir
))
312 if (access(dir
, F_OK
) != 0)
314 if (!mkdir_p(dir
, PERME
))
316 DBG1("creating overlay for guest '%s' failed: %m", this->name
);
320 if (!this->cowfs
->set_overlay(this->cowfs
, dir
))
324 while (this->ifaces
->remove_last(this->ifaces
, (void**)&iface
) == SUCCESS
)
326 iface
->destroy(iface
);
332 * Variadic version of the exec function
334 static int vexec(private_guest_t
*this, void(*cb
)(void*,char*,size_t), void *data
,
335 char *cmd
, va_list args
)
342 len
= vsnprintf(buf
, sizeof(buf
), cmd
, args
);
344 if (len
> 0 && len
< sizeof(buf
))
346 return this->mconsole
->exec(this->mconsole
, cb
, data
, buf
);
353 * Implementation of guest_t.exec
355 static int exec(private_guest_t
*this, void(*cb
)(void*,char*,size_t), void *data
,
361 res
= vexec(this, cb
, data
, cmd
, args
);
368 void (*cb
)(void*,char*);
373 * callback that combines chunks to a string. if a callback is given, the string
374 * is split at newlines and the callback is called for each line.
376 static void exec_str_cb(exec_str_t
*data
, char *buf
, size_t len
)
380 data
->buf
= chunk_alloc(len
+ 1);
381 memcpy(data
->buf
.ptr
, buf
, len
);
382 data
->buf
.ptr
[len
] = '\0';
386 size_t newlen
= strlen(data
->buf
.ptr
) + len
+ 1;
387 if (newlen
> data
->buf
.len
)
389 data
->buf
.ptr
= realloc(data
->buf
.ptr
, newlen
);
390 data
->buf
.len
= newlen
;
392 strncat(data
->buf
.ptr
, buf
, len
);
398 while ((nl
= strchr(data
->buf
.ptr
, '\n')) != NULL
)
401 data
->cb(data
->data
, data
->buf
.ptr
);
402 memmove(data
->buf
.ptr
, nl
, strlen(nl
) + 1);
408 * Implementation of guest_t.exec_str
410 static int exec_str(private_guest_t
*this, void(*cb
)(void*,char*), bool lines
,
411 void *data
, char *cmd
, ...)
418 exec_str_t exec
= { chunk_empty
, NULL
, NULL
};
424 res
= vexec(this, (void(*)(void*,char*,size_t))exec_str_cb
, &exec
, cmd
, args
);
427 if (!lines
|| strlen(exec
.buf
.ptr
) > 0)
429 /* return the complete string or the remaining stuff in the
430 * buffer (i.e. when there was no newline at the end) */
431 cb(data
, exec
.buf
.ptr
);
433 chunk_free(&exec
.buf
);
438 res
= vexec(this, NULL
, NULL
, cmd
, args
);
445 * Implementation of guest_t.sigchild.
447 static void sigchild(private_guest_t
*this)
449 DESTROY_IF(this->mconsole
);
450 this->mconsole
= NULL
;
451 this->state
= GUEST_STOPPED
;
455 * umount the union filesystem
457 static bool umount_unionfs(private_guest_t
*this)
461 this->cowfs
->destroy(this->cowfs
);
469 * mount the union filesystem
471 static bool mount_unionfs(private_guest_t
*this)
473 char master
[PATH_MAX
];
475 char mount
[PATH_MAX
];
477 if (this->cowfs
== NULL
)
479 snprintf(master
, sizeof(master
), "%s/%s", this->dirname
, MASTER_DIR
);
480 snprintf(diff
, sizeof(diff
), "%s/%s", this->dirname
, DIFF_DIR
);
481 snprintf(mount
, sizeof(mount
), "%s/%s", this->dirname
, UNION_DIR
);
483 this->cowfs
= cowfs_create(master
, diff
, mount
);
493 * load memory configuration from file
495 int loadmem(private_guest_t
*this)
500 file
= fdopen(openat(this->dir
, MEMORY_FILE
, O_RDONLY
, PERM
), "r");
503 if (fscanf(file
, "%d", &mem
) <= 0)
513 * save memory configuration to file
515 bool savemem(private_guest_t
*this, int mem
)
520 file
= fdopen(openat(this->dir
, MEMORY_FILE
, O_RDWR
| O_CREAT
| O_TRUNC
,
524 if (fprintf(file
, "%d", mem
) > 0)
534 * Implementation of guest_t.destroy.
536 static void destroy(private_guest_t
*this)
539 umount_unionfs(this);
544 this->ifaces
->destroy(this->ifaces
);
551 * generic guest constructor
553 static private_guest_t
*guest_create_generic(char *parent
, char *name
,
557 private_guest_t
*this = malloc_thing(private_guest_t
);
559 this->public.get_name
= (void*)get_name
;
560 this->public.get_pid
= (pid_t(*)(guest_t
*))get_pid
;
561 this->public.get_state
= (guest_state_t(*)(guest_t
*))get_state
;
562 this->public.create_iface
= (iface_t
*(*)(guest_t
*,char*))create_iface
;
563 this->public.destroy_iface
= (void(*)(guest_t
*,iface_t
*))destroy_iface
;
564 this->public.create_iface_enumerator
= (enumerator_t
*(*)(guest_t
*))create_iface_enumerator
;
565 this->public.start
= (void*)start
;
566 this->public.stop
= (void*)stop
;
567 this->public.load_template
= (bool(*)(guest_t
*, char *path
))load_template
;
568 this->public.exec
= (int(*)(guest_t
*, void(*cb
)(void*,char*,size_t),void*,char*,...))exec
;
569 this->public.exec_str
= (int(*)(guest_t
*, void(*cb
)(void*,char*),bool,void*,char*,...))exec_str
;
570 this->public.sigchild
= (void(*)(guest_t
*))sigchild
;
571 this->public.destroy
= (void*)destroy
;
573 if (*parent
== '/' || getcwd(cwd
, sizeof(cwd
)) == NULL
)
575 asprintf(&this->dirname
, "%s/%s", parent
, name
);
579 asprintf(&this->dirname
, "%s/%s/%s", cwd
, parent
, name
);
583 mkdir(this->dirname
, PERME
);
585 this->dir
= open(this->dirname
, O_DIRECTORY
, PERME
);
588 DBG1("opening guest directory '%s' failed: %m", this->dirname
);
594 this->state
= GUEST_STOPPED
;
595 this->mconsole
= NULL
;
596 this->ifaces
= linked_list_create();
598 this->name
= strdup(name
);
605 * create a symlink to old called new in our working dir
607 static bool make_symlink(private_guest_t
*this, char *old
, char *new)
612 if (*old
== '/' || getcwd(cwd
, sizeof(cwd
)) == NULL
)
614 snprintf(buf
, sizeof(buf
), "%s", old
);
618 snprintf(buf
, sizeof(buf
), "%s/%s", cwd
, old
);
620 return symlinkat(buf
, this->dir
, new) == 0;
625 * create the guest instance, including required dirs and mounts
627 guest_t
*guest_create(char *parent
, char *name
, char *kernel
,
628 char *master
, int mem
)
630 private_guest_t
*this = guest_create_generic(parent
, name
, TRUE
);
637 if (!make_symlink(this, master
, MASTER_DIR
) ||
638 !make_symlink(this, kernel
, KERNEL_FILE
))
640 DBG1("creating master/kernel symlink failed: %m");
645 if (mkdirat(this->dir
, UNION_DIR
, PERME
) != 0 ||
646 mkdirat(this->dir
, DIFF_DIR
, PERME
) != 0)
648 DBG1("unable to create directories for '%s': %m", name
);
654 if (!savemem(this, mem
))
660 if (!mount_unionfs(this))
666 return &this->public;
670 * load an already created guest
672 guest_t
*guest_load(char *parent
, char *name
)
674 private_guest_t
*this = guest_create_generic(parent
, name
, FALSE
);
681 this->mem
= loadmem(this);
684 DBG1("unable to open memory configuration file: %m", name
);
689 if (!mount_unionfs(this))
695 return &this->public;