2 * Copyright (C) 2007 Martin Willi
3 * Hochschule fuer Technik Rapperswil
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>.
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
18 #include <sys/types.h>
31 #include <utils/linked_list.h>
38 #define PERME (S_IRWXU | S_IRWXG)
39 #define PERM (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP)
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"
51 typedef struct private_guest_t private_guest_t
;
53 struct private_guest_t
{
54 /** implemented public interface */
56 /** name of the guest */
58 /** directory of guest */
60 /** directory name of guest */
62 /** amount of memory for guest, in MB */
64 /** pid of guest child process */
68 /** FUSE cowfs instance */
70 /** mconsole to control running UML */
72 /** list of interfaces attached to the guest */
73 linked_list_t
*ifaces
;
76 ENUM(guest_state_names
, GUEST_STOPPED
, GUEST_STOPPING
,
85 * Implementation of guest_t.get_name.
87 static char* get_name(private_guest_t
*this)
93 * Implementation of guest_t.create_iface.
95 static iface_t
* create_iface(private_guest_t
*this, char *name
)
97 enumerator_t
*enumerator
;
100 if (this->state
!= GUEST_RUNNING
)
102 DBG1("guest '%s' not running, unable to add interface", this->name
);
106 enumerator
= this->ifaces
->create_enumerator(this->ifaces
);
107 while (enumerator
->enumerate(enumerator
, (void**)&iface
))
109 if (streq(name
, iface
->get_guestif(iface
)))
111 DBG1("guest '%s' already has an interface '%s'", this->name
, name
);
112 enumerator
->destroy(enumerator
);
116 enumerator
->destroy(enumerator
);
118 iface
= iface_create(name
, &this->public, this->mconsole
);
121 this->ifaces
->insert_last(this->ifaces
, iface
);
127 * Implementation of guest_t.destroy_iface.
129 static void destroy_iface(private_guest_t
*this, iface_t
*iface
)
131 enumerator_t
*enumerator
;
134 enumerator
= this->ifaces
->create_enumerator(this->ifaces
);
135 while (enumerator
->enumerate(enumerator
, (void**)¤t
))
137 if (current
== iface
)
139 this->ifaces
->remove_at(this->ifaces
, enumerator
);
140 current
->destroy(current
);
144 enumerator
->destroy(enumerator
);
148 * Implementation of guest_t.create_iface_enumerator.
150 static enumerator_t
* create_iface_enumerator(private_guest_t
*this)
152 return this->ifaces
->create_enumerator(this->ifaces
);
156 * Implementation of guest_t.get_state.
158 static guest_state_t
get_state(private_guest_t
*this)
164 * Implementation of guest_t.get_pid.
166 static pid_t
get_pid(private_guest_t
*this)
172 * write format string to a buffer, and advance buffer position
174 static char* write_arg(char **pos
, size_t *left
, char *format
, ...)
180 va_start(args
, format
);
181 len
= vsnprintf(*pos
, *left
, format
, args
);
194 * Implementation of guest_t.stop.
196 static void stop(private_guest_t
*this, idle_function_t idle
)
198 if (this->state
!= GUEST_STOPPED
)
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
)
215 unlinkat(this->dir
, PID_FILE
, 0);
222 void savepid(private_guest_t
*this)
226 file
= fdopen(openat(this->dir
, PID_FILE
, O_RDWR
| O_CREAT
| O_TRUNC
,
230 fprintf(file
, "%d", this->pid
);
236 * Implementation of guest_t.start.
238 static bool start(private_guest_t
*this, invoke_function_t invoke
, void* data
,
239 idle_function_t idle
)
246 size_t left
= sizeof(buf
);
248 memset(args
, 0, sizeof(args
));
250 if (this->state
!= GUEST_STOPPED
)
252 DBG1("unable to start guest in state %N", guest_state_names
, this->state
);
255 this->state
= GUEST_STARTING
;
257 notify
= write_arg(&pos
, &left
, "%s/%s", this->dirname
, NOTIFY_FILE
);
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");
269 this->pid
= invoke(data
, &this->public, args
, i
);
272 this->state
= GUEST_STOPPED
;
278 this->mconsole
= mconsole_create(notify
, idle
);
279 if (this->mconsole
== NULL
)
281 DBG1("opening mconsole at '%s' failed, stopping guest", buf
);
286 this->state
= GUEST_RUNNING
;
291 * Implementation of guest_t.load_template.
293 static bool load_template(private_guest_t
*this, char *path
)
301 return this->cowfs
->set_overlay(this->cowfs
, NULL
);
304 len
= snprintf(dir
, sizeof(dir
), "%s/%s", path
, this->name
);
305 if (len
< 0 || len
>= sizeof(dir
))
309 if (access(dir
, F_OK
) != 0)
311 if (mkdir(dir
, PERME
) != 0)
313 DBG1("creating overlay for guest '%s' failed: %m", this->name
);
317 if (!this->cowfs
->set_overlay(this->cowfs
, dir
))
321 while (this->ifaces
->remove_last(this->ifaces
, (void**)&iface
) == SUCCESS
)
323 iface
->destroy(iface
);
329 * Implementation of gues_t.exec
331 static int exec(private_guest_t
*this, void(*cb
)(void*,char*), void *data
,
341 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.sigchild.
355 static void sigchild(private_guest_t
*this)
357 DESTROY_IF(this->mconsole
);
358 this->mconsole
= NULL
;
359 this->state
= GUEST_STOPPED
;
363 * umount the union filesystem
365 static bool umount_unionfs(private_guest_t
*this)
369 this->cowfs
->destroy(this->cowfs
);
377 * mount the union filesystem
379 static bool mount_unionfs(private_guest_t
*this)
381 char master
[PATH_MAX
];
383 char mount
[PATH_MAX
];
385 if (this->cowfs
== NULL
)
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
);
391 this->cowfs
= cowfs_create(master
, diff
, mount
);
401 * load memory configuration from file
403 int loadmem(private_guest_t
*this)
408 file
= fdopen(openat(this->dir
, MEMORY_FILE
, O_RDONLY
, PERM
), "r");
411 if (fscanf(file
, "%d", &mem
) <= 0)
421 * save memory configuration to file
423 bool savemem(private_guest_t
*this, int mem
)
428 file
= fdopen(openat(this->dir
, MEMORY_FILE
, O_RDWR
| O_CREAT
| O_TRUNC
,
432 if (fprintf(file
, "%d", mem
) > 0)
442 * Implementation of guest_t.destroy.
444 static void destroy(private_guest_t
*this)
447 umount_unionfs(this);
452 this->ifaces
->destroy(this->ifaces
);
459 * generic guest constructor
461 static private_guest_t
*guest_create_generic(char *parent
, char *name
,
465 private_guest_t
*this = malloc_thing(private_guest_t
);
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
;
480 if (*parent
== '/' || getcwd(cwd
, sizeof(cwd
)) == NULL
)
482 asprintf(&this->dirname
, "%s/%s", parent
, name
);
486 asprintf(&this->dirname
, "%s/%s/%s", cwd
, parent
, name
);
490 mkdir(this->dirname
, PERME
);
492 this->dir
= open(this->dirname
, O_DIRECTORY
, PERME
);
495 DBG1("opening guest directory '%s' failed: %m", this->dirname
);
501 this->state
= GUEST_STOPPED
;
502 this->mconsole
= NULL
;
503 this->ifaces
= linked_list_create();
505 this->name
= strdup(name
);
512 * create a symlink to old called new in our working dir
514 static bool make_symlink(private_guest_t
*this, char *old
, char *new)
519 if (*old
== '/' || getcwd(cwd
, sizeof(cwd
)) == NULL
)
521 snprintf(buf
, sizeof(buf
), "%s", old
);
525 snprintf(buf
, sizeof(buf
), "%s/%s", cwd
, old
);
527 return symlinkat(buf
, this->dir
, new) == 0;
532 * create the guest instance, including required dirs and mounts
534 guest_t
*guest_create(char *parent
, char *name
, char *kernel
,
535 char *master
, int mem
)
537 private_guest_t
*this = guest_create_generic(parent
, name
, TRUE
);
544 if (!make_symlink(this, master
, MASTER_DIR
) ||
545 !make_symlink(this, kernel
, KERNEL_FILE
))
547 DBG1("creating master/kernel symlink failed: %m");
552 if (mkdirat(this->dir
, UNION_DIR
, PERME
) != 0 ||
553 mkdirat(this->dir
, DIFF_DIR
, PERME
) != 0)
555 DBG1("unable to create directories for '%s': %m", name
);
561 if (!savemem(this, mem
))
567 if (!mount_unionfs(this))
573 return &this->public;
577 * load an already created guest
579 guest_t
*guest_load(char *parent
, char *name
)
581 private_guest_t
*this = guest_create_generic(parent
, name
, FALSE
);
588 this->mem
= loadmem(this);
591 DBG1("unable to open memory configuration file: %m", name
);
596 if (!mount_unionfs(this))
602 return &this->public;