2007-07-25 13:23:45 +00:00
|
|
|
/*
|
|
|
|
* Copyright (C) 2007 Martin Willi
|
|
|
|
* Hochschule fuer Technik Rapperswil
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
|
|
* under the terms of the GNU General Public License as published by the
|
|
|
|
* Free Software Foundation; either version 2 of the License, or (at your
|
|
|
|
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful, but
|
|
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
|
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
|
|
* for more details.
|
|
|
|
*/
|
|
|
|
|
2007-07-24 14:22:56 +00:00
|
|
|
#define _GNU_SOURCE
|
|
|
|
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <sys/uio.h>
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <signal.h>
|
|
|
|
|
|
|
|
#include <debug.h>
|
2007-07-25 13:23:45 +00:00
|
|
|
#include <utils/linked_list.h>
|
2007-07-24 14:22:56 +00:00
|
|
|
|
|
|
|
#include "dumm.h"
|
|
|
|
#include "guest.h"
|
2007-07-25 13:23:45 +00:00
|
|
|
#include "mconsole.h"
|
2007-07-24 14:22:56 +00:00
|
|
|
|
|
|
|
typedef struct private_guest_t private_guest_t;
|
|
|
|
|
|
|
|
struct private_guest_t {
|
2007-07-25 13:23:45 +00:00
|
|
|
/** implemented public interface */
|
2007-07-24 14:22:56 +00:00
|
|
|
guest_t public;
|
2007-07-25 13:23:45 +00:00
|
|
|
/** name of the guest */
|
2007-07-24 14:22:56 +00:00
|
|
|
char *name;
|
2007-07-25 13:23:45 +00:00
|
|
|
/** kernel to boot for guest */
|
2007-07-24 14:22:56 +00:00
|
|
|
char *kernel;
|
2007-07-25 13:23:45 +00:00
|
|
|
/** read only master filesystem guest uses */
|
2007-07-24 14:22:56 +00:00
|
|
|
char *master;
|
2007-07-25 13:23:45 +00:00
|
|
|
/** amount of memory for guest, in MB */
|
2007-07-24 14:22:56 +00:00
|
|
|
int mem;
|
2007-07-25 13:23:45 +00:00
|
|
|
/** pid of guest child process */
|
2007-07-24 14:22:56 +00:00
|
|
|
int pid;
|
2007-07-25 13:23:45 +00:00
|
|
|
/** log file for console 0 */
|
2007-07-24 14:22:56 +00:00
|
|
|
int bootlog;
|
2007-07-25 13:23:45 +00:00
|
|
|
/** mconsole to control running UML */
|
|
|
|
mconsole_t *mconsole;
|
|
|
|
/** list of interfaces attached to the guest */
|
|
|
|
linked_list_t *ifaces;
|
2007-07-24 14:22:56 +00:00
|
|
|
};
|
|
|
|
|
2007-07-25 13:23:45 +00:00
|
|
|
/**
|
|
|
|
* Implementation of guest_t.get_name.
|
|
|
|
*/
|
2007-07-24 14:22:56 +00:00
|
|
|
static char* get_name(private_guest_t *this)
|
|
|
|
{
|
|
|
|
return this->name;
|
|
|
|
}
|
|
|
|
|
2007-07-25 13:23:45 +00:00
|
|
|
/**
|
|
|
|
* Implementation of guest_t.create_iface.
|
|
|
|
*/
|
|
|
|
static iface_t* create_iface(private_guest_t *this, char *name)
|
|
|
|
{
|
|
|
|
iterator_t *iterator;
|
|
|
|
iface_t *iface;
|
|
|
|
|
|
|
|
if (this->pid == 0)
|
|
|
|
{
|
|
|
|
DBG1("guest '%s' not running, unable to add interface", this->name);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
iterator = this->ifaces->create_iterator(this->ifaces, TRUE);
|
|
|
|
while (iterator->iterate(iterator, (void**)&iface))
|
|
|
|
{
|
|
|
|
if (streq(name, iface->get_guest(iface)))
|
|
|
|
{
|
|
|
|
DBG1("guest '%s' already has an interface '%s'", this->name, name);
|
|
|
|
iterator->destroy(iterator);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
iterator->destroy(iterator);
|
|
|
|
|
|
|
|
iface = iface_create(name, this->mconsole);
|
|
|
|
if (iface)
|
|
|
|
{
|
|
|
|
this->ifaces->insert_last(this->ifaces, iface);
|
|
|
|
}
|
|
|
|
return iface;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Implementation of guest_t.create_iface_iterator.
|
|
|
|
*/
|
|
|
|
static iterator_t* create_iface_iterator(private_guest_t *this)
|
|
|
|
{
|
|
|
|
return this->ifaces->create_iterator(this->ifaces, TRUE);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* write format string to a buffer, and advance buffer position
|
|
|
|
*/
|
2007-07-24 14:22:56 +00:00
|
|
|
static char* write_arg(char **pos, size_t *left, char *format, ...)
|
|
|
|
{
|
|
|
|
size_t len;
|
|
|
|
char *res = NULL;
|
|
|
|
va_list args;
|
|
|
|
|
|
|
|
va_start(args, format);
|
|
|
|
len = vsnprintf(*pos, *left, format, args);
|
|
|
|
va_end(args);
|
|
|
|
if (len < *left)
|
|
|
|
{
|
|
|
|
res = *pos;
|
|
|
|
len++;
|
|
|
|
*pos += len + 1;
|
|
|
|
*left -= len + 1;
|
|
|
|
}
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2007-07-25 13:23:45 +00:00
|
|
|
/**
|
|
|
|
* Implementation of guest_t.start.
|
|
|
|
*/
|
2007-07-24 14:22:56 +00:00
|
|
|
static bool start(private_guest_t *this)
|
|
|
|
{
|
|
|
|
char buf[1024];
|
|
|
|
char cwd[512];
|
|
|
|
char *pos = buf;
|
|
|
|
char *args[16];
|
|
|
|
int i = 0;
|
|
|
|
size_t left = sizeof(buf);
|
|
|
|
|
|
|
|
args[i++] = this->kernel;
|
|
|
|
args[i++] = write_arg(&pos, &left, "root=/dev/root");
|
|
|
|
args[i++] = write_arg(&pos, &left, "rootfstype=hostfs");
|
|
|
|
args[i++] = write_arg(&pos, &left, "rootflags=%s/%s/%s",
|
|
|
|
getcwd(cwd, sizeof(cwd)), MOUNT_DIR, this->name);
|
|
|
|
args[i++] = write_arg(&pos, &left, "uml_dir=%s/%s", RUN_DIR, this->name);
|
|
|
|
args[i++] = write_arg(&pos, &left, "umid=%s", this->name);
|
|
|
|
args[i++] = write_arg(&pos, &left, "mem=%dM", this->mem);
|
2007-07-25 13:23:45 +00:00
|
|
|
/*args[i++] = write_arg(&pos, &left, "con=pts");*/
|
2007-07-24 14:22:56 +00:00
|
|
|
args[i++] = write_arg(&pos, &left, "con0=null,fd:%d", this->bootlog);
|
|
|
|
args[i++] = write_arg(&pos, &left, "con1=fd:0,fd:1");
|
|
|
|
args[i++] = write_arg(&pos, &left, "con3=null,null");
|
|
|
|
args[i++] = write_arg(&pos, &left, "con4=null,null");
|
|
|
|
args[i++] = write_arg(&pos, &left, "con5=null,null");
|
|
|
|
args[i++] = write_arg(&pos, &left, "con6=null,null");
|
|
|
|
args[i++] = NULL;
|
|
|
|
|
|
|
|
this->pid = fork();
|
|
|
|
switch (this->pid)
|
|
|
|
{
|
|
|
|
case 0: /* child, */
|
|
|
|
dup2(open("/dev/null", 0), 0);
|
|
|
|
dup2(open("/dev/null", 0), 1);
|
|
|
|
dup2(open("/dev/null", 0), 2);
|
|
|
|
execvp(args[0], args);
|
2007-07-25 13:23:45 +00:00
|
|
|
DBG1("starting UML kernel '%s' failed", args[0]);
|
2007-07-24 14:22:56 +00:00
|
|
|
exit(1);
|
|
|
|
case -1:
|
|
|
|
this->pid = 0;
|
|
|
|
return FALSE;
|
|
|
|
default:
|
2007-07-25 13:23:45 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
/* open mconsole */
|
|
|
|
snprintf(buf, sizeof(buf), "%s/%s/%s/mconsole", RUN_DIR, this->name, this->name);
|
|
|
|
this->mconsole = mconsole_create(buf);
|
|
|
|
if (this->mconsole == NULL)
|
|
|
|
{
|
|
|
|
DBG1("opening mconsole at '%s' failed, stopping guest", buf);
|
|
|
|
kill(this->pid, SIGINT);
|
|
|
|
this->pid = 0;
|
|
|
|
return FALSE;
|
2007-07-24 14:22:56 +00:00
|
|
|
}
|
2007-07-25 13:23:45 +00:00
|
|
|
return TRUE;
|
2007-07-24 14:22:56 +00:00
|
|
|
}
|
|
|
|
|
2007-07-25 13:23:45 +00:00
|
|
|
/**
|
|
|
|
* Implementation of guest_t.stop.
|
|
|
|
*/
|
2007-07-24 14:22:56 +00:00
|
|
|
static void stop(private_guest_t *this)
|
|
|
|
{
|
|
|
|
if (this->pid)
|
|
|
|
{
|
|
|
|
kill(this->pid, SIGINT);
|
|
|
|
this->pid = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2007-07-25 13:23:45 +00:00
|
|
|
* Check if directory exists, create otherwise
|
2007-07-24 14:22:56 +00:00
|
|
|
*/
|
|
|
|
static bool makedir(char *dir, char *name)
|
|
|
|
{
|
|
|
|
struct stat st;
|
|
|
|
char buf[256];
|
|
|
|
size_t len;
|
|
|
|
|
|
|
|
len = snprintf(buf, sizeof(buf), "%s/%s", dir, name);
|
|
|
|
if (len < 0 || len >= sizeof(buf))
|
|
|
|
{
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
if (stat(buf, &st) != 0)
|
|
|
|
{
|
|
|
|
return mkdir(buf, S_IRWXU) == 0;
|
|
|
|
}
|
|
|
|
return S_ISDIR(st.st_mode);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* umount the union filesystem
|
|
|
|
*/
|
|
|
|
static bool umount_unionfs(char *name)
|
|
|
|
{
|
|
|
|
char cmd[128];
|
|
|
|
size_t len;
|
|
|
|
|
|
|
|
len = snprintf(cmd, sizeof(cmd), "fusermount -u %s/%s", MOUNT_DIR, name);
|
|
|
|
if (len < 0 || len >= sizeof(cmd))
|
|
|
|
{
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
if (system(cmd) != 0)
|
|
|
|
{
|
2007-07-25 13:23:45 +00:00
|
|
|
DBG1("unmounting guest unionfs for %s failed", name);
|
2007-07-24 14:22:56 +00:00
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* mount the union filesystem
|
|
|
|
*/
|
|
|
|
static bool mount_unionfs(char *name, char *master)
|
|
|
|
{
|
|
|
|
char cmd[256];
|
|
|
|
size_t len;
|
|
|
|
|
|
|
|
len = snprintf(cmd, sizeof(cmd), "unionfs %s/%s:%s %s/%s",
|
|
|
|
HOST_DIR, name, master, MOUNT_DIR, name);
|
|
|
|
if (len < 0 || len >= sizeof(cmd))
|
|
|
|
{
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
if (system(cmd) != 0)
|
|
|
|
{
|
2007-07-25 13:23:45 +00:00
|
|
|
DBG1("mounting guest unionfs for %s using '%s' failed", name, cmd);
|
2007-07-24 14:22:56 +00:00
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* open logfile for boot messages
|
|
|
|
*/
|
|
|
|
static int open_bootlog(char *name)
|
|
|
|
{
|
|
|
|
char blg[256];
|
|
|
|
size_t len;
|
|
|
|
int fd;
|
|
|
|
|
|
|
|
len = snprintf(blg, sizeof(blg), "%s/%s/boot.log", RUN_DIR, name);
|
|
|
|
if (len < 0 || len >= sizeof(blg))
|
|
|
|
{
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
fd = open(blg, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR);
|
|
|
|
if (fd == -1)
|
|
|
|
{
|
2007-07-25 13:23:45 +00:00
|
|
|
DBG1("opening bootlog '%s' for %s failed, using stdout", blg, name);
|
2007-07-24 14:22:56 +00:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
return fd;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2007-07-25 13:23:45 +00:00
|
|
|
* Implementation of guest_t.destroy.
|
2007-07-24 14:22:56 +00:00
|
|
|
*/
|
|
|
|
static void destroy(private_guest_t *this)
|
|
|
|
{
|
|
|
|
stop(this);
|
|
|
|
umount_unionfs(this->name);
|
2007-07-25 13:23:45 +00:00
|
|
|
this->ifaces->destroy_offset(this->ifaces, offsetof(iface_t, destroy));
|
|
|
|
DESTROY_IF(this->mconsole);
|
2007-07-24 14:22:56 +00:00
|
|
|
free(this->name);
|
|
|
|
free(this->kernel);
|
|
|
|
free(this->master);
|
|
|
|
free(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* create the guest instance, including required dirs and mounts
|
|
|
|
*/
|
|
|
|
guest_t *guest_create(char *name, char *kernel, char *master, int mem)
|
|
|
|
{
|
|
|
|
private_guest_t *this = malloc_thing(private_guest_t);
|
|
|
|
|
|
|
|
this->public.get_name = (void*)get_name;
|
2007-07-25 13:23:45 +00:00
|
|
|
this->public.create_iface = (iface_t*(*)(guest_t*,char*))create_iface;
|
|
|
|
this->public.create_iface_iterator = (iterator_t*(*)(guest_t*))create_iface_iterator;
|
2007-07-24 14:22:56 +00:00
|
|
|
this->public.start = (void*)start;
|
|
|
|
this->public.stop = (void*)stop;
|
|
|
|
this->public.destroy = (void*)destroy;
|
|
|
|
|
|
|
|
if (!makedir(HOST_DIR, name) || !makedir(MOUNT_DIR, name) ||
|
2007-07-25 13:23:45 +00:00
|
|
|
!makedir(RUN_DIR, name) || !mount_unionfs(name, master))
|
2007-07-24 14:22:56 +00:00
|
|
|
{
|
|
|
|
free(this);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
this->name = strdup(name);
|
|
|
|
this->kernel = strdup(kernel);
|
|
|
|
this->master = strdup(master);
|
|
|
|
this->mem = mem;
|
|
|
|
this->pid = 0;
|
|
|
|
this->bootlog = open_bootlog(name);
|
2007-07-25 13:23:45 +00:00
|
|
|
this->mconsole = NULL;
|
|
|
|
this->ifaces = linked_list_create();
|
2007-07-24 14:22:56 +00:00
|
|
|
|
|
|
|
return &this->public;
|
|
|
|
}
|
|
|
|
|