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>
29 #include <utils/linked_list.h>
36 #define PERME (S_IRWXU | S_IRWXG)
37 #define PERM (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP)
39 typedef struct private_guest_t private_guest_t
;
41 struct private_guest_t
{
42 /** implemented public interface */
44 /** name of the guest */
46 /** directory of guest */
48 /** directory name of guest */
50 /** amount of memory for guest, in MB */
52 /** pid of guest child process */
56 /** log file for console 0 */
58 /** FUSE cowfs instance */
60 /** mconsole to control running UML */
62 /** list of interfaces attached to the guest */
63 linked_list_t
*ifaces
;
66 ENUM(guest_state_names
, GUEST_STOPPED
, GUEST_STOPPING
,
75 * Implementation of guest_t.get_name.
77 static char* get_name(private_guest_t
*this)
83 * Implementation of guest_t.create_iface.
85 static iface_t
* create_iface(private_guest_t
*this, char *name
)
90 if (this->state
!= GUEST_RUNNING
)
92 DBG1("guest '%s' not running, unable to add interface", this->name
);
96 iterator
= this->ifaces
->create_iterator(this->ifaces
, TRUE
);
97 while (iterator
->iterate(iterator
, (void**)&iface
))
99 if (streq(name
, iface
->get_guestif(iface
)))
101 DBG1("guest '%s' already has an interface '%s'", this->name
, name
);
102 iterator
->destroy(iterator
);
106 iterator
->destroy(iterator
);
108 iface
= iface_create(this->name
, name
, this->mconsole
);
111 this->ifaces
->insert_last(this->ifaces
, iface
);
117 * Implementation of guest_t.create_iface_iterator.
119 static iterator_t
* create_iface_iterator(private_guest_t
*this)
121 return this->ifaces
->create_iterator(this->ifaces
, TRUE
);
125 * Implementation of guest_t.get_state.
127 static guest_state_t
get_state(private_guest_t
*this)
133 * Implementation of guest_t.get_pid.
135 static pid_t
get_pid(private_guest_t
*this)
141 * write format string to a buffer, and advance buffer position
143 static char* write_arg(char **pos
, size_t *left
, char *format
, ...)
149 va_start(args
, format
);
150 len
= vsnprintf(*pos
, *left
, format
, args
);
163 * Implementation of guest_t.stop.
165 static void stop(private_guest_t
*this)
167 if (this->state
!= GUEST_STOPPED
)
169 this->state
= GUEST_STOPPING
;
170 this->ifaces
->destroy_offset(this->ifaces
, offsetof(iface_t
, destroy
));
171 this->ifaces
= linked_list_create();
172 kill(this->pid
, SIGINT
);
173 waitpid(this->pid
, NULL
, 0);
174 this->state
= GUEST_STOPPED
;
179 * Implementation of guest_t.start.
181 static bool start(private_guest_t
*this, char *kernel
)
188 size_t left
= sizeof(buf
);
190 if (this->state
!= GUEST_STOPPED
)
192 DBG1("unable to start guest in state %N", guest_state_names
, this->state
);
195 this->state
= GUEST_STARTING
;
197 notify
= write_arg(&pos
, &left
, "%s/%s", this->dirname
, NOTIFY_FILE
);
199 args
[i
++] = write_arg(&pos
, &left
, "%s/%s", this->dirname
, KERNEL_FILE
);
200 args
[i
++] = write_arg(&pos
, &left
, "root=/dev/root");
201 args
[i
++] = write_arg(&pos
, &left
, "rootfstype=hostfs");
202 args
[i
++] = write_arg(&pos
, &left
, "rootflags=%s/%s", this->dirname
, UNION_DIR
);
203 args
[i
++] = write_arg(&pos
, &left
, "uml_dir=%s", this->dirname
);
204 args
[i
++] = write_arg(&pos
, &left
, "umid=%s", this->name
);
205 args
[i
++] = write_arg(&pos
, &left
, "mem=%dM", this->mem
);
206 args
[i
++] = write_arg(&pos
, &left
, "mconsole=notify:%s", notify
);
207 /*args[i++] = write_arg(&pos, &left, "con=pts");*/
208 args
[i
++] = write_arg(&pos
, &left
, "con0=null,fd:%d", this->bootlog
);
209 //args[i++] = write_arg(&pos, &left, "con0=fd:0,fd:1");
210 //args[i++] = write_arg(&pos, &left, "con1=null,null");
211 args
[i
++] = write_arg(&pos
, &left
, "con2=null,null");
212 args
[i
++] = write_arg(&pos
, &left
, "con3=null,null");
213 args
[i
++] = write_arg(&pos
, &left
, "con4=null,null");
214 args
[i
++] = write_arg(&pos
, &left
, "con5=null,null");
215 args
[i
++] = write_arg(&pos
, &left
, "con6=null,null");
222 dup2(open("/dev/null", 0), 0);
223 dup2(this->bootlog
, 1);
224 dup2(this->bootlog
, 2);
225 execvp(args
[0], args
);
226 DBG1("starting UML kernel '%s' failed: %m", args
[0]);
229 this->state
= GUEST_STOPPED
;
235 this->mconsole
= mconsole_create(notify
);
236 if (this->mconsole
== NULL
)
238 DBG1("opening mconsole at '%s' failed, stopping guest", buf
);
242 this->state
= GUEST_RUNNING
;
247 * Implementation of guest_t.sigchild.
249 static void sigchild(private_guest_t
*this)
251 if (this->state
!= GUEST_STOPPING
)
252 { /* collect zombie if uml crashed */
253 waitpid(this->pid
, NULL
, WNOHANG
);
255 DESTROY_IF(this->mconsole
);
256 this->mconsole
= NULL
;
257 this->state
= GUEST_STOPPED
;
261 * umount the union filesystem
263 static bool umount_unionfs(private_guest_t
*this)
267 this->cowfs
->destroy(this->cowfs
);
275 * mount the union filesystem
277 static bool mount_unionfs(private_guest_t
*this)
279 char master
[PATH_MAX
];
281 char mount
[PATH_MAX
];
283 if (this->cowfs
== NULL
)
285 snprintf(master
, sizeof(master
), "%s/%s", this->dirname
, MASTER_DIR
);
286 snprintf(diff
, sizeof(diff
), "%s/%s", this->dirname
, DIFF_DIR
);
287 snprintf(mount
, sizeof(mount
), "%s/%s", this->dirname
, UNION_DIR
);
289 this->cowfs
= cowfs_create(master
, diff
, mount
);
299 * open logfile for boot messages
301 static int open_bootlog(private_guest_t
*this)
305 fd
= openat(this->dir
, LOG_FILE
, O_WRONLY
| O_CREAT
, PERM
);
308 DBG1("opening bootlog failed, using stdout");
315 * load memory configuration from file
317 int loadmem(private_guest_t
*this)
322 file
= fdopen(openat(this->dir
, MEMORY_FILE
, O_RDONLY
, PERM
), "r");
325 if (fscanf(file
, "%d", &mem
) <= 0)
335 * save memory configuration to file
337 bool savemem(private_guest_t
*this, int mem
)
342 file
= fdopen(openat(this->dir
, MEMORY_FILE
, O_RDWR
| O_CREAT
| O_TRUNC
,
346 if (fprintf(file
, "%d", mem
) > 0)
356 * Implementation of guest_t.destroy.
358 static void destroy(private_guest_t
*this)
361 umount_unionfs(this);
362 if (this->bootlog
> 1)
364 close(this->bootlog
);
376 * generic guest constructor
378 static private_guest_t
*guest_create_generic(char *parent
, char *name
,
382 private_guest_t
*this = malloc_thing(private_guest_t
);
384 this->public.get_name
= (void*)get_name
;
385 this->public.get_pid
= (pid_t(*)(guest_t
*))get_pid
;
386 this->public.get_state
= (guest_state_t(*)(guest_t
*))get_state
;
387 this->public.create_iface
= (iface_t
*(*)(guest_t
*,char*))create_iface
;
388 this->public.create_iface_iterator
= (iterator_t
*(*)(guest_t
*))create_iface_iterator
;
389 this->public.start
= (void*)start
;
390 this->public.stop
= (void*)stop
;
391 this->public.sigchild
= (void(*)(guest_t
*))sigchild
;
392 this->public.destroy
= (void*)destroy
;
394 if (*parent
== '/' || getcwd(cwd
, sizeof(cwd
)) == NULL
)
396 asprintf(&this->dirname
, "%s/%s", parent
, name
);
400 asprintf(&this->dirname
, "%s/%s/%s", cwd
, parent
, name
);
404 mkdir(this->dirname
, PERME
);
406 this->dir
= open(this->dirname
, O_DIRECTORY
, PERME
);
409 DBG1("opening guest directory '%s' failed: %m", this->dirname
);
416 this->state
= GUEST_STOPPED
;
417 this->mconsole
= NULL
;
418 this->ifaces
= linked_list_create();
420 this->bootlog
= open_bootlog(this);
421 this->name
= strdup(name
);
428 * create a symlink to old called new in our working dir
430 static bool make_symlink(private_guest_t
*this, char *old
, char *new)
435 if (*old
== '/' || getcwd(cwd
, sizeof(cwd
)) == NULL
)
437 snprintf(buf
, sizeof(buf
), "%s", old
);
441 snprintf(buf
, sizeof(buf
), "%s/%s", cwd
, old
);
443 return symlinkat(buf
, this->dir
, new) == 0;
448 * create the guest instance, including required dirs and mounts
450 guest_t
*guest_create(char *parent
, char *name
, char *kernel
,
451 char *master
, int mem
)
453 private_guest_t
*this = guest_create_generic(parent
, name
, TRUE
);
460 if (!make_symlink(this, master
, MASTER_DIR
) ||
461 !make_symlink(this, kernel
, KERNEL_FILE
))
463 DBG1("creating master/kernel symlink failed: %m");
468 if (mkdirat(this->dir
, UNION_DIR
, PERME
) != 0 ||
469 mkdirat(this->dir
, DIFF_DIR
, PERME
) != 0)
471 DBG1("unable to create directories for '%s': %m", name
);
477 if (!savemem(this, mem
))
483 if (!mount_unionfs(this))
489 return &this->public;
493 * load an already created guest
495 guest_t
*guest_load(char *parent
, char *name
)
497 private_guest_t
*this = guest_create_generic(parent
, name
, FALSE
);
504 this->mem
= loadmem(this);
507 DBG1("unable to open memory configuration file: %m", name
);
512 if (!mount_unionfs(this))
518 return &this->public;