diff options
Diffstat (limited to 'src/dumm/guest.c')
-rw-r--r-- | src/dumm/guest.c | 682 |
1 files changed, 0 insertions, 682 deletions
diff --git a/src/dumm/guest.c b/src/dumm/guest.c deleted file mode 100644 index 327b86c63..000000000 --- a/src/dumm/guest.c +++ /dev/null @@ -1,682 +0,0 @@ -/* - * Copyright (C) 2008-2009 Tobias Brunner - * Copyright (C) 2007 Martin Willi - * HSR 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. - */ - -#define _GNU_SOURCE - -#include <sys/types.h> -#include <sys/stat.h> -#include <sys/wait.h> -#include <sys/uio.h> -#include <unistd.h> -#include <stdio.h> -#include <fcntl.h> -#include <signal.h> -#include <dirent.h> -#include <termios.h> -#include <stdarg.h> - -#include <utils/debug.h> -#include <collections/linked_list.h> - -#include "dumm.h" -#include "guest.h" -#include "mconsole.h" -#include "cowfs.h" - -#define PERME (S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) -#define PERM (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH) - -#define MASTER_DIR "master" -#define DIFF_DIR "diff" -#define UNION_DIR "union" -#define ARGS_FILE "args" -#define PID_FILE "pid" -#define KERNEL_FILE "linux" -#define LOG_FILE "boot.log" -#define NOTIFY_FILE "notify" -#define PTYS 0 - -typedef struct private_guest_t private_guest_t; - -struct private_guest_t { - /** implemented public interface */ - guest_t public; - /** name of the guest */ - char *name; - /** directory of guest */ - int dir; - /** directory name of guest */ - char *dirname; - /** additional args to pass to guest */ - char *args; - /** pid of guest child process */ - int pid; - /** state of guest */ - guest_state_t state; - /** FUSE cowfs instance */ - cowfs_t *cowfs; - /** mconsole to control running UML */ - mconsole_t *mconsole; - /** list of interfaces attached to the guest */ - linked_list_t *ifaces; -}; - -ENUM(guest_state_names, GUEST_STOPPED, GUEST_STOPPING, - "STOPPED", - "STARTING", - "RUNNING", - "PAUSED", - "STOPPING", -); - -METHOD(guest_t, get_name, char*, - private_guest_t *this) -{ - return this->name; -} - -METHOD(guest_t, create_iface, iface_t*, - private_guest_t *this, char *name) -{ - enumerator_t *enumerator; - iface_t *iface; - - if (this->state != GUEST_RUNNING) - { - DBG1(DBG_LIB, "guest '%s' not running, unable to add interface", - this->name); - return NULL; - } - - enumerator = this->ifaces->create_enumerator(this->ifaces); - while (enumerator->enumerate(enumerator, (void**)&iface)) - { - if (streq(name, iface->get_guestif(iface))) - { - DBG1(DBG_LIB, "guest '%s' already has an interface '%s'", - this->name, name); - enumerator->destroy(enumerator); - return NULL; - } - } - enumerator->destroy(enumerator); - - iface = iface_create(name, &this->public, this->mconsole); - if (iface) - { - this->ifaces->insert_last(this->ifaces, iface); - } - return iface; -} - -METHOD(guest_t, destroy_iface, void, - private_guest_t *this, iface_t *iface) -{ - 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); -} - -METHOD(guest_t, create_iface_enumerator, enumerator_t*, - private_guest_t *this) -{ - return this->ifaces->create_enumerator(this->ifaces); -} - -METHOD(guest_t, get_state, guest_state_t, - private_guest_t *this) -{ - return this->state; -} - -METHOD(guest_t, get_pid, pid_t, - private_guest_t *this) -{ - return this->pid; -} - -/** - * write format string to a buffer, and advance buffer position - */ -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; -} - -METHOD(guest_t, stop, void, - private_guest_t *this, idle_function_t idle) -{ - 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_STOPPED) - { - if (idle) - { - idle(); - } - else - { - usleep(50000); - } - } - unlinkat(this->dir, PID_FILE, 0); - this->pid = 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); - } -} - -METHOD(guest_t, start, bool, - private_guest_t *this, invoke_function_t invoke, void* data, - idle_function_t idle) -{ - char buf[2048]; - char *notify; - char *pos = buf; - char *args[32]; - int i = 0; - size_t left = sizeof(buf); - - memset(args, 0, sizeof(args)); - - if (this->state != GUEST_STOPPED) - { - DBG1(DBG_LIB, "unable to start guest in state %N", guest_state_names, - this->state); - return FALSE; - } - this->state = GUEST_STARTING; - - notify = write_arg(&pos, &left, "%s/%s", this->dirname, NOTIFY_FILE); - - args[i++] = write_arg(&pos, &left, "nice"); - args[i++] = write_arg(&pos, &left, "%s/%s", this->dirname, KERNEL_FILE); - 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", this->dirname, UNION_DIR); - args[i++] = write_arg(&pos, &left, "uml_dir=%s", this->dirname); - args[i++] = write_arg(&pos, &left, "umid=%s", this->name); - args[i++] = write_arg(&pos, &left, "mconsole=notify:%s", notify); - args[i++] = write_arg(&pos, &left, "con=null"); - if (this->args) - { - args[i++] = this->args; - } - - this->pid = invoke(data, &this->public, args, i); - if (!this->pid) - { - this->state = GUEST_STOPPED; - return FALSE; - } - savepid(this); - - /* open mconsole */ - this->mconsole = mconsole_create(notify, idle); - if (this->mconsole == NULL) - { - DBG1(DBG_LIB, "opening mconsole at '%s' failed, stopping guest", buf); - stop(this, NULL); - return FALSE; - } - - this->state = GUEST_RUNNING; - return TRUE; -} - -METHOD(guest_t, add_overlay, bool, - private_guest_t *this, char *path) -{ - if (path == NULL) - { - return FALSE; - } - - if (access(path, F_OK) != 0) - { - if (!mkdir_p(path, PERME)) - { - DBG1(DBG_LIB, "creating overlay for guest '%s' failed: %m", - this->name); - return FALSE; - } - } - - return this->cowfs->add_overlay(this->cowfs, path); -} - -METHOD(guest_t, del_overlay, bool, - private_guest_t *this, char *path) -{ - return this->cowfs->del_overlay(this->cowfs, path); -} - -METHOD(guest_t, pop_overlay, bool, - private_guest_t *this) -{ - return this->cowfs->pop_overlay(this->cowfs); -} - -/** - * Variadic version of the exec function - */ -static int vexec(private_guest_t *this, void(*cb)(void*,char*,size_t), void *data, - char *cmd, va_list args) -{ - char buf[1024]; - size_t len; - - if (this->mconsole) - { - len = vsnprintf(buf, sizeof(buf), cmd, args); - - if (len > 0 && len < sizeof(buf)) - { - return this->mconsole->exec(this->mconsole, cb, data, buf); - } - } - return -1; -} - -METHOD(guest_t, exec, int, - private_guest_t *this, void(*cb)(void*,char*,size_t), void *data, - char *cmd, ...) -{ - int res; - va_list args; - va_start(args, cmd); - res = vexec(this, cb, data, cmd, args); - va_end(args); - return res; -} - -typedef struct { - chunk_t buf; - void (*cb)(void*,char*); - void *data; -} exec_str_t; - -/** - * callback that combines chunks to a string. if a callback is given, the string - * is split at newlines and the callback is called for each line. - */ -static void exec_str_cb(exec_str_t *data, char *buf, size_t len) -{ - if (!data->buf.ptr) - { - data->buf = chunk_alloc(len + 1); - memcpy(data->buf.ptr, buf, len); - data->buf.ptr[len] = '\0'; - } - else - { - size_t newlen = strlen(data->buf.ptr) + len + 1; - if (newlen > data->buf.len) - { - data->buf.ptr = realloc(data->buf.ptr, newlen); - data->buf.len = newlen; - } - strncat(data->buf.ptr, buf, len); - } - - if (data->cb) - { - char *nl; - while ((nl = strchr(data->buf.ptr, '\n')) != NULL) - { - *nl++ = '\0'; - data->cb(data->data, data->buf.ptr); - memmove(data->buf.ptr, nl, strlen(nl) + 1); - } - } -} - -METHOD(guest_t, exec_str, int, - private_guest_t *this, void(*cb)(void*,char*), bool lines, void *data, - char *cmd, ...) -{ - int res; - va_list args; - va_start(args, cmd); - if (cb) - { - exec_str_t exec = { chunk_empty, NULL, NULL }; - if (lines) - { - exec.cb = cb; - exec.data = data; - } - res = vexec(this, (void(*)(void*,char*,size_t))exec_str_cb, &exec, cmd, args); - if (exec.buf.ptr) - { - if (!lines || strlen(exec.buf.ptr) > 0) - { - /* return the complete string or the remaining stuff in the - * buffer (i.e. when there was no newline at the end) */ - cb(data, exec.buf.ptr); - } - chunk_free(&exec.buf); - } - } - else - { - res = vexec(this, NULL, NULL, cmd, args); - } - va_end(args); - return res; -} - -METHOD(guest_t, sigchild, void, - private_guest_t *this) -{ - DESTROY_IF(this->mconsole); - this->mconsole = NULL; - this->state = GUEST_STOPPED; -} - -/** - * umount the union filesystem - */ -static bool umount_unionfs(private_guest_t *this) -{ - if (this->cowfs) - { - this->cowfs->destroy(this->cowfs); - this->cowfs = NULL; - return TRUE; - } - return FALSE; -} - -/** - * mount the union filesystem - */ -static bool mount_unionfs(private_guest_t *this) -{ - char master[PATH_MAX]; - char diff[PATH_MAX]; - char mount[PATH_MAX]; - - if (this->cowfs == NULL) - { - 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); - - this->cowfs = cowfs_create(master, diff, mount); - if (this->cowfs) - { - return TRUE; - } - } - return FALSE; -} - -/** - * load args configuration from file - */ -char *loadargs(private_guest_t *this) -{ - FILE *file; - char buf[512], *args = NULL; - - file = fdopen(openat(this->dir, ARGS_FILE, O_RDONLY, PERM), "r"); - if (file) - { - if (fgets(buf, sizeof(buf), file)) - { - args = strdup(buf); - } - fclose(file); - } - return args; -} - -/** - * save args configuration to file - */ -bool saveargs(private_guest_t *this, char *args) -{ - FILE *file; - bool retval = FALSE; - - file = fdopen(openat(this->dir, ARGS_FILE, O_RDWR | O_CREAT | O_TRUNC, - PERM), "w"); - if (file) - { - if (fprintf(file, "%s", args) > 0) - { - retval = TRUE; - } - fclose(file); - } - return retval; -} - -METHOD(guest_t, destroy, void, - private_guest_t *this) -{ - stop(this, NULL); - umount_unionfs(this); - if (this->dir > 0) - { - close(this->dir); - } - this->ifaces->destroy(this->ifaces); - free(this->dirname); - free(this->args); - free(this->name); - free(this); -} - -/** - * generic guest constructor - */ -static private_guest_t *guest_create_generic(char *parent, char *name, - bool create) -{ - char cwd[PATH_MAX]; - private_guest_t *this; - - INIT(this, - .public = { - .get_name = _get_name, - .get_pid = _get_pid, - .get_state = _get_state, - .create_iface = _create_iface, - .destroy_iface = _destroy_iface, - .create_iface_enumerator = _create_iface_enumerator, - .start = _start, - .stop = _stop, - .add_overlay = _add_overlay, - .del_overlay = _del_overlay, - .pop_overlay = _pop_overlay, - .exec = _exec, - .exec_str = _exec_str, - .sigchild = _sigchild, - .destroy = _destroy, - } - ); - - if (*parent == '/' || getcwd(cwd, sizeof(cwd)) == NULL) - { - if (asprintf(&this->dirname, "%s/%s", parent, name) < 0) - { - this->dirname = NULL; - } - } - else - { - if (asprintf(&this->dirname, "%s/%s/%s", cwd, parent, name) < 0) - { - this->dirname = NULL; - } - } - if (this->dirname == NULL) - { - free(this); - return NULL; - } - if (create) - { - mkdir(this->dirname, PERME); - } - this->dir = open(this->dirname, O_DIRECTORY, PERME); - if (this->dir < 0) - { - DBG1(DBG_LIB, "opening guest directory '%s' failed: %m", this->dirname); - free(this->dirname); - free(this); - return NULL; - } - this->state = GUEST_STOPPED; - this->ifaces = linked_list_create(); - this->name = strdup(name); - - 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, char *args) -{ - 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(DBG_LIB, "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(DBG_LIB, "unable to create directories for '%s': %m", name); - destroy(this); - return NULL; - } - - this->args = args; - if (args && !saveargs(this, args)) - { - destroy(this); - return NULL; - } - - if (!mount_unionfs(this)) - { - destroy(this); - return NULL; - } - - return &this->public; -} - -/** - * 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->args = loadargs(this); - - if (!mount_unionfs(this)) - { - destroy(this); - return NULL; - } - - return &this->public; -} - |