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>
|
2007-07-30 13:20:35 +00:00
|
|
|
#include <sys/wait.h>
|
|
|
|
#include <sys/uio.h>
|
2007-07-24 14:22:56 +00:00
|
|
|
#include <unistd.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <signal.h>
|
2007-07-30 13:20:35 +00:00
|
|
|
#include <dirent.h>
|
2007-08-28 07:53:46 +00:00
|
|
|
#include <termios.h>
|
2008-07-07 14:56:04 +00:00
|
|
|
#include <stdarg.h>
|
2007-07-24 14:22:56 +00:00
|
|
|
|
|
|
|
#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-31 15:23:23 +00:00
|
|
|
#include "cowfs.h"
|
2007-07-24 14:22:56 +00:00
|
|
|
|
2007-07-30 13:20:35 +00:00
|
|
|
#define PERME (S_IRWXU | S_IRWXG)
|
|
|
|
#define PERM (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP)
|
|
|
|
|
2007-08-08 12:35:24 +00:00
|
|
|
#define MASTER_DIR "master"
|
|
|
|
#define DIFF_DIR "diff"
|
|
|
|
#define UNION_DIR "union"
|
|
|
|
#define MEMORY_FILE "mem"
|
2008-05-21 16:02:16 +00:00
|
|
|
#define PID_FILE "pid"
|
2007-08-08 12:35:24 +00:00
|
|
|
#define KERNEL_FILE "linux"
|
|
|
|
#define LOG_FILE "boot.log"
|
|
|
|
#define NOTIFY_FILE "notify"
|
2007-08-28 07:53:46 +00:00
|
|
|
#define PTYS 0
|
2007-08-08 12:35:24 +00:00
|
|
|
|
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-30 13:20:35 +00:00
|
|
|
/** directory of guest */
|
|
|
|
int dir;
|
|
|
|
/** directory name of guest */
|
|
|
|
char *dirname;
|
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-26 13:21:06 +00:00
|
|
|
/** state of guest */
|
|
|
|
guest_state_t state;
|
2007-07-31 15:23:23 +00:00
|
|
|
/** FUSE cowfs instance */
|
|
|
|
cowfs_t *cowfs;
|
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-26 13:21:06 +00:00
|
|
|
ENUM(guest_state_names, GUEST_STOPPED, GUEST_STOPPING,
|
|
|
|
"STOPPED",
|
|
|
|
"STARTING",
|
|
|
|
"RUNNING",
|
|
|
|
"PAUSED",
|
|
|
|
"STOPPING",
|
|
|
|
);
|
|
|
|
|
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)
|
|
|
|
{
|
2008-04-28 16:43:30 +00:00
|
|
|
enumerator_t *enumerator;
|
2007-07-25 13:23:45 +00:00
|
|
|
iface_t *iface;
|
|
|
|
|
2007-07-27 07:37:15 +00:00
|
|
|
if (this->state != GUEST_RUNNING)
|
2007-07-25 13:23:45 +00:00
|
|
|
{
|
|
|
|
DBG1("guest '%s' not running, unable to add interface", this->name);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2008-04-28 16:43:30 +00:00
|
|
|
enumerator = this->ifaces->create_enumerator(this->ifaces);
|
|
|
|
while (enumerator->enumerate(enumerator, (void**)&iface))
|
2007-07-25 13:23:45 +00:00
|
|
|
{
|
2007-07-26 07:39:49 +00:00
|
|
|
if (streq(name, iface->get_guestif(iface)))
|
2007-07-25 13:23:45 +00:00
|
|
|
{
|
|
|
|
DBG1("guest '%s' already has an interface '%s'", this->name, name);
|
2008-04-28 16:43:30 +00:00
|
|
|
enumerator->destroy(enumerator);
|
2007-07-25 13:23:45 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
2008-04-28 16:43:30 +00:00
|
|
|
enumerator->destroy(enumerator);
|
2007-07-25 13:23:45 +00:00
|
|
|
|
2008-07-04 14:21:41 +00:00
|
|
|
iface = iface_create(name, &this->public, this->mconsole);
|
2007-07-25 13:23:45 +00:00
|
|
|
if (iface)
|
|
|
|
{
|
|
|
|
this->ifaces->insert_last(this->ifaces, iface);
|
|
|
|
}
|
|
|
|
return iface;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2008-04-28 16:43:30 +00:00
|
|
|
* Implementation of guest_t.destroy_iface.
|
2007-07-25 13:23:45 +00:00
|
|
|
*/
|
2008-04-28 16:43:30 +00:00
|
|
|
static void destroy_iface(private_guest_t *this, iface_t *iface)
|
2007-07-25 13:23:45 +00:00
|
|
|
{
|
2008-04-28 16:43:30 +00:00
|
|
|
enumerator_t *enumerator;
|
|
|
|
iface_t *current;
|
|
|
|
|
|
|
|
enumerator = this->ifaces->create_enumerator(this->ifaces);
|
|
|
|
while (enumerator->enumerate(enumerator, (void**)¤t))
|
|
|
|
{
|
|
|
|
if (current == iface)
|
|
|
|
{
|
|
|
|
this->ifaces->remove_at(this->ifaces, enumerator);
|
|
|
|
current->destroy(current);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
enumerator->destroy(enumerator);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Implementation of guest_t.create_iface_enumerator.
|
|
|
|
*/
|
|
|
|
static enumerator_t* create_iface_enumerator(private_guest_t *this)
|
|
|
|
{
|
|
|
|
return this->ifaces->create_enumerator(this->ifaces);
|
2007-07-25 13:23:45 +00:00
|
|
|
}
|
2007-07-26 13:21:06 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Implementation of guest_t.get_state.
|
|
|
|
*/
|
|
|
|
static guest_state_t get_state(private_guest_t *this)
|
|
|
|
{
|
|
|
|
return this->state;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Implementation of guest_t.get_pid.
|
|
|
|
*/
|
|
|
|
static pid_t get_pid(private_guest_t *this)
|
|
|
|
{
|
|
|
|
return this->pid;
|
|
|
|
}
|
2007-07-25 13:23:45 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* 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-30 13:20:35 +00:00
|
|
|
/**
|
|
|
|
* Implementation of guest_t.stop.
|
|
|
|
*/
|
2008-04-28 16:43:30 +00:00
|
|
|
static void stop(private_guest_t *this, idle_function_t idle)
|
2007-07-30 13:20:35 +00:00
|
|
|
{
|
|
|
|
if (this->state != GUEST_STOPPED)
|
|
|
|
{
|
|
|
|
this->state = GUEST_STOPPING;
|
|
|
|
this->ifaces->destroy_offset(this->ifaces, offsetof(iface_t, destroy));
|
|
|
|
this->ifaces = linked_list_create();
|
|
|
|
kill(this->pid, SIGINT);
|
2008-04-28 16:43:30 +00:00
|
|
|
while (this->state != GUEST_STOPPED)
|
|
|
|
{
|
|
|
|
if (idle)
|
|
|
|
{
|
|
|
|
idle();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
usleep(50000);
|
|
|
|
}
|
|
|
|
}
|
2008-05-21 16:02:16 +00:00
|
|
|
unlinkat(this->dir, PID_FILE, 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* save pid in file
|
|
|
|
*/
|
|
|
|
void savepid(private_guest_t *this)
|
|
|
|
{
|
|
|
|
FILE *file;
|
|
|
|
|
|
|
|
file = fdopen(openat(this->dir, PID_FILE, O_RDWR | O_CREAT | O_TRUNC,
|
|
|
|
PERM), "w");
|
|
|
|
if (file)
|
|
|
|
{
|
|
|
|
fprintf(file, "%d", this->pid);
|
|
|
|
fclose(file);
|
2007-07-30 13:20:35 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-07-25 13:23:45 +00:00
|
|
|
/**
|
|
|
|
* Implementation of guest_t.start.
|
|
|
|
*/
|
2008-04-28 16:43:30 +00:00
|
|
|
static bool start(private_guest_t *this, invoke_function_t invoke, void* data,
|
|
|
|
idle_function_t idle)
|
2007-07-24 14:22:56 +00:00
|
|
|
{
|
2007-07-30 13:20:35 +00:00
|
|
|
char buf[2048];
|
2007-07-26 13:21:06 +00:00
|
|
|
char *notify;
|
2007-07-24 14:22:56 +00:00
|
|
|
char *pos = buf;
|
2008-04-28 16:43:30 +00:00
|
|
|
char *args[32];
|
2007-07-24 14:22:56 +00:00
|
|
|
int i = 0;
|
|
|
|
size_t left = sizeof(buf);
|
2007-07-26 13:21:06 +00:00
|
|
|
|
2008-04-28 16:43:30 +00:00
|
|
|
memset(args, 0, sizeof(args));
|
|
|
|
|
2007-07-26 13:21:06 +00:00
|
|
|
if (this->state != GUEST_STOPPED)
|
|
|
|
{
|
|
|
|
DBG1("unable to start guest in state %N", guest_state_names, this->state);
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
this->state = GUEST_STARTING;
|
|
|
|
|
2007-07-30 13:20:35 +00:00
|
|
|
notify = write_arg(&pos, &left, "%s/%s", this->dirname, NOTIFY_FILE);
|
2007-07-26 13:21:06 +00:00
|
|
|
|
2007-07-30 13:20:35 +00:00
|
|
|
args[i++] = write_arg(&pos, &left, "%s/%s", this->dirname, KERNEL_FILE);
|
2007-07-24 14:22:56 +00:00
|
|
|
args[i++] = write_arg(&pos, &left, "root=/dev/root");
|
|
|
|
args[i++] = write_arg(&pos, &left, "rootfstype=hostfs");
|
2007-07-30 13:20:35 +00:00
|
|
|
args[i++] = write_arg(&pos, &left, "rootflags=%s/%s", this->dirname, UNION_DIR);
|
|
|
|
args[i++] = write_arg(&pos, &left, "uml_dir=%s", this->dirname);
|
2007-07-24 14:22:56 +00:00
|
|
|
args[i++] = write_arg(&pos, &left, "umid=%s", this->name);
|
|
|
|
args[i++] = write_arg(&pos, &left, "mem=%dM", this->mem);
|
2007-07-26 13:21:06 +00:00
|
|
|
args[i++] = write_arg(&pos, &left, "mconsole=notify:%s", notify);
|
2008-04-28 16:43:30 +00:00
|
|
|
args[i++] = write_arg(&pos, &left, "con=null");
|
2007-07-24 14:22:56 +00:00
|
|
|
|
2008-04-28 16:43:30 +00:00
|
|
|
this->pid = invoke(data, &this->public, args, i);
|
|
|
|
if (!this->pid)
|
2007-07-24 14:22:56 +00:00
|
|
|
{
|
2008-04-28 16:43:30 +00:00
|
|
|
this->state = GUEST_STOPPED;
|
|
|
|
return FALSE;
|
2007-07-25 13:23:45 +00:00
|
|
|
}
|
2008-05-21 16:02:16 +00:00
|
|
|
savepid(this);
|
|
|
|
|
2007-07-25 13:23:45 +00:00
|
|
|
/* open mconsole */
|
2008-04-28 16:43:30 +00:00
|
|
|
this->mconsole = mconsole_create(notify, idle);
|
2007-07-25 13:23:45 +00:00
|
|
|
if (this->mconsole == NULL)
|
|
|
|
{
|
|
|
|
DBG1("opening mconsole at '%s' failed, stopping guest", buf);
|
2008-04-28 16:43:30 +00:00
|
|
|
stop(this, NULL);
|
2007-07-25 13:23:45 +00:00
|
|
|
return FALSE;
|
2007-07-24 14:22:56 +00:00
|
|
|
}
|
2007-08-28 07:53:46 +00:00
|
|
|
|
2007-07-26 13:21:06 +00:00
|
|
|
this->state = GUEST_RUNNING;
|
2007-07-25 13:23:45 +00:00
|
|
|
return TRUE;
|
2007-08-08 12:35:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2007-09-13 08:10:36 +00:00
|
|
|
* Implementation of guest_t.load_template.
|
2007-08-08 12:35:24 +00:00
|
|
|
*/
|
2007-09-13 08:10:36 +00:00
|
|
|
static bool load_template(private_guest_t *this, char *path)
|
2007-08-08 12:35:24 +00:00
|
|
|
{
|
|
|
|
char dir[PATH_MAX];
|
|
|
|
size_t len;
|
2008-04-28 16:43:30 +00:00
|
|
|
iface_t *iface;
|
2007-08-08 12:35:24 +00:00
|
|
|
|
2007-08-09 07:14:56 +00:00
|
|
|
if (path == NULL)
|
|
|
|
{
|
2007-09-13 08:10:36 +00:00
|
|
|
return this->cowfs->set_overlay(this->cowfs, NULL);
|
2007-08-09 07:14:56 +00:00
|
|
|
}
|
|
|
|
|
2007-08-08 12:35:24 +00:00
|
|
|
len = snprintf(dir, sizeof(dir), "%s/%s", path, this->name);
|
|
|
|
if (len < 0 || len >= sizeof(dir))
|
|
|
|
{
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
if (access(dir, F_OK) != 0)
|
|
|
|
{
|
|
|
|
if (mkdir(dir, PERME) != 0)
|
|
|
|
{
|
2007-09-13 08:10:36 +00:00
|
|
|
DBG1("creating overlay for guest '%s' failed: %m", this->name);
|
2007-08-08 12:35:24 +00:00
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
}
|
2008-04-28 16:43:30 +00:00
|
|
|
if (!this->cowfs->set_overlay(this->cowfs, dir))
|
|
|
|
{
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
while (this->ifaces->remove_last(this->ifaces, (void**)&iface) == SUCCESS)
|
|
|
|
{
|
|
|
|
iface->destroy(iface);
|
|
|
|
}
|
|
|
|
return TRUE;
|
2007-07-24 14:22:56 +00:00
|
|
|
}
|
|
|
|
|
2008-07-07 14:56:04 +00:00
|
|
|
/**
|
|
|
|
* Implementation of gues_t.exec
|
|
|
|
*/
|
2008-07-18 10:54:49 +00:00
|
|
|
static int exec(private_guest_t *this, void(*cb)(void*,char*,size_t), void *data,
|
2008-07-08 14:58:20 +00:00
|
|
|
char *cmd, ...)
|
2008-07-07 14:56:04 +00:00
|
|
|
{
|
2008-07-08 14:58:20 +00:00
|
|
|
char buf[1024];
|
2008-07-07 14:56:04 +00:00
|
|
|
size_t len;
|
|
|
|
va_list args;
|
|
|
|
|
|
|
|
if (this->mconsole)
|
|
|
|
{
|
|
|
|
va_start(args, cmd);
|
|
|
|
len = vsnprintf(buf, sizeof(buf), cmd, args);
|
|
|
|
va_end(args);
|
|
|
|
|
|
|
|
if (len > 0 && len < sizeof(buf))
|
|
|
|
{
|
2008-07-08 14:58:20 +00:00
|
|
|
return this->mconsole->exec(this->mconsole, cb, data, buf);
|
2008-07-07 14:56:04 +00:00
|
|
|
}
|
|
|
|
}
|
2008-07-08 14:58:20 +00:00
|
|
|
return -1;
|
2008-07-07 14:56:04 +00:00
|
|
|
}
|
|
|
|
|
2007-07-27 07:37:15 +00:00
|
|
|
/**
|
|
|
|
* Implementation of guest_t.sigchild.
|
|
|
|
*/
|
|
|
|
static void sigchild(private_guest_t *this)
|
|
|
|
{
|
2007-07-27 10:17:50 +00:00
|
|
|
DESTROY_IF(this->mconsole);
|
|
|
|
this->mconsole = NULL;
|
2007-07-27 07:37:15 +00:00
|
|
|
this->state = GUEST_STOPPED;
|
2007-07-24 14:22:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* umount the union filesystem
|
|
|
|
*/
|
2007-07-30 13:20:35 +00:00
|
|
|
static bool umount_unionfs(private_guest_t *this)
|
2007-07-24 14:22:56 +00:00
|
|
|
{
|
2007-07-31 15:23:23 +00:00
|
|
|
if (this->cowfs)
|
2007-07-24 14:22:56 +00:00
|
|
|
{
|
2007-07-31 15:23:23 +00:00
|
|
|
this->cowfs->destroy(this->cowfs);
|
|
|
|
this->cowfs = NULL;
|
|
|
|
return TRUE;
|
2007-07-24 14:22:56 +00:00
|
|
|
}
|
2007-07-31 15:23:23 +00:00
|
|
|
return FALSE;
|
2007-07-24 14:22:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* mount the union filesystem
|
|
|
|
*/
|
2007-07-30 13:20:35 +00:00
|
|
|
static bool mount_unionfs(private_guest_t *this)
|
2007-07-24 14:22:56 +00:00
|
|
|
{
|
2007-07-31 15:23:23 +00:00
|
|
|
char master[PATH_MAX];
|
|
|
|
char diff[PATH_MAX];
|
|
|
|
char mount[PATH_MAX];
|
|
|
|
|
|
|
|
if (this->cowfs == NULL)
|
2007-07-24 14:22:56 +00:00
|
|
|
{
|
2007-08-03 11:47:20 +00:00
|
|
|
snprintf(master, sizeof(master), "%s/%s", this->dirname, MASTER_DIR);
|
|
|
|
snprintf(diff, sizeof(diff), "%s/%s", this->dirname, DIFF_DIR);
|
|
|
|
snprintf(mount, sizeof(mount), "%s/%s", this->dirname, UNION_DIR);
|
|
|
|
|
2007-07-31 15:23:23 +00:00
|
|
|
this->cowfs = cowfs_create(master, diff, mount);
|
|
|
|
if (this->cowfs)
|
2007-07-30 13:20:35 +00:00
|
|
|
{
|
2007-07-31 15:23:23 +00:00
|
|
|
return TRUE;
|
2007-07-30 13:20:35 +00:00
|
|
|
}
|
2007-07-24 14:22:56 +00:00
|
|
|
}
|
2007-07-31 15:23:23 +00:00
|
|
|
return FALSE;
|
2007-07-24 14:22:56 +00:00
|
|
|
}
|
|
|
|
|
2007-07-30 13:20:35 +00:00
|
|
|
/**
|
|
|
|
* load memory configuration from file
|
|
|
|
*/
|
|
|
|
int loadmem(private_guest_t *this)
|
|
|
|
{
|
|
|
|
FILE *file;
|
|
|
|
int mem = 0;
|
|
|
|
|
|
|
|
file = fdopen(openat(this->dir, MEMORY_FILE, O_RDONLY, PERM), "r");
|
|
|
|
if (file)
|
|
|
|
{
|
|
|
|
if (fscanf(file, "%d", &mem) <= 0)
|
|
|
|
{
|
|
|
|
mem = 0;
|
|
|
|
}
|
|
|
|
fclose(file);
|
|
|
|
}
|
|
|
|
return mem;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* save memory configuration to file
|
|
|
|
*/
|
|
|
|
bool savemem(private_guest_t *this, int mem)
|
|
|
|
{
|
|
|
|
FILE *file;
|
|
|
|
bool retval = FALSE;
|
|
|
|
|
|
|
|
file = fdopen(openat(this->dir, MEMORY_FILE, O_RDWR | O_CREAT | O_TRUNC,
|
|
|
|
PERM), "w");
|
|
|
|
if (file)
|
|
|
|
{
|
|
|
|
if (fprintf(file, "%d", mem) > 0)
|
|
|
|
{
|
|
|
|
retval = TRUE;
|
|
|
|
}
|
|
|
|
fclose(file);
|
|
|
|
}
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
2007-07-24 14:22:56 +00:00
|
|
|
/**
|
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)
|
|
|
|
{
|
2008-04-28 16:43:30 +00:00
|
|
|
stop(this, NULL);
|
2007-07-30 13:20:35 +00:00
|
|
|
umount_unionfs(this);
|
|
|
|
if (this->dir > 0)
|
|
|
|
{
|
|
|
|
close(this->dir);
|
|
|
|
}
|
2008-07-04 06:58:04 +00:00
|
|
|
this->ifaces->destroy(this->ifaces);
|
2007-07-30 13:20:35 +00:00
|
|
|
free(this->dirname);
|
2007-07-24 14:22:56 +00:00
|
|
|
free(this->name);
|
|
|
|
free(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2007-07-30 13:20:35 +00:00
|
|
|
* generic guest constructor
|
2007-07-24 14:22:56 +00:00
|
|
|
*/
|
2007-07-30 13:20:35 +00:00
|
|
|
static private_guest_t *guest_create_generic(char *parent, char *name,
|
|
|
|
bool create)
|
2007-07-24 14:22:56 +00:00
|
|
|
{
|
2007-07-30 13:20:35 +00:00
|
|
|
char cwd[PATH_MAX];
|
2007-07-24 14:22:56 +00:00
|
|
|
private_guest_t *this = malloc_thing(private_guest_t);
|
|
|
|
|
|
|
|
this->public.get_name = (void*)get_name;
|
2007-07-26 13:21:06 +00:00
|
|
|
this->public.get_pid = (pid_t(*)(guest_t*))get_pid;
|
|
|
|
this->public.get_state = (guest_state_t(*)(guest_t*))get_state;
|
2007-07-25 13:23:45 +00:00
|
|
|
this->public.create_iface = (iface_t*(*)(guest_t*,char*))create_iface;
|
2008-04-28 16:43:30 +00:00
|
|
|
this->public.destroy_iface = (void(*)(guest_t*,iface_t*))destroy_iface;
|
|
|
|
this->public.create_iface_enumerator = (enumerator_t*(*)(guest_t*))create_iface_enumerator;
|
2007-07-24 14:22:56 +00:00
|
|
|
this->public.start = (void*)start;
|
|
|
|
this->public.stop = (void*)stop;
|
2007-09-13 08:10:36 +00:00
|
|
|
this->public.load_template = (bool(*)(guest_t*, char *path))load_template;
|
2008-07-08 14:58:20 +00:00
|
|
|
this->public.exec = (int(*)(guest_t*, void(*cb)(void*,char*,size_t), void *data, char *cmd, ...))exec;
|
2007-07-27 07:37:15 +00:00
|
|
|
this->public.sigchild = (void(*)(guest_t*))sigchild;
|
2007-07-24 14:22:56 +00:00
|
|
|
this->public.destroy = (void*)destroy;
|
2007-08-28 07:53:46 +00:00
|
|
|
|
2007-07-30 13:20:35 +00:00
|
|
|
if (*parent == '/' || getcwd(cwd, sizeof(cwd)) == NULL)
|
|
|
|
{
|
|
|
|
asprintf(&this->dirname, "%s/%s", parent, name);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
asprintf(&this->dirname, "%s/%s/%s", cwd, parent, name);
|
|
|
|
}
|
|
|
|
if (create)
|
|
|
|
{
|
|
|
|
mkdir(this->dirname, PERME);
|
|
|
|
}
|
|
|
|
this->dir = open(this->dirname, O_DIRECTORY, PERME);
|
|
|
|
if (this->dir < 0)
|
2007-07-24 14:22:56 +00:00
|
|
|
{
|
2007-07-30 13:20:35 +00:00
|
|
|
DBG1("opening guest directory '%s' failed: %m", this->dirname);
|
|
|
|
free(this->dirname);
|
2007-07-24 14:22:56 +00:00
|
|
|
free(this);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
this->pid = 0;
|
2007-07-26 13:21:06 +00:00
|
|
|
this->state = GUEST_STOPPED;
|
2007-07-25 13:23:45 +00:00
|
|
|
this->mconsole = NULL;
|
|
|
|
this->ifaces = linked_list_create();
|
2007-07-30 13:20:35 +00:00
|
|
|
this->mem = 0;
|
|
|
|
this->name = strdup(name);
|
2007-07-31 15:23:23 +00:00
|
|
|
this->cowfs = NULL;
|
2007-07-30 13:20:35 +00:00
|
|
|
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* create a symlink to old called new in our working dir
|
|
|
|
*/
|
|
|
|
static bool make_symlink(private_guest_t *this, char *old, char *new)
|
|
|
|
{
|
|
|
|
char cwd[PATH_MAX];
|
|
|
|
char buf[PATH_MAX];
|
|
|
|
|
|
|
|
if (*old == '/' || getcwd(cwd, sizeof(cwd)) == NULL)
|
|
|
|
{
|
|
|
|
snprintf(buf, sizeof(buf), "%s", old);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
snprintf(buf, sizeof(buf), "%s/%s", cwd, old);
|
|
|
|
}
|
|
|
|
return symlinkat(buf, this->dir, new) == 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* create the guest instance, including required dirs and mounts
|
|
|
|
*/
|
|
|
|
guest_t *guest_create(char *parent, char *name, char *kernel,
|
|
|
|
char *master, int mem)
|
|
|
|
{
|
|
|
|
private_guest_t *this = guest_create_generic(parent, name, TRUE);
|
|
|
|
|
|
|
|
if (this == NULL)
|
|
|
|
{
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!make_symlink(this, master, MASTER_DIR) ||
|
|
|
|
!make_symlink(this, kernel, KERNEL_FILE))
|
|
|
|
{
|
|
|
|
DBG1("creating master/kernel symlink failed: %m");
|
|
|
|
destroy(this);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mkdirat(this->dir, UNION_DIR, PERME) != 0 ||
|
|
|
|
mkdirat(this->dir, DIFF_DIR, PERME) != 0)
|
|
|
|
{
|
|
|
|
DBG1("unable to create directories for '%s': %m", name);
|
|
|
|
destroy(this);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
this->mem = mem;
|
|
|
|
if (!savemem(this, mem))
|
|
|
|
{
|
|
|
|
destroy(this);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!mount_unionfs(this))
|
|
|
|
{
|
|
|
|
destroy(this);
|
|
|
|
return NULL;
|
|
|
|
}
|
2007-07-24 14:22:56 +00:00
|
|
|
|
|
|
|
return &this->public;
|
|
|
|
}
|
|
|
|
|
2007-07-30 13:20:35 +00:00
|
|
|
/**
|
|
|
|
* load an already created guest
|
|
|
|
*/
|
|
|
|
guest_t *guest_load(char *parent, char *name)
|
|
|
|
{
|
|
|
|
private_guest_t *this = guest_create_generic(parent, name, FALSE);
|
|
|
|
|
|
|
|
if (this == NULL)
|
|
|
|
{
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
this->mem = loadmem(this);
|
|
|
|
if (this->mem == 0)
|
|
|
|
{
|
|
|
|
DBG1("unable to open memory configuration file: %m", name);
|
|
|
|
destroy(this);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!mount_unionfs(this))
|
|
|
|
{
|
|
|
|
destroy(this);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return &this->public;
|
|
|
|
}
|
|
|
|
|