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-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-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-25 13:23:45 +00:00
|
|
|
/** log file for console 0 */
|
2007-07-24 14:22:56 +00:00
|
|
|
int bootlog;
|
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)
|
|
|
|
{
|
|
|
|
iterator_t *iterator;
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
iterator = this->ifaces->create_iterator(this->ifaces, TRUE);
|
|
|
|
while (iterator->iterate(iterator, (void**)&iface))
|
|
|
|
{
|
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);
|
|
|
|
iterator->destroy(iterator);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
iterator->destroy(iterator);
|
|
|
|
|
2007-07-26 07:39:49 +00:00
|
|
|
iface = iface_create(this->name, name, this->mconsole);
|
2007-07-25 13:23:45 +00:00
|
|
|
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);
|
|
|
|
}
|
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.
|
|
|
|
*/
|
|
|
|
static void stop(private_guest_t *this)
|
|
|
|
{
|
|
|
|
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);
|
|
|
|
while (this->state == GUEST_STOPPING)
|
|
|
|
{
|
|
|
|
sched_yield();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-07-25 13:23:45 +00:00
|
|
|
/**
|
|
|
|
* Implementation of guest_t.start.
|
|
|
|
*/
|
2007-07-26 13:21:06 +00:00
|
|
|
static bool start(private_guest_t *this, char *kernel)
|
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;
|
|
|
|
char *args[16];
|
|
|
|
int i = 0;
|
|
|
|
size_t left = sizeof(buf);
|
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);
|
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);
|
2007-07-26 13:21:06 +00:00
|
|
|
/*args[i++] = write_arg(&pos, &left, "con1=fd:0,fd:1");*/
|
|
|
|
args[i++] = write_arg(&pos, &left, "con2=null,null");
|
2007-07-24 14:22:56 +00:00
|
|
|
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-30 13:20:35 +00:00
|
|
|
DBG1("starting UML kernel '%s' failed: %m", args[0]);
|
2007-07-24 14:22:56 +00:00
|
|
|
exit(1);
|
|
|
|
case -1:
|
2007-07-30 13:20:35 +00:00
|
|
|
this->state = GUEST_STOPPED;
|
2007-07-24 14:22:56 +00:00
|
|
|
return FALSE;
|
|
|
|
default:
|
2007-07-25 13:23:45 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
/* open mconsole */
|
2007-07-26 13:21:06 +00:00
|
|
|
this->mconsole = mconsole_create(notify);
|
2007-07-25 13:23:45 +00:00
|
|
|
if (this->mconsole == NULL)
|
|
|
|
{
|
|
|
|
DBG1("opening mconsole at '%s' failed, stopping guest", buf);
|
2007-07-30 13:20:35 +00:00
|
|
|
stop(this);
|
2007-07-25 13:23:45 +00:00
|
|
|
return FALSE;
|
2007-07-24 14:22:56 +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-07-24 14:22:56 +00:00
|
|
|
}
|
|
|
|
|
2007-07-27 07:37:15 +00:00
|
|
|
/**
|
|
|
|
* Implementation of guest_t.sigchild.
|
|
|
|
*/
|
|
|
|
static void sigchild(private_guest_t *this)
|
|
|
|
{
|
2007-07-30 13:20:35 +00:00
|
|
|
waitpid(this->pid, NULL, WNOHANG);
|
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];
|
2007-07-24 14:22:56 +00:00
|
|
|
|
2007-07-31 15:23:23 +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);
|
|
|
|
|
|
|
|
if (this->cowfs == NULL)
|
2007-07-24 14:22:56 +00:00
|
|
|
{
|
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
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* open logfile for boot messages
|
|
|
|
*/
|
2007-07-30 13:20:35 +00:00
|
|
|
static int open_bootlog(private_guest_t *this)
|
2007-07-24 14:22:56 +00:00
|
|
|
{
|
|
|
|
int fd;
|
|
|
|
|
2007-07-30 13:20:35 +00:00
|
|
|
fd = openat(this->dir, LOG_FILE, O_WRONLY | O_CREAT, PERM);
|
2007-07-24 14:22:56 +00:00
|
|
|
if (fd == -1)
|
|
|
|
{
|
2007-07-30 13:20:35 +00:00
|
|
|
DBG1("opening bootlog failed, using stdout");
|
2007-07-24 14:22:56 +00:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
return fd;
|
|
|
|
}
|
|
|
|
|
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)
|
|
|
|
{
|
|
|
|
stop(this);
|
2007-07-30 13:20:35 +00:00
|
|
|
umount_unionfs(this);
|
|
|
|
if (this->bootlog > 1)
|
|
|
|
{
|
|
|
|
close(this->bootlog);
|
|
|
|
}
|
|
|
|
if (this->dir > 0)
|
|
|
|
{
|
|
|
|
close(this->dir);
|
|
|
|
}
|
|
|
|
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;
|
|
|
|
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;
|
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-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->bootlog = open_bootlog(this);
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|