dumm: Remove the Dynamic UML Mesh Modeler framework
This has been pretty much defunct for several years (requires a specially patched UML-enabled guest kernel).
This commit is contained in:
@@ -280,7 +280,6 @@ ARG_ENABL_SET([aikgen], [enable AIK generator for TPM 1.2.])
|
||||
ARG_DISBL_SET([charon], [disable the IKEv1/IKEv2 keying daemon charon.])
|
||||
ARG_ENABL_SET([cmd], [enable the command line IKE client charon-cmd.])
|
||||
ARG_ENABL_SET([conftest], [enforce Suite B conformance test framework.])
|
||||
ARG_ENABL_SET([dumm], [enable the DUMM UML test framework.])
|
||||
ARG_ENABL_SET([fast], [enable libfast (FastCGI Application Server w/ templates.])
|
||||
ARG_ENABL_SET([fuzzing], [enable fuzzing scripts (found in directory fuzz).])
|
||||
ARG_ENABL_SET([libipsec], [enable user space IPsec implementation.])
|
||||
@@ -1051,18 +1050,6 @@ if test x$imc_swima = xtrue -o $imv_swima = xtrue; then
|
||||
AC_SUBST(json_LIBS)
|
||||
fi
|
||||
|
||||
if test x$dumm = xtrue; then
|
||||
PKG_CHECK_MODULES(gtk, [gtk+-2.0 vte])
|
||||
AC_SUBST(gtk_CFLAGS)
|
||||
AC_SUBST(gtk_LIBS)
|
||||
AC_CHECK_PROGS(RUBY, ruby)
|
||||
PKG_CHECK_MODULES(ruby, [ruby])
|
||||
saved_LIBS=$LIBS
|
||||
LIBS=$ruby_LIBS
|
||||
AC_CHECK_FUNCS(rb_errinfo)
|
||||
LIBS=$saved_LIBS
|
||||
fi
|
||||
|
||||
if test x$fast = xtrue; then
|
||||
AC_CHECK_LIB([neo_utl],[hdf_init],[LIBS="$LIBS"],[AC_MSG_ERROR([ClearSilver library neo_utl not found!])],[])
|
||||
AC_MSG_CHECKING([for -lneo_cgi and dependencies])
|
||||
@@ -1691,7 +1678,6 @@ AM_CONDITIONAL(USE_COUNTERS, test x$counters = xtrue)
|
||||
# ---------------
|
||||
AM_CONDITIONAL(USE_LEAK_DETECTIVE, test x$leak_detective = xtrue)
|
||||
AM_CONDITIONAL(USE_LOCK_PROFILER, test x$lock_profiler = xtrue)
|
||||
AM_CONDITIONAL(USE_DUMM, test x$dumm = xtrue)
|
||||
AM_CONDITIONAL(USE_FAST, test x$fast = xtrue)
|
||||
AM_CONDITIONAL(USE_MANAGER, test x$manager = xtrue)
|
||||
AM_CONDITIONAL(USE_ME, test x$mediation = xtrue)
|
||||
@@ -2001,8 +1987,6 @@ AC_CONFIG_FILES([
|
||||
src/pki/Makefile
|
||||
src/pki/man/Makefile
|
||||
src/pool/Makefile
|
||||
src/dumm/Makefile
|
||||
src/dumm/ext/extconf.rb
|
||||
src/libfast/Makefile
|
||||
src/manager/Makefile
|
||||
src/medsrv/Makefile
|
||||
|
||||
+1
-1
@@ -31,7 +31,7 @@ printf-builtin)
|
||||
;;
|
||||
all|coverage|sonarcloud)
|
||||
CONFIG="--enable-all --disable-android-dns --disable-android-log
|
||||
--disable-dumm --disable-kernel-pfroute --disable-keychain
|
||||
--disable-kernel-pfroute --disable-keychain
|
||||
--disable-lock-profiler --disable-padlock --disable-fuzzing
|
||||
--disable-osx-attr --disable-tkm --disable-uci
|
||||
--disable-systemd --disable-soup --disable-unwind-backtraces
|
||||
|
||||
@@ -88,10 +88,6 @@ if USE_CONFTEST
|
||||
SUBDIRS += conftest
|
||||
endif
|
||||
|
||||
if USE_DUMM
|
||||
SUBDIRS += dumm
|
||||
endif
|
||||
|
||||
if USE_FAST
|
||||
SUBDIRS += libfast
|
||||
endif
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
dumm
|
||||
irdumm
|
||||
@@ -1,34 +0,0 @@
|
||||
EXTRA_DIST = ext/dumm.c ext/README \
|
||||
ext/lib/dumm.rb ext/lib/dumm/guest.rb
|
||||
|
||||
ipseclib_LTLIBRARIES = libdumm.la
|
||||
ipsec_PROGRAMS = dumm irdumm
|
||||
|
||||
libdumm_la_SOURCES = dumm.c dumm.h guest.c guest.h iface.c iface.h \
|
||||
bridge.c bridge.h mconsole.c mconsole.h cowfs.h cowfs.c
|
||||
dumm_SOURCES = main.c
|
||||
irdumm_SOURCES = irdumm.c
|
||||
|
||||
libdumm_la_LIBADD = -lbridge -lfuse -lutil $(top_builddir)/src/libstrongswan/libstrongswan.la
|
||||
dumm_LDADD = libdumm.la ${gtk_LIBS} $(top_builddir)/src/libstrongswan/libstrongswan.la
|
||||
irdumm_LDADD = libdumm.la ${ruby_LIBS} $(top_builddir)/src/libstrongswan/libstrongswan.la
|
||||
|
||||
AM_CPPFLAGS = \
|
||||
-D_FILE_OFFSET_BITS=64 \
|
||||
-I$(top_srcdir)/src/libstrongswan
|
||||
|
||||
dumm_CFLAGS = ${gtk_CFLAGS}
|
||||
irdumm_CFLAGS = ${ruby_CFLAGS}
|
||||
|
||||
all-local: ext
|
||||
|
||||
clean-local:
|
||||
(test -f ext/Makefile && cd ext && $(MAKE) clean && rm Makefile || true)
|
||||
|
||||
install-data-local:
|
||||
(test -f ext/Makefile && cd ext && $(MAKE) install)
|
||||
|
||||
ext: libdumm.la
|
||||
(cd ext && $(RUBY) extconf.rb && $(MAKE))
|
||||
|
||||
.PHONY: ext
|
||||
@@ -1,181 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <netinet/in.h>
|
||||
#include <libbridge.h>
|
||||
|
||||
#include <utils/debug.h>
|
||||
#include <collections/linked_list.h>
|
||||
|
||||
#include "bridge.h"
|
||||
|
||||
typedef struct private_bridge_t private_bridge_t;
|
||||
|
||||
struct private_bridge_t {
|
||||
/** public interface */
|
||||
bridge_t public;
|
||||
/** device name */
|
||||
char *name;
|
||||
/** list of attached interfaces */
|
||||
linked_list_t *ifaces;
|
||||
};
|
||||
|
||||
/**
|
||||
* defined in iface.c
|
||||
*/
|
||||
bool iface_control(char *name, bool up);
|
||||
|
||||
METHOD(bridge_t, get_name, char*,
|
||||
private_bridge_t *this)
|
||||
{
|
||||
return this->name;
|
||||
}
|
||||
|
||||
METHOD(bridge_t, create_iface_enumerator, enumerator_t*,
|
||||
private_bridge_t *this)
|
||||
{
|
||||
return this->ifaces->create_enumerator(this->ifaces);
|
||||
}
|
||||
|
||||
METHOD(bridge_t, disconnect_iface, bool,
|
||||
private_bridge_t *this, iface_t *iface)
|
||||
{
|
||||
enumerator_t *enumerator;
|
||||
iface_t *current = NULL;
|
||||
bool good = FALSE;
|
||||
|
||||
enumerator = this->ifaces->create_enumerator(this->ifaces);
|
||||
while (enumerator->enumerate(enumerator, (void**)¤t))
|
||||
{
|
||||
if (current == iface)
|
||||
{
|
||||
if (br_del_interface(this->name, iface->get_hostif(iface)) != 0)
|
||||
{
|
||||
DBG1(DBG_LIB, "removing iface '%s' from bridge '%s' in kernel"
|
||||
" failed: %m", iface->get_hostif(iface), this->name);
|
||||
}
|
||||
else
|
||||
{
|
||||
iface->set_bridge(iface, NULL);
|
||||
this->ifaces->remove_at(this->ifaces, enumerator);
|
||||
good = TRUE;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (iface != current)
|
||||
{
|
||||
DBG1(DBG_LIB, "iface '%s' not found on bridge '%s'",
|
||||
iface->get_hostif(iface), this->name);
|
||||
}
|
||||
enumerator->destroy(enumerator);
|
||||
return good;
|
||||
}
|
||||
|
||||
METHOD(bridge_t, connect_iface, bool,
|
||||
private_bridge_t *this, iface_t *iface)
|
||||
{
|
||||
if (br_add_interface(this->name, iface->get_hostif(iface)) != 0)
|
||||
{
|
||||
DBG1(DBG_LIB, "adding iface '%s' to bridge '%s' failed: %m",
|
||||
iface->get_hostif(iface), this->name);
|
||||
return FALSE;
|
||||
}
|
||||
iface->set_bridge(iface, &this->public);
|
||||
this->ifaces->insert_last(this->ifaces, iface);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* instance counter to (de-)initialize libbridge
|
||||
*/
|
||||
static int instances = 0;
|
||||
|
||||
METHOD(bridge_t, destroy, void,
|
||||
private_bridge_t *this)
|
||||
{
|
||||
enumerator_t *enumerator;
|
||||
iface_t *iface;
|
||||
|
||||
enumerator = this->ifaces->create_enumerator(this->ifaces);
|
||||
while (enumerator->enumerate(enumerator, (void**)&iface))
|
||||
{
|
||||
if (br_del_interface(this->name, iface->get_hostif(iface)) != 0)
|
||||
{
|
||||
DBG1(DBG_LIB, "disconnecting iface '%s' failed: %m",
|
||||
iface->get_hostif(iface));
|
||||
}
|
||||
iface->set_bridge(iface, NULL);
|
||||
}
|
||||
enumerator->destroy(enumerator);
|
||||
this->ifaces->destroy(this->ifaces);
|
||||
iface_control(this->name, FALSE);
|
||||
if (br_del_bridge(this->name) != 0)
|
||||
{
|
||||
DBG1(DBG_LIB, "deleting bridge '%s' from kernel failed: %m",
|
||||
this->name);
|
||||
}
|
||||
free(this->name);
|
||||
free(this);
|
||||
if (--instances == 0)
|
||||
{
|
||||
br_shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* create the bridge instance
|
||||
*/
|
||||
bridge_t *bridge_create(char *name)
|
||||
{
|
||||
private_bridge_t *this;
|
||||
|
||||
if (instances == 0)
|
||||
{
|
||||
if (br_init() != 0)
|
||||
{
|
||||
DBG1(DBG_LIB, "libbridge initialization failed: %m");
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
INIT(this,
|
||||
.public = {
|
||||
.get_name = _get_name,
|
||||
.create_iface_enumerator = _create_iface_enumerator,
|
||||
.disconnect_iface = _disconnect_iface,
|
||||
.connect_iface = _connect_iface,
|
||||
.destroy = _destroy,
|
||||
}
|
||||
);
|
||||
|
||||
if (br_add_bridge(name) != 0)
|
||||
{
|
||||
DBG1(DBG_LIB, "creating bridge '%s' failed: %m", name);
|
||||
free(this);
|
||||
return NULL;
|
||||
}
|
||||
if (!iface_control(name, TRUE))
|
||||
{
|
||||
DBG1(DBG_LIB, "bringing bridge '%s' up failed: %m", name);
|
||||
}
|
||||
|
||||
this->name = strdup(name);
|
||||
this->ifaces = linked_list_create();
|
||||
|
||||
instances++;
|
||||
return &this->public;
|
||||
}
|
||||
@@ -1,76 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef BRIDGE_H
|
||||
#define BRIDGE_H
|
||||
|
||||
#include <library.h>
|
||||
#include <collections/enumerator.h>
|
||||
|
||||
typedef struct bridge_t bridge_t;
|
||||
|
||||
#include "iface.h"
|
||||
|
||||
/**
|
||||
* Interface in a guest, connected to a tap device on the host.
|
||||
*/
|
||||
struct bridge_t {
|
||||
|
||||
/**
|
||||
* Get the name of the bridge.
|
||||
*
|
||||
* @return name of the bridge
|
||||
*/
|
||||
char* (*get_name)(bridge_t *this);
|
||||
|
||||
/**
|
||||
* Add an interface to a bridge.
|
||||
*
|
||||
* @param iface interface to add
|
||||
* @return TRUE if interface added
|
||||
*/
|
||||
bool (*connect_iface)(bridge_t *this, iface_t *iface);
|
||||
|
||||
/**
|
||||
* Remove an interface from a bridge.
|
||||
*
|
||||
* @param iface interface to remove
|
||||
* @return TRUE if interface removed
|
||||
*/
|
||||
bool (*disconnect_iface)(bridge_t *this, iface_t *iface);
|
||||
|
||||
/**
|
||||
* Create an enumerator over all interfaces.
|
||||
*
|
||||
* @return enumerator over iface_t's
|
||||
*/
|
||||
enumerator_t* (*create_iface_enumerator)(bridge_t *this);
|
||||
|
||||
/**
|
||||
* Destroy a bridge
|
||||
*/
|
||||
void (*destroy) (bridge_t *this);
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a new bridge.
|
||||
*
|
||||
* @param name name of the bridge to create
|
||||
* @return bridge, NULL if failed
|
||||
*/
|
||||
bridge_t *bridge_create(char *name);
|
||||
|
||||
#endif /* BRIDGE_H */
|
||||
|
||||
@@ -1,980 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2009 Tobias Brunner
|
||||
* Copyright (C) 2007 Martin Willi
|
||||
* HSR Hochschule fuer Technik Rapperswil
|
||||
* Copyright (C) 2001-2007 Miklos Szeredi
|
||||
*
|
||||
* Based on example shipped with FUSE.
|
||||
*
|
||||
* 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 FUSE_USE_VERSION 26
|
||||
#define _GNU_SOURCE
|
||||
|
||||
#include <fuse.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
#include "cowfs.h"
|
||||
|
||||
#include <library.h>
|
||||
#include <utils/debug.h>
|
||||
#include <threading/thread.h>
|
||||
#include <threading/rwlock.h>
|
||||
#include <collections/linked_list.h>
|
||||
|
||||
/** define _XOPEN_SOURCE 500 fails when using libstrongswan, define popen */
|
||||
extern ssize_t pread(int fd, void *buf, size_t count, off_t offset);
|
||||
extern ssize_t pwrite(int fd, const void *buf, size_t count, off_t offset);
|
||||
|
||||
typedef struct private_cowfs_t private_cowfs_t;
|
||||
|
||||
struct private_cowfs_t {
|
||||
/** public cowfs interface */
|
||||
cowfs_t public;
|
||||
/** fuse channel to mountpoint */
|
||||
struct fuse_chan *chan;
|
||||
/** fuse handle */
|
||||
struct fuse *fuse;
|
||||
/** mountpoint of cowfs FUSE */
|
||||
char *mount;
|
||||
/** master filesystem path */
|
||||
char *master;
|
||||
/** host filesystem path */
|
||||
char *host;
|
||||
/** overlay filesystems */
|
||||
linked_list_t *overlays;
|
||||
/** lock for overlays */
|
||||
rwlock_t *lock;
|
||||
/** fd of read only master filesystem */
|
||||
int master_fd;
|
||||
/** copy on write overlay to master */
|
||||
int host_fd;
|
||||
/** thread processing FUSE */
|
||||
thread_t *thread;
|
||||
};
|
||||
|
||||
typedef struct overlay_t overlay_t;
|
||||
|
||||
/**
|
||||
* data for overlay filesystems
|
||||
*/
|
||||
struct overlay_t {
|
||||
/** path to overlay */
|
||||
char *path;
|
||||
/** overlay fd */
|
||||
int fd;
|
||||
};
|
||||
|
||||
/**
|
||||
* destroy an overlay
|
||||
*/
|
||||
static void overlay_destroy(overlay_t *this)
|
||||
{
|
||||
close(this->fd);
|
||||
free(this->path);
|
||||
free(this);
|
||||
}
|
||||
|
||||
CALLBACK(overlay_equals, bool,
|
||||
overlay_t *this, va_list args)
|
||||
{
|
||||
overlay_t *other;
|
||||
|
||||
VA_ARGS_VGET(args, other);
|
||||
return streq(this->path, other->path);
|
||||
}
|
||||
|
||||
/**
|
||||
* remove and destroy the overlay with the given absolute path.
|
||||
* returns FALSE, if not found.
|
||||
*/
|
||||
static bool overlay_remove(private_cowfs_t *this, char *path)
|
||||
{
|
||||
overlay_t over, *current;
|
||||
over.path = path;
|
||||
if (!this->overlays->find_first(this->overlays, overlay_equals,
|
||||
(void**)¤t, &over))
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
this->overlays->remove(this->overlays, current, NULL);
|
||||
overlay_destroy(current);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* get this pointer stored in fuse context
|
||||
*/
|
||||
static private_cowfs_t *get_this()
|
||||
{
|
||||
return (fuse_get_context())->private_data;
|
||||
}
|
||||
|
||||
/**
|
||||
* make a path relative
|
||||
*/
|
||||
static void rel(const char **path)
|
||||
{
|
||||
if (**path == '/')
|
||||
{
|
||||
(*path)++;
|
||||
}
|
||||
if (**path == '\0')
|
||||
{
|
||||
*path = ".";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* get the highest overlay in which path exists
|
||||
*/
|
||||
static int get_rd(const char *path)
|
||||
{
|
||||
overlay_t *over;
|
||||
enumerator_t *enumerator;
|
||||
private_cowfs_t *this = get_this();
|
||||
|
||||
this->lock->read_lock(this->lock);
|
||||
enumerator = this->overlays->create_enumerator(this->overlays);
|
||||
while (enumerator->enumerate(enumerator, (void**)&over))
|
||||
{
|
||||
if (faccessat(over->fd, path, F_OK, 0) == 0)
|
||||
{
|
||||
int fd = over->fd;
|
||||
enumerator->destroy(enumerator);
|
||||
this->lock->unlock(this->lock);
|
||||
return fd;
|
||||
}
|
||||
}
|
||||
enumerator->destroy(enumerator);
|
||||
this->lock->unlock(this->lock);
|
||||
|
||||
if (faccessat(this->host_fd, path, F_OK, 0) == 0)
|
||||
{
|
||||
return this->host_fd;
|
||||
}
|
||||
return this->master_fd;
|
||||
}
|
||||
|
||||
/**
|
||||
* get the highest overlay available, to write something
|
||||
*/
|
||||
static int get_wr(const char *path)
|
||||
{
|
||||
overlay_t *over;
|
||||
private_cowfs_t *this = get_this();
|
||||
int fd = this->host_fd;
|
||||
this->lock->read_lock(this->lock);
|
||||
if (this->overlays->get_first(this->overlays, (void**)&over) == SUCCESS)
|
||||
{
|
||||
fd = over->fd;
|
||||
}
|
||||
this->lock->unlock(this->lock);
|
||||
return fd;
|
||||
}
|
||||
|
||||
/**
|
||||
* create full "path" at "wr" the same way they exist at "rd"
|
||||
*/
|
||||
static bool clone_path(int rd, int wr, const char *path)
|
||||
{
|
||||
char *pos, *full;
|
||||
struct stat st;
|
||||
full = strdupa(path);
|
||||
pos = full;
|
||||
|
||||
while ((pos = strchr(pos, '/')))
|
||||
{
|
||||
*pos = '\0';
|
||||
if (fstatat(wr, full, &st, 0) < 0)
|
||||
{
|
||||
/* TODO: handle symlinks!? */
|
||||
if (fstatat(rd, full, &st, 0) < 0)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
if (mkdirat(wr, full, st.st_mode) < 0)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
*pos = '/';
|
||||
pos++;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* copy a (special) file from a readonly to a read-write overlay
|
||||
*/
|
||||
static int copy(const char *path)
|
||||
{
|
||||
char *buf[4096];
|
||||
int len;
|
||||
int rd, wr;
|
||||
int from, to;
|
||||
struct stat st;
|
||||
|
||||
rd = get_rd(path);
|
||||
wr = get_wr(path);
|
||||
|
||||
if (rd == wr)
|
||||
{
|
||||
/* already writeable */
|
||||
return wr;
|
||||
}
|
||||
if (fstatat(rd, path, &st, 0) < 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
if (!clone_path(rd, wr, path))
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
if (mknodat(wr, path, st.st_mode, st.st_rdev) < 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
/* copy if no special file */
|
||||
if (st.st_size)
|
||||
{
|
||||
from = openat(rd, path, O_RDONLY, st.st_mode);
|
||||
if (from < 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
to = openat(wr, path, O_WRONLY , st.st_mode);
|
||||
if (to < 0)
|
||||
{
|
||||
close(from);
|
||||
return -1;
|
||||
}
|
||||
while ((len = read(from, buf, sizeof(buf))) > 0)
|
||||
{
|
||||
if (write(to, buf, len) < len)
|
||||
{
|
||||
/* TODO: only on len < 0 ? */
|
||||
close(from);
|
||||
close(to);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
close(from);
|
||||
close(to);
|
||||
if (len < 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return wr;
|
||||
}
|
||||
|
||||
/**
|
||||
* FUSE getattr method
|
||||
*/
|
||||
static int cowfs_getattr(const char *path, struct stat *stbuf)
|
||||
{
|
||||
rel(&path);
|
||||
|
||||
if (fstatat(get_rd(path), path, stbuf, AT_SYMLINK_NOFOLLOW) < 0)
|
||||
{
|
||||
return -errno;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* FUSE access method
|
||||
*/
|
||||
static int cowfs_access(const char *path, int mask)
|
||||
{
|
||||
rel(&path);
|
||||
|
||||
if (faccessat(get_rd(path), path, mask, 0) < 0)
|
||||
{
|
||||
return -errno;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* FUSE readlink method
|
||||
*/
|
||||
static int cowfs_readlink(const char *path, char *buf, size_t size)
|
||||
{
|
||||
int res;
|
||||
|
||||
rel(&path);
|
||||
|
||||
res = readlinkat(get_rd(path), path, buf, size - 1);
|
||||
if (res < 0)
|
||||
{
|
||||
return -errno;
|
||||
}
|
||||
buf[res] = '\0';
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* get a directory stream of two concatenated paths
|
||||
*/
|
||||
static DIR* get_dir(char *dir, const char *subdir)
|
||||
{
|
||||
char *full;
|
||||
|
||||
if (dir == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
full = alloca(strlen(dir) + strlen(subdir) + 1);
|
||||
strcpy(full, dir);
|
||||
strcat(full, subdir);
|
||||
|
||||
return opendir(full);
|
||||
}
|
||||
|
||||
/**
|
||||
* check if a directory stream contains a directory
|
||||
*/
|
||||
static bool contains_dir(DIR *d, char *dirname)
|
||||
{
|
||||
struct dirent *ent;
|
||||
|
||||
rewinddir(d);
|
||||
while ((ent = readdir(d)))
|
||||
{
|
||||
if (streq(ent->d_name, dirname))
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* check if one of the higher overlays contains a directory
|
||||
*/
|
||||
static bool overlays_contain_dir(DIR **d, char *dirname)
|
||||
{
|
||||
for (; *d; ++d)
|
||||
{
|
||||
if (contains_dir(*d, dirname))
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* FUSE readdir method
|
||||
*/
|
||||
static int cowfs_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
|
||||
off_t offset, struct fuse_file_info *fi)
|
||||
{
|
||||
#define ADD_DIR(overlay, base, path) ({\
|
||||
DIR *dir = get_dir(base, path);\
|
||||
if (dir) { *(--overlay) = dir; }\
|
||||
})
|
||||
private_cowfs_t *this = get_this();
|
||||
int count;
|
||||
DIR **d, **overlays;
|
||||
struct stat st;
|
||||
struct dirent *ent;
|
||||
overlay_t *over;
|
||||
enumerator_t *enumerator;
|
||||
|
||||
memset(&st, 0, sizeof(st));
|
||||
|
||||
this->lock->read_lock(this->lock);
|
||||
/* create a null-terminated array of DIR objects for all overlays (including
|
||||
* the master and host layer). the order is from bottom to top */
|
||||
count = this->overlays->get_count(this->overlays) + 2;
|
||||
overlays = calloc(count + 1, sizeof(DIR*));
|
||||
d = &overlays[count];
|
||||
|
||||
enumerator = this->overlays->create_enumerator(this->overlays);
|
||||
while (enumerator->enumerate(enumerator, (void**)&over))
|
||||
{
|
||||
ADD_DIR(d, over->path, path);
|
||||
}
|
||||
enumerator->destroy(enumerator);
|
||||
this->lock->unlock(this->lock);
|
||||
|
||||
ADD_DIR(d, this->host, path);
|
||||
ADD_DIR(d, this->master, path);
|
||||
|
||||
for (; *d; ++d)
|
||||
{
|
||||
rewinddir(*d);
|
||||
while((ent = readdir(*d)))
|
||||
{
|
||||
if (!overlays_contain_dir(d + 1, ent->d_name))
|
||||
{
|
||||
st.st_ino = ent->d_ino;
|
||||
st.st_mode = ent->d_type << 12;
|
||||
filler(buf, ent->d_name, &st, 0);
|
||||
}
|
||||
}
|
||||
closedir(*d);
|
||||
}
|
||||
|
||||
free(overlays);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* FUSE mknod method
|
||||
*/
|
||||
static int cowfs_mknod(const char *path, mode_t mode, dev_t rdev)
|
||||
{
|
||||
int fd;
|
||||
rel(&path);
|
||||
|
||||
fd = get_wr(path);
|
||||
if (!clone_path(get_rd(path), fd, path))
|
||||
{
|
||||
return -errno;
|
||||
}
|
||||
|
||||
if (mknodat(fd, path, mode, rdev) < 0)
|
||||
{
|
||||
return -errno;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* FUSE mkdir method
|
||||
*/
|
||||
static int cowfs_mkdir(const char *path, mode_t mode)
|
||||
{
|
||||
int fd;
|
||||
rel(&path);
|
||||
|
||||
fd = get_wr(path);
|
||||
if (!clone_path(get_rd(path), fd, path))
|
||||
{
|
||||
return -errno;
|
||||
}
|
||||
if (mkdirat(fd, path, mode) < 0)
|
||||
{
|
||||
return -errno;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* FUSE unlink method
|
||||
*/
|
||||
static int cowfs_unlink(const char *path)
|
||||
{
|
||||
rel(&path);
|
||||
|
||||
/* TODO: whiteout master */
|
||||
if (unlinkat(get_wr(path), path, 0) < 0)
|
||||
{
|
||||
return -errno;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* FUSE rmdir method
|
||||
*/
|
||||
static int cowfs_rmdir(const char *path)
|
||||
{
|
||||
rel(&path);
|
||||
|
||||
/* TODO: whiteout master */
|
||||
if (unlinkat(get_wr(path), path, AT_REMOVEDIR) < 0)
|
||||
{
|
||||
return -errno;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* FUSE symlink method
|
||||
*/
|
||||
static int cowfs_symlink(const char *from, const char *to)
|
||||
{
|
||||
int fd;
|
||||
const char *fromrel = from;
|
||||
|
||||
rel(&to);
|
||||
rel(&fromrel);
|
||||
|
||||
fd = get_wr(to);
|
||||
if (!clone_path(get_rd(fromrel), fd, fromrel))
|
||||
{
|
||||
return -errno;
|
||||
}
|
||||
if (symlinkat(from, fd, to) < 0)
|
||||
{
|
||||
return -errno;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* FUSE rename method
|
||||
*/
|
||||
static int cowfs_rename(const char *from, const char *to)
|
||||
{
|
||||
int fd;
|
||||
|
||||
rel(&from);
|
||||
rel(&to);
|
||||
|
||||
fd = copy(from);
|
||||
if (fd < 0)
|
||||
{
|
||||
return -errno;
|
||||
}
|
||||
if (renameat(fd, from, get_wr(to), to) < 0)
|
||||
{
|
||||
return -errno;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* FUSE link method
|
||||
*/
|
||||
static int cowfs_link(const char *from, const char *to)
|
||||
{
|
||||
int rd, wr;
|
||||
|
||||
rel(&from);
|
||||
rel(&to);
|
||||
|
||||
rd = get_rd(from);
|
||||
wr = get_wr(to);
|
||||
|
||||
if (!clone_path(rd, wr, to))
|
||||
{
|
||||
DBG1(DBG_LIB, "cloning path '%s' failed", to);
|
||||
return -errno;
|
||||
}
|
||||
if (linkat(rd, from, wr, to, 0) < 0)
|
||||
{
|
||||
DBG1(DBG_LIB, "linking '%s' to '%s' failed", from, to);
|
||||
return -errno;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* FUSE chmod method
|
||||
*/
|
||||
static int cowfs_chmod(const char *path, mode_t mode)
|
||||
{
|
||||
int fd;
|
||||
struct stat st;
|
||||
|
||||
rel(&path);
|
||||
fd = get_rd(path);
|
||||
if (fstatat(fd, path, &st, 0) < 0)
|
||||
{
|
||||
return -errno;
|
||||
}
|
||||
if (st.st_mode == mode)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
fd = copy(path);
|
||||
if (fd < 0)
|
||||
{
|
||||
return -errno;
|
||||
}
|
||||
if (fchmodat(fd, path, mode, 0) < 0)
|
||||
{
|
||||
return -errno;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* FUSE chown method
|
||||
*/
|
||||
static int cowfs_chown(const char *path, uid_t uid, gid_t gid)
|
||||
{
|
||||
int fd;
|
||||
struct stat st;
|
||||
|
||||
rel(&path);
|
||||
fd = get_rd(path);
|
||||
if (fstatat(fd, path, &st, 0) < 0)
|
||||
{
|
||||
return -errno;
|
||||
}
|
||||
if (st.st_uid == uid && st.st_gid == gid)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
fd = copy(path);
|
||||
if (fd < 0)
|
||||
{
|
||||
return -errno;
|
||||
}
|
||||
if (fchownat(fd, path, uid, gid, AT_SYMLINK_NOFOLLOW) < 0)
|
||||
{
|
||||
return -errno;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* FUSE truncate method
|
||||
*/
|
||||
static int cowfs_truncate(const char *path, off_t size)
|
||||
{
|
||||
int fd;
|
||||
struct stat st;
|
||||
|
||||
rel(&path);
|
||||
fd = get_rd(path);
|
||||
if (fstatat(fd, path, &st, 0) < 0)
|
||||
{
|
||||
return -errno;
|
||||
}
|
||||
if (st.st_size == size)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
fd = copy(path);
|
||||
if (fd < 0)
|
||||
{
|
||||
return -errno;
|
||||
}
|
||||
fd = openat(fd, path, O_WRONLY);
|
||||
if (fd < 0)
|
||||
{
|
||||
return -errno;
|
||||
}
|
||||
if (ftruncate(fd, size) < 0)
|
||||
{
|
||||
close(fd);
|
||||
return -errno;
|
||||
}
|
||||
close(fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* FUSE utimens method
|
||||
*/
|
||||
static int cowfs_utimens(const char *path, const struct timespec ts[2])
|
||||
{
|
||||
struct timeval tv[2];
|
||||
int fd;
|
||||
|
||||
rel(&path);
|
||||
fd = copy(path);
|
||||
if (fd < 0)
|
||||
{
|
||||
return -errno;
|
||||
}
|
||||
|
||||
tv[0].tv_sec = ts[0].tv_sec;
|
||||
tv[0].tv_usec = ts[0].tv_nsec / 1000;
|
||||
tv[1].tv_sec = ts[1].tv_sec;
|
||||
tv[1].tv_usec = ts[1].tv_nsec / 1000;
|
||||
|
||||
if (futimesat(fd, path, tv) < 0)
|
||||
{
|
||||
return -errno;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* FUSE open method
|
||||
*/
|
||||
static int cowfs_open(const char *path, struct fuse_file_info *fi)
|
||||
{
|
||||
int fd;
|
||||
|
||||
rel(&path);
|
||||
fd = get_rd(path);
|
||||
|
||||
fd = openat(fd, path, fi->flags);
|
||||
if (fd < 0)
|
||||
{
|
||||
return -errno;
|
||||
}
|
||||
close(fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* FUSE read method
|
||||
*/
|
||||
static int cowfs_read(const char *path, char *buf, size_t size, off_t offset,
|
||||
struct fuse_file_info *fi)
|
||||
{
|
||||
int file, fd, res;
|
||||
|
||||
rel(&path);
|
||||
|
||||
fd = get_rd(path);
|
||||
|
||||
file = openat(fd, path, O_RDONLY);
|
||||
if (file < 0)
|
||||
{
|
||||
return -errno;
|
||||
}
|
||||
|
||||
res = pread(file, buf, size, offset);
|
||||
if (res < 0)
|
||||
{
|
||||
res = -errno;
|
||||
}
|
||||
close(file);
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* FUSE write method
|
||||
*/
|
||||
static int cowfs_write(const char *path, const char *buf, size_t size,
|
||||
off_t offset, struct fuse_file_info *fi)
|
||||
{
|
||||
int file, fd, res;
|
||||
|
||||
rel(&path);
|
||||
|
||||
fd = copy(path);
|
||||
if (fd < 0)
|
||||
{
|
||||
return -errno;
|
||||
}
|
||||
file = openat(fd, path, O_WRONLY);
|
||||
if (file < 0)
|
||||
{
|
||||
return -errno;
|
||||
}
|
||||
res = pwrite(file, buf, size, offset);
|
||||
if (res < 0)
|
||||
{
|
||||
res = -errno;
|
||||
}
|
||||
close(file);
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* FUSE statfs method
|
||||
*/
|
||||
static int cowfs_statfs(const char *path, struct statvfs *stbuf)
|
||||
{
|
||||
int fd;
|
||||
|
||||
fd = get_rd(path);
|
||||
if (fstatvfs(fd, stbuf) < 0)
|
||||
{
|
||||
return -errno;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* FUSE init method
|
||||
*/
|
||||
static void *cowfs_init(struct fuse_conn_info *conn)
|
||||
{
|
||||
struct fuse_context *ctx;
|
||||
|
||||
ctx = fuse_get_context();
|
||||
|
||||
return ctx->private_data;
|
||||
}
|
||||
|
||||
/**
|
||||
* FUSE method vectors
|
||||
*/
|
||||
static struct fuse_operations cowfs_operations = {
|
||||
.getattr = cowfs_getattr,
|
||||
.access = cowfs_access,
|
||||
.readlink = cowfs_readlink,
|
||||
.readdir = cowfs_readdir,
|
||||
.mknod = cowfs_mknod,
|
||||
.mkdir = cowfs_mkdir,
|
||||
.symlink = cowfs_symlink,
|
||||
.unlink = cowfs_unlink,
|
||||
.rmdir = cowfs_rmdir,
|
||||
.rename = cowfs_rename,
|
||||
.link = cowfs_link,
|
||||
.chmod = cowfs_chmod,
|
||||
.chown = cowfs_chown,
|
||||
.truncate = cowfs_truncate,
|
||||
.utimens = cowfs_utimens,
|
||||
.open = cowfs_open,
|
||||
.read = cowfs_read,
|
||||
.write = cowfs_write,
|
||||
.statfs = cowfs_statfs,
|
||||
.init = cowfs_init,
|
||||
};
|
||||
|
||||
METHOD(cowfs_t, add_overlay, bool,
|
||||
private_cowfs_t *this, char *path)
|
||||
{
|
||||
overlay_t *over = malloc_thing(overlay_t);
|
||||
over->fd = open(path, O_RDONLY | O_DIRECTORY);
|
||||
if (over->fd < 0)
|
||||
{
|
||||
DBG1(DBG_LIB, "failed to open overlay directory '%s': %m", path);
|
||||
free(over);
|
||||
return FALSE;
|
||||
}
|
||||
over->path = realpath(path, NULL);
|
||||
this->lock->write_lock(this->lock);
|
||||
overlay_remove(this, over->path);
|
||||
this->overlays->insert_first(this->overlays, over);
|
||||
this->lock->unlock(this->lock);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
METHOD(cowfs_t, del_overlay, bool,
|
||||
private_cowfs_t *this, char *path)
|
||||
{
|
||||
bool removed;
|
||||
char real[PATH_MAX];
|
||||
this->lock->write_lock(this->lock);
|
||||
removed = overlay_remove(this, realpath(path, real));
|
||||
this->lock->unlock(this->lock);
|
||||
return removed;
|
||||
}
|
||||
|
||||
METHOD(cowfs_t, pop_overlay, bool,
|
||||
private_cowfs_t *this)
|
||||
{
|
||||
overlay_t *over;
|
||||
this->lock->write_lock(this->lock);
|
||||
if (this->overlays->remove_first(this->overlays, (void**)&over) != SUCCESS)
|
||||
{
|
||||
this->lock->unlock(this->lock);
|
||||
return FALSE;
|
||||
}
|
||||
this->lock->unlock(this->lock);
|
||||
overlay_destroy(over);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
METHOD(cowfs_t, destroy, void,
|
||||
private_cowfs_t *this)
|
||||
{
|
||||
fuse_exit(this->fuse);
|
||||
fuse_unmount(this->mount, this->chan);
|
||||
this->thread->join(this->thread);
|
||||
fuse_destroy(this->fuse);
|
||||
this->lock->destroy(this->lock);
|
||||
this->overlays->destroy_function(this->overlays, (void*)overlay_destroy);
|
||||
free(this->mount);
|
||||
free(this->master);
|
||||
free(this->host);
|
||||
close(this->master_fd);
|
||||
close(this->host_fd);
|
||||
free(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* creates a new cowfs fuse instance
|
||||
*/
|
||||
cowfs_t *cowfs_create(char *master, char *host, char *mount)
|
||||
{
|
||||
struct fuse_args args = {0, NULL, 0};
|
||||
private_cowfs_t *this;
|
||||
|
||||
INIT(this,
|
||||
.public = {
|
||||
.add_overlay = _add_overlay,
|
||||
.del_overlay = _del_overlay,
|
||||
.pop_overlay = _pop_overlay,
|
||||
.destroy = _destroy,
|
||||
}
|
||||
);
|
||||
|
||||
this->master_fd = open(master, O_RDONLY | O_DIRECTORY);
|
||||
if (this->master_fd < 0)
|
||||
{
|
||||
DBG1(DBG_LIB, "failed to open master filesystem '%s'", master);
|
||||
free(this);
|
||||
return NULL;
|
||||
}
|
||||
this->host_fd = open(host, O_RDONLY | O_DIRECTORY);
|
||||
if (this->host_fd < 0)
|
||||
{
|
||||
DBG1(DBG_LIB, "failed to open host filesystem '%s'", host);
|
||||
close(this->master_fd);
|
||||
free(this);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
this->chan = fuse_mount(mount, &args);
|
||||
if (this->chan == NULL)
|
||||
{
|
||||
DBG1(DBG_LIB, "mounting cowfs FUSE on '%s' failed", mount);
|
||||
close(this->master_fd);
|
||||
close(this->host_fd);
|
||||
free(this);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
this->fuse = fuse_new(this->chan, &args, &cowfs_operations,
|
||||
sizeof(cowfs_operations), this);
|
||||
if (this->fuse == NULL)
|
||||
{
|
||||
DBG1(DBG_LIB, "creating cowfs FUSE handle failed");
|
||||
close(this->master_fd);
|
||||
close(this->host_fd);
|
||||
fuse_unmount(mount, this->chan);
|
||||
free(this);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
this->mount = strdup(mount);
|
||||
this->master = strdup(master);
|
||||
this->host = strdup(host);
|
||||
this->overlays = linked_list_create();
|
||||
this->lock = rwlock_create(RWLOCK_TYPE_DEFAULT);
|
||||
|
||||
this->thread = thread_create((thread_main_t)fuse_loop, this->fuse);
|
||||
if (!this->thread)
|
||||
{
|
||||
DBG1(DBG_LIB, "creating thread to handle FUSE failed");
|
||||
fuse_unmount(mount, this->chan);
|
||||
this->lock->destroy(this->lock);
|
||||
this->overlays->destroy(this->overlays);
|
||||
free(this->mount);
|
||||
free(this->master);
|
||||
free(this->host);
|
||||
close(this->master_fd);
|
||||
close(this->host_fd);
|
||||
free(this);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return &this->public;
|
||||
}
|
||||
|
||||
@@ -1,72 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 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.
|
||||
*/
|
||||
|
||||
#ifndef COWFS_H
|
||||
#define COWFS_H
|
||||
|
||||
#include <library.h>
|
||||
|
||||
typedef struct cowfs_t cowfs_t;
|
||||
|
||||
/**
|
||||
* cowfs - Copy on write FUSE filesystem.
|
||||
*
|
||||
*/
|
||||
struct cowfs_t {
|
||||
|
||||
/**
|
||||
* Adds an additional copy on write overlay.
|
||||
*
|
||||
* If the path was already added as overlay, it is moved to the top.
|
||||
*
|
||||
* @param path path of the overlay
|
||||
* @return FALSE, if failed
|
||||
*/
|
||||
bool (*add_overlay)(cowfs_t *this, char *path);
|
||||
|
||||
/**
|
||||
* Remove the specified copy on write overlay.
|
||||
*
|
||||
* @param path path of the overlay
|
||||
* @return FALSE, if not found
|
||||
*/
|
||||
bool (*del_overlay)(cowfs_t *this, char *path);
|
||||
|
||||
/**
|
||||
* Remove the most recently added copy on write overlay.
|
||||
*
|
||||
* @return FALSE, if no overlay was found
|
||||
*/
|
||||
bool (*pop_overlay)(cowfs_t *this);
|
||||
|
||||
/**
|
||||
* Stop, umount and destroy a cowfs FUSE filesystem.
|
||||
*/
|
||||
void (*destroy) (cowfs_t *this);
|
||||
};
|
||||
|
||||
/**
|
||||
* Mount a cowfs FUSE filesystem.
|
||||
*
|
||||
* @param master read only master file system directory
|
||||
* @param host copy on write host directory
|
||||
* @param mount mountpoint where union is mounted
|
||||
* @return instance, or NULL if FUSE initialization failed
|
||||
*/
|
||||
cowfs_t *cowfs_create(char *master, char *host, char *mount);
|
||||
|
||||
#endif /* COWFS_H */
|
||||
|
||||
-444
@@ -1,444 +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 <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <utils/debug.h>
|
||||
#include <collections/linked_list.h>
|
||||
|
||||
#include "dumm.h"
|
||||
|
||||
#define PERME (S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH)
|
||||
#define GUEST_DIR "guests"
|
||||
#define TEMPLATE_DIR "templates"
|
||||
|
||||
typedef struct private_dumm_t private_dumm_t;
|
||||
|
||||
struct private_dumm_t {
|
||||
/** public dumm interface */
|
||||
dumm_t public;
|
||||
/** working dir */
|
||||
char *dir;
|
||||
/** directory of guests */
|
||||
char *guest_dir;
|
||||
/** directory of loaded template */
|
||||
char *template;
|
||||
/** list of managed guests */
|
||||
linked_list_t *guests;
|
||||
/** list of managed bridges */
|
||||
linked_list_t *bridges;
|
||||
};
|
||||
|
||||
METHOD(dumm_t, create_guest, guest_t*,
|
||||
private_dumm_t *this, char *name, char *kernel, char *master, char *args)
|
||||
{
|
||||
guest_t *guest;
|
||||
|
||||
guest = guest_create(this->guest_dir, name, kernel, master, args);
|
||||
if (guest)
|
||||
{
|
||||
this->guests->insert_last(this->guests, guest);
|
||||
}
|
||||
return guest;
|
||||
}
|
||||
|
||||
METHOD(dumm_t, create_guest_enumerator, enumerator_t*,
|
||||
private_dumm_t *this)
|
||||
{
|
||||
return this->guests->create_enumerator(this->guests);
|
||||
}
|
||||
|
||||
METHOD(dumm_t, delete_guest, void,
|
||||
private_dumm_t *this, guest_t *guest)
|
||||
{
|
||||
if (this->guests->remove(this->guests, guest, NULL))
|
||||
{
|
||||
char buf[512];
|
||||
int len;
|
||||
|
||||
len = snprintf(buf, sizeof(buf), "rm -Rf %s/%s",
|
||||
this->guest_dir, guest->get_name(guest));
|
||||
guest->destroy(guest);
|
||||
if (len > 8 && len < 512)
|
||||
{
|
||||
ignore_result(system(buf));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
METHOD(dumm_t, create_bridge, bridge_t*,
|
||||
private_dumm_t *this, char *name)
|
||||
{
|
||||
bridge_t *bridge;
|
||||
|
||||
bridge = bridge_create(name);
|
||||
if (bridge)
|
||||
{
|
||||
this->bridges->insert_last(this->bridges, bridge);
|
||||
}
|
||||
return bridge;
|
||||
}
|
||||
|
||||
METHOD(dumm_t, create_bridge_enumerator, enumerator_t*,
|
||||
private_dumm_t *this)
|
||||
{
|
||||
return this->bridges->create_enumerator(this->bridges);
|
||||
}
|
||||
|
||||
METHOD(dumm_t, delete_bridge, void,
|
||||
private_dumm_t *this, bridge_t *bridge)
|
||||
{
|
||||
if (this->bridges->remove(this->bridges, bridge, NULL))
|
||||
{
|
||||
bridge->destroy(bridge);
|
||||
}
|
||||
}
|
||||
|
||||
METHOD(dumm_t, add_overlay, bool,
|
||||
private_dumm_t *this, char *dir)
|
||||
{
|
||||
enumerator_t *enumerator;
|
||||
guest_t *guest;
|
||||
|
||||
if (dir == NULL)
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
if (strlen(dir) > PATH_MAX)
|
||||
{
|
||||
DBG1(DBG_LIB, "overlay directory string '%s' is too long", dir);
|
||||
return FALSE;
|
||||
}
|
||||
if (access(dir, F_OK) != 0)
|
||||
{
|
||||
if (!mkdir_p(dir, PERME))
|
||||
{
|
||||
DBG1(DBG_LIB, "creating overlay directory '%s' failed: %m", dir);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
enumerator = this->guests->create_enumerator(this->guests);
|
||||
while (enumerator->enumerate(enumerator, (void**)&guest))
|
||||
{
|
||||
char guest_dir[PATH_MAX];
|
||||
int len = snprintf(guest_dir, sizeof(guest_dir), "%s/%s", dir,
|
||||
guest->get_name(guest));
|
||||
if (len < 0 || len >= sizeof(guest_dir))
|
||||
{
|
||||
goto error;
|
||||
}
|
||||
if (access(guest_dir, F_OK) != 0)
|
||||
{
|
||||
if (!mkdir_p(guest_dir, PERME))
|
||||
{
|
||||
DBG1(DBG_LIB, "creating overlay directory for guest '%s' failed: %m",
|
||||
guest->get_name(guest));
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
if (!guest->add_overlay(guest, guest_dir))
|
||||
{
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
enumerator->destroy(enumerator);
|
||||
return TRUE;
|
||||
error:
|
||||
enumerator->destroy(enumerator);
|
||||
this->public.del_overlay(&this->public, dir);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
METHOD(dumm_t, del_overlay, bool,
|
||||
private_dumm_t *this, char *dir)
|
||||
{
|
||||
bool ret = FALSE;
|
||||
enumerator_t *enumerator;
|
||||
guest_t *guest;
|
||||
|
||||
enumerator = this->guests->create_enumerator(this->guests);
|
||||
while (enumerator->enumerate(enumerator, (void**)&guest))
|
||||
{
|
||||
char guest_dir[PATH_MAX];
|
||||
int len = snprintf(guest_dir, sizeof(guest_dir), "%s/%s", dir,
|
||||
guest->get_name(guest));
|
||||
if (len < 0 || len >= sizeof(guest_dir))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
ret = guest->del_overlay(guest, guest_dir) || ret;
|
||||
}
|
||||
enumerator->destroy(enumerator);
|
||||
return ret;
|
||||
}
|
||||
|
||||
METHOD(dumm_t, pop_overlay, bool,
|
||||
private_dumm_t *this)
|
||||
{
|
||||
bool ret = FALSE;
|
||||
enumerator_t *enumerator;
|
||||
guest_t *guest;
|
||||
|
||||
enumerator = this->guests->create_enumerator(this->guests);
|
||||
while (enumerator->enumerate(enumerator, (void**)&guest))
|
||||
{
|
||||
ret = guest->pop_overlay(guest) || ret;
|
||||
}
|
||||
enumerator->destroy(enumerator);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* disable the currently enabled template
|
||||
*/
|
||||
static void clear_template(private_dumm_t *this)
|
||||
{
|
||||
if (this->template)
|
||||
{
|
||||
del_overlay(this, this->template);
|
||||
free(this->template);
|
||||
this->template = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
METHOD(dumm_t, load_template, bool,
|
||||
private_dumm_t *this, char *name)
|
||||
{
|
||||
clear_template(this);
|
||||
if (name == NULL)
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
if (strlen(name) > PATH_MAX)
|
||||
{
|
||||
DBG1(DBG_LIB, "template name '%s' is too long", name);
|
||||
return FALSE;
|
||||
}
|
||||
if (strchr(name, '/') != NULL)
|
||||
{
|
||||
DBG1(DBG_LIB, "template name '%s' must not contain '/' characters", name);
|
||||
return FALSE;
|
||||
}
|
||||
if (asprintf(&this->template, "%s/%s", TEMPLATE_DIR, name) < 0)
|
||||
{
|
||||
this->template = NULL;
|
||||
return FALSE;
|
||||
}
|
||||
if (access(this->template, F_OK) != 0)
|
||||
{
|
||||
if (!mkdir_p(this->template, PERME))
|
||||
{
|
||||
DBG1(DBG_LIB, "creating template directory '%s' failed: %m",
|
||||
this->template);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
return add_overlay(this, this->template);
|
||||
}
|
||||
|
||||
/**
|
||||
* Template directory enumerator
|
||||
*/
|
||||
typedef struct {
|
||||
/** implements enumerator_t */
|
||||
enumerator_t public;
|
||||
/** directory enumerator */
|
||||
enumerator_t *inner;
|
||||
} template_enumerator_t;
|
||||
|
||||
METHOD(enumerator_t, template_enumerate, bool,
|
||||
template_enumerator_t *this, va_list args)
|
||||
{
|
||||
struct stat st;
|
||||
char *rel, **template;
|
||||
|
||||
VA_ARGS_VGET(args, template);
|
||||
|
||||
while (this->inner->enumerate(this->inner, &rel, NULL, &st))
|
||||
{
|
||||
if (S_ISDIR(st.st_mode) && *rel != '.')
|
||||
{
|
||||
*template = rel;
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
METHOD(enumerator_t, template_enumerator_destroy, void,
|
||||
template_enumerator_t *this)
|
||||
{
|
||||
this->inner->destroy(this->inner);
|
||||
free(this);
|
||||
}
|
||||
|
||||
METHOD(dumm_t, create_template_enumerator, enumerator_t*,
|
||||
private_dumm_t *this)
|
||||
{
|
||||
template_enumerator_t *enumerator;
|
||||
INIT(enumerator,
|
||||
.public = {
|
||||
.enumerate = enumerator_enumerate_default,
|
||||
.venumerate = _template_enumerate,
|
||||
.destroy = (void*)_template_enumerator_destroy,
|
||||
},
|
||||
.inner = enumerator_create_directory(TEMPLATE_DIR),
|
||||
);
|
||||
if (!enumerator->inner)
|
||||
{
|
||||
free(enumerator);
|
||||
return enumerator_create_empty();
|
||||
}
|
||||
return &enumerator->public;
|
||||
}
|
||||
|
||||
METHOD(dumm_t, destroy, void,
|
||||
private_dumm_t *this)
|
||||
{
|
||||
enumerator_t *enumerator;
|
||||
guest_t *guest;
|
||||
|
||||
this->bridges->destroy_offset(this->bridges, offsetof(bridge_t, destroy));
|
||||
|
||||
enumerator = this->guests->create_enumerator(this->guests);
|
||||
while (enumerator->enumerate(enumerator, (void**)&guest))
|
||||
{
|
||||
guest->stop(guest, NULL);
|
||||
}
|
||||
enumerator->destroy(enumerator);
|
||||
|
||||
while (this->guests->remove_last(this->guests, (void**)&guest) == SUCCESS)
|
||||
{
|
||||
guest->destroy(guest);
|
||||
}
|
||||
this->guests->destroy(this->guests);
|
||||
free(this->guest_dir);
|
||||
free(this->template);
|
||||
free(this->dir);
|
||||
free(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* load all guests in our working dir
|
||||
*/
|
||||
static void load_guests(private_dumm_t *this)
|
||||
{
|
||||
DIR *dir;
|
||||
struct dirent *ent;
|
||||
guest_t *guest;
|
||||
|
||||
dir = opendir(this->guest_dir);
|
||||
if (dir == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
while ((ent = readdir(dir)))
|
||||
{
|
||||
if (*ent->d_name == '.')
|
||||
{ /* skip ".", ".." and hidden files (such as ".svn") */
|
||||
continue;
|
||||
}
|
||||
guest = guest_load(this->guest_dir, ent->d_name);
|
||||
if (guest)
|
||||
{
|
||||
this->guests->insert_last(this->guests, guest);
|
||||
}
|
||||
else
|
||||
{
|
||||
DBG1(DBG_LIB, "loading guest in directory '%s' failed, skipped",
|
||||
ent->d_name);
|
||||
}
|
||||
}
|
||||
closedir(dir);
|
||||
}
|
||||
|
||||
/**
|
||||
* create a dumm instance
|
||||
*/
|
||||
dumm_t *dumm_create(char *dir)
|
||||
{
|
||||
char cwd[PATH_MAX];
|
||||
private_dumm_t *this;
|
||||
|
||||
INIT(this,
|
||||
.public = {
|
||||
.create_guest = _create_guest,
|
||||
.create_guest_enumerator = _create_guest_enumerator,
|
||||
.delete_guest = _delete_guest,
|
||||
.create_bridge = _create_bridge,
|
||||
.create_bridge_enumerator = _create_bridge_enumerator,
|
||||
.delete_bridge = _delete_bridge,
|
||||
.add_overlay = _add_overlay,
|
||||
.del_overlay = _del_overlay,
|
||||
.pop_overlay = _pop_overlay,
|
||||
.load_template = _load_template,
|
||||
.create_template_enumerator = _create_template_enumerator,
|
||||
.destroy = _destroy,
|
||||
},
|
||||
);
|
||||
|
||||
if (dir && *dir == '/')
|
||||
{
|
||||
this->dir = strdup(dir);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (getcwd(cwd, sizeof(cwd)) == NULL)
|
||||
{
|
||||
free(this);
|
||||
return NULL;
|
||||
}
|
||||
if (dir)
|
||||
{
|
||||
if (asprintf(&this->dir, "%s/%s", cwd, dir) < 0)
|
||||
{
|
||||
this->dir = NULL;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
this->dir = strdup(cwd);
|
||||
}
|
||||
}
|
||||
if (asprintf(&this->guest_dir, "%s/%s", this->dir, GUEST_DIR) < 0)
|
||||
{
|
||||
this->guest_dir = NULL;
|
||||
}
|
||||
|
||||
this->guests = linked_list_create();
|
||||
this->bridges = linked_list_create();
|
||||
|
||||
if (this->dir == NULL || this->guest_dir == NULL ||
|
||||
(mkdir(this->guest_dir, PERME) < 0 && errno != EEXIST))
|
||||
{
|
||||
DBG1(DBG_LIB, "creating guest directory '%s' failed: %m",
|
||||
this->guest_dir);
|
||||
destroy(this);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
load_guests(this);
|
||||
return &this->public;
|
||||
}
|
||||
|
||||
-150
@@ -1,150 +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.
|
||||
*/
|
||||
|
||||
#ifndef DUMM_H
|
||||
#define DUMM_H
|
||||
|
||||
#include <signal.h>
|
||||
|
||||
#include <library.h>
|
||||
#include <collections/enumerator.h>
|
||||
|
||||
#include "guest.h"
|
||||
#include "bridge.h"
|
||||
|
||||
typedef struct dumm_t dumm_t;
|
||||
|
||||
/**
|
||||
* dumm - Dynamic Uml Mesh Modeler
|
||||
*
|
||||
* Controls a group of UML guests and their networks.
|
||||
*/
|
||||
struct dumm_t {
|
||||
|
||||
/**
|
||||
* Starts a new UML guest
|
||||
*
|
||||
* @param name name of the guest
|
||||
* @param kernel UML kernel to use for guest
|
||||
* @param master mounted read only master filesystem
|
||||
* @param args additional args to pass to kernel
|
||||
* @return guest if started, NULL if failed
|
||||
*/
|
||||
guest_t* (*create_guest) (dumm_t *this, char *name, char *kernel,
|
||||
char *master, char *args);
|
||||
|
||||
/**
|
||||
* Create an enumerator over all guests.
|
||||
*
|
||||
* @return enumerator over guest_t's
|
||||
*/
|
||||
enumerator_t* (*create_guest_enumerator) (dumm_t *this);
|
||||
|
||||
/**
|
||||
* Delete a guest from disk.
|
||||
*
|
||||
* @param guest guest to destroy
|
||||
*/
|
||||
void (*delete_guest) (dumm_t *this, guest_t *guest);
|
||||
|
||||
/**
|
||||
* Create a new bridge.
|
||||
*
|
||||
* @param name name of the bridge to create
|
||||
* @return created bridge
|
||||
*/
|
||||
bridge_t* (*create_bridge)(dumm_t *this, char *name);
|
||||
|
||||
/**
|
||||
* Create an enumerator over all bridges.
|
||||
*
|
||||
* @return enumerator over bridge_t's
|
||||
*/
|
||||
enumerator_t* (*create_bridge_enumerator)(dumm_t *this);
|
||||
|
||||
/**
|
||||
* Delete a bridge.
|
||||
*
|
||||
* @param bridge bridge to destroy
|
||||
*/
|
||||
void (*delete_bridge) (dumm_t *this, bridge_t *bridge);
|
||||
|
||||
/**
|
||||
* Add an overlay to all guests.
|
||||
*
|
||||
* Directories named after the guests are created, if they do not exist
|
||||
* in the given overlay directory.
|
||||
*
|
||||
* If adding the overlay on at lest one guest fails, FALSE is returned and
|
||||
* the overlay is again removed from all guests.
|
||||
*
|
||||
* @param dir dir to the overlay
|
||||
* @return FALSE, on failure
|
||||
*/
|
||||
bool (*add_overlay)(dumm_t *this, char *dir);
|
||||
|
||||
/**
|
||||
* Removes an overlay from all guests.
|
||||
*
|
||||
* @param dir dir to the overlay
|
||||
* @return FALSE, if the overlay was not found on any guest
|
||||
*/
|
||||
bool (*del_overlay)(dumm_t *this, char *dir);
|
||||
|
||||
/**
|
||||
* Remove the latest overlay from all guests.
|
||||
*
|
||||
* @return FALSE, if no overlay was found on any guest
|
||||
*/
|
||||
bool (*pop_overlay)(dumm_t *this);
|
||||
|
||||
/**
|
||||
* Loads a template, create a new one if it does not exist.
|
||||
*
|
||||
* This is basically a wrapper around add/del_overlay to simplify working
|
||||
* with overlays. Templates are located in a predefined directory, so that
|
||||
* only a name for the template has to be specified here. Only one template
|
||||
* can be loaded at any one time (but other overlays can be added on top or
|
||||
* below a template).
|
||||
*
|
||||
* @param name name of the template to load, NULL to unload
|
||||
* @return FALSE if load/create failed
|
||||
*/
|
||||
bool (*load_template)(dumm_t *this, char *name);
|
||||
|
||||
/**
|
||||
* Create an enumerator over all available templates.
|
||||
*
|
||||
* @return enumerator over char*
|
||||
*/
|
||||
enumerator_t* (*create_template_enumerator)(dumm_t *this);
|
||||
|
||||
/**
|
||||
* stop all guests and destroy the modeler
|
||||
*/
|
||||
void (*destroy) (dumm_t *this);
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a group of UML hosts and networks.
|
||||
*
|
||||
* @param dir directory to create guests/load from, NULL for cwd
|
||||
* @return created UML group, or NULL if failed.
|
||||
*/
|
||||
dumm_t *dumm_create(char *dir);
|
||||
|
||||
#endif /* DUMM_H */
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
dumm.so
|
||||
mkmf.log
|
||||
extconf.rb
|
||||
@@ -1,8 +0,0 @@
|
||||
DUMM Ruby Extension
|
||||
===================
|
||||
|
||||
Build and Install
|
||||
|
||||
$ ruby extconf.rb
|
||||
$ make
|
||||
# make install
|
||||
@@ -1,797 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2008-2010 Tobias Brunner
|
||||
* Copyright (C) 2008 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.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <signal.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include <library.h>
|
||||
#include <dumm.h>
|
||||
#include <utils/debug.h>
|
||||
#include <collections/linked_list.h>
|
||||
|
||||
#undef PACKAGE_NAME
|
||||
#undef PACKAGE_TARNAME
|
||||
#undef PACKAGE_VERSION
|
||||
#undef PACKAGE_STRING
|
||||
#undef PACKAGE_BUGREPORT
|
||||
#undef PACKAGE_URL
|
||||
#undef HAVE_DLADDR
|
||||
#undef HAVE_QSORT_R
|
||||
/* avoid redefintiion of snprintf etc. */
|
||||
#define RUBY_DONT_SUBST
|
||||
/* undef our _GNU_SOURCE, as it gets redefined by <ruby.h> */
|
||||
#undef _GNU_SOURCE
|
||||
#include <ruby.h>
|
||||
|
||||
static dumm_t *dumm;
|
||||
|
||||
static VALUE rbm_dumm;
|
||||
static VALUE rbc_guest;
|
||||
static VALUE rbc_bridge;
|
||||
static VALUE rbc_iface;
|
||||
static VALUE rbc_template;
|
||||
|
||||
/**
|
||||
* Guest invocation callback
|
||||
*/
|
||||
static pid_t invoke(void *null, guest_t *guest, char *args[], int argc)
|
||||
{
|
||||
pid_t pid;
|
||||
|
||||
pid = fork();
|
||||
switch (pid)
|
||||
{
|
||||
case 0: /* child */
|
||||
/* create a new process group in order to prevent signals (e.g.
|
||||
* SIGINT) sent to the parent from terminating the child */
|
||||
setpgid(0, 0);
|
||||
dup2(open("/dev/null", 0), 1);
|
||||
dup2(open("/dev/null", 0), 2);
|
||||
execvp(args[0], args);
|
||||
/* FALL */
|
||||
case -1:
|
||||
return 0;
|
||||
default:
|
||||
return pid;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* SIGCHLD signal handler
|
||||
*/
|
||||
static void sigchld_handler(int signal, siginfo_t *info, void* ptr)
|
||||
{
|
||||
enumerator_t *enumerator;
|
||||
guest_t *guest;
|
||||
|
||||
enumerator = dumm->create_guest_enumerator(dumm);
|
||||
while (enumerator->enumerate(enumerator, &guest))
|
||||
{
|
||||
if (guest->get_pid(guest) == info->si_pid)
|
||||
{
|
||||
guest->sigchild(guest);
|
||||
break;
|
||||
}
|
||||
}
|
||||
enumerator->destroy(enumerator);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Global Dumm bindings
|
||||
*/
|
||||
static VALUE dumm_add_overlay(VALUE class, VALUE dir)
|
||||
{
|
||||
if (!dumm->add_overlay(dumm, StringValuePtr(dir)))
|
||||
{
|
||||
rb_raise(rb_eRuntimeError, "loading overlay failed");
|
||||
}
|
||||
return class;
|
||||
}
|
||||
|
||||
static VALUE dumm_del_overlay(VALUE class, VALUE dir)
|
||||
{
|
||||
return dumm->del_overlay(dumm, StringValuePtr(dir)) ? Qtrue : Qfalse;
|
||||
}
|
||||
|
||||
static VALUE dumm_pop_overlay(VALUE class)
|
||||
{
|
||||
return dumm->pop_overlay(dumm) ? Qtrue : Qfalse;
|
||||
}
|
||||
|
||||
static void dumm_init()
|
||||
{
|
||||
rbm_dumm = rb_define_module("Dumm");
|
||||
|
||||
rb_define_module_function(rbm_dumm, "add_overlay", dumm_add_overlay, 1);
|
||||
rb_define_module_function(rbm_dumm, "del_overlay", dumm_del_overlay, 1);
|
||||
rb_define_module_function(rbm_dumm, "pop_overlay", dumm_pop_overlay, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Guest bindings
|
||||
*/
|
||||
static VALUE guest_hash_create(VALUE class)
|
||||
{
|
||||
enumerator_t *enumerator;
|
||||
guest_t *guest;
|
||||
VALUE hash = rb_hash_new();
|
||||
enumerator = dumm->create_guest_enumerator(dumm);
|
||||
while (enumerator->enumerate(enumerator, &guest))
|
||||
{
|
||||
rb_hash_aset(hash, rb_str_new2(guest->get_name(guest)),
|
||||
Data_Wrap_Struct(class, NULL, NULL, guest));
|
||||
}
|
||||
enumerator->destroy(enumerator);
|
||||
return hash;
|
||||
}
|
||||
|
||||
static VALUE guest_hash(VALUE class)
|
||||
{
|
||||
ID id = rb_intern("@@guests");
|
||||
if (!rb_cvar_defined(class, id))
|
||||
{
|
||||
VALUE hash = guest_hash_create(class);
|
||||
#ifdef RB_CVAR_SET_4_ARGS
|
||||
rb_cvar_set(class, id, hash, 0);
|
||||
#else
|
||||
rb_cvar_set(class, id, hash);
|
||||
#endif
|
||||
return hash;
|
||||
}
|
||||
return rb_cvar_get(class, id);
|
||||
}
|
||||
|
||||
static VALUE guest_find(VALUE class, VALUE key)
|
||||
{
|
||||
if (TYPE(key) != T_STRING)
|
||||
{
|
||||
key = rb_convert_type(key, T_STRING, "String", "to_s");
|
||||
}
|
||||
return rb_hash_aref(guest_hash(class), key);
|
||||
}
|
||||
|
||||
static VALUE guest_get(VALUE class, VALUE key)
|
||||
{
|
||||
return guest_find(class, key);
|
||||
}
|
||||
|
||||
static VALUE guest_each(int argc, VALUE *argv, VALUE class)
|
||||
{
|
||||
if (!rb_block_given_p())
|
||||
{
|
||||
rb_raise(rb_eArgError, "must be called with a block");
|
||||
}
|
||||
rb_block_call(guest_hash(class), rb_intern("each_value"), 0, 0,
|
||||
rb_yield, 0);
|
||||
return class;
|
||||
}
|
||||
|
||||
static VALUE guest_new(VALUE class, VALUE name, VALUE kernel,
|
||||
VALUE master, VALUE args)
|
||||
{
|
||||
VALUE self;
|
||||
guest_t *guest;
|
||||
guest = dumm->create_guest(dumm, StringValuePtr(name),
|
||||
StringValuePtr(kernel), StringValuePtr(master),
|
||||
StringValuePtr(args));
|
||||
if (!guest)
|
||||
{
|
||||
rb_raise(rb_eRuntimeError, "creating guest failed");
|
||||
}
|
||||
self = Data_Wrap_Struct(class, NULL, NULL, guest);
|
||||
rb_hash_aset(guest_hash(class), name, self);
|
||||
return self;
|
||||
}
|
||||
|
||||
static VALUE guest_to_s(VALUE self)
|
||||
{
|
||||
guest_t *guest;
|
||||
|
||||
Data_Get_Struct(self, guest_t, guest);
|
||||
return rb_str_new2(guest->get_name(guest));
|
||||
}
|
||||
|
||||
static VALUE guest_start(VALUE self)
|
||||
{
|
||||
guest_t *guest;
|
||||
|
||||
Data_Get_Struct(self, guest_t, guest);
|
||||
|
||||
if (!guest->start(guest, invoke, NULL, NULL))
|
||||
{
|
||||
rb_raise(rb_eRuntimeError, "starting guest failed");
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
static VALUE guest_stop(VALUE self)
|
||||
{
|
||||
guest_t *guest;
|
||||
|
||||
Data_Get_Struct(self, guest_t, guest);
|
||||
guest->stop(guest, NULL);
|
||||
return self;
|
||||
}
|
||||
|
||||
static VALUE guest_running(VALUE self)
|
||||
{
|
||||
guest_t *guest;
|
||||
|
||||
Data_Get_Struct(self, guest_t, guest);
|
||||
return guest->get_pid(guest) ? Qtrue : Qfalse;
|
||||
}
|
||||
|
||||
static void exec_cb(void *data, char *buf)
|
||||
{
|
||||
rb_yield(rb_str_new2(buf));
|
||||
}
|
||||
|
||||
static VALUE guest_exec(VALUE self, VALUE cmd)
|
||||
{
|
||||
guest_t *guest;
|
||||
bool block;
|
||||
int ret;
|
||||
|
||||
block = rb_block_given_p();
|
||||
Data_Get_Struct(self, guest_t, guest);
|
||||
ret = guest->exec_str(guest, block ? (void*)exec_cb : NULL, TRUE, NULL,
|
||||
"exec %s", StringValuePtr(cmd));
|
||||
rb_iv_set(self, "@execstatus", INT2NUM(ret));
|
||||
return self;
|
||||
}
|
||||
|
||||
static VALUE guest_mconsole(VALUE self, VALUE cmd)
|
||||
{
|
||||
guest_t *guest;
|
||||
bool block;
|
||||
int ret;
|
||||
|
||||
block = rb_block_given_p();
|
||||
Data_Get_Struct(self, guest_t, guest);
|
||||
if ((ret = guest->exec_str(guest, block ? (void*)exec_cb : NULL, TRUE, NULL,
|
||||
"%s", StringValuePtr(cmd))) != 0)
|
||||
{
|
||||
rb_raise(rb_eRuntimeError, "executing command failed (%d)", ret);
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
static VALUE guest_add_iface(VALUE self, VALUE name)
|
||||
{
|
||||
guest_t *guest;
|
||||
iface_t *iface;
|
||||
|
||||
Data_Get_Struct(self, guest_t, guest);
|
||||
iface = guest->create_iface(guest, StringValuePtr(name));
|
||||
if (!iface)
|
||||
{
|
||||
rb_raise(rb_eRuntimeError, "adding interface failed");
|
||||
}
|
||||
return Data_Wrap_Struct(rbc_iface, NULL, NULL, iface);
|
||||
}
|
||||
|
||||
static VALUE guest_find_iface(VALUE self, VALUE key)
|
||||
{
|
||||
enumerator_t *enumerator;
|
||||
iface_t *iface, *found = NULL;
|
||||
guest_t *guest;
|
||||
|
||||
if (TYPE(key) == T_SYMBOL)
|
||||
{
|
||||
key = rb_convert_type(key, T_STRING, "String", "to_s");
|
||||
}
|
||||
Data_Get_Struct(self, guest_t, guest);
|
||||
enumerator = guest->create_iface_enumerator(guest);
|
||||
while (enumerator->enumerate(enumerator, &iface))
|
||||
{
|
||||
if (streq(iface->get_guestif(iface), StringValuePtr(key)))
|
||||
{
|
||||
found = iface;
|
||||
break;
|
||||
}
|
||||
}
|
||||
enumerator->destroy(enumerator);
|
||||
if (!found)
|
||||
{
|
||||
return Qnil;
|
||||
}
|
||||
return Data_Wrap_Struct(rbc_iface, NULL, NULL, iface);
|
||||
}
|
||||
|
||||
static VALUE guest_get_iface(VALUE self, VALUE key)
|
||||
{
|
||||
VALUE iface = guest_find_iface(self, key);
|
||||
if (NIL_P(iface))
|
||||
{
|
||||
rb_raise(rb_eRuntimeError, "interface not found");
|
||||
}
|
||||
return iface;
|
||||
}
|
||||
|
||||
static VALUE guest_each_iface(int argc, VALUE *argv, VALUE self)
|
||||
{
|
||||
enumerator_t *enumerator;
|
||||
linked_list_t *list;
|
||||
guest_t *guest;
|
||||
iface_t *iface;
|
||||
|
||||
if (!rb_block_given_p())
|
||||
{
|
||||
rb_raise(rb_eArgError, "must be called with a block");
|
||||
}
|
||||
Data_Get_Struct(self, guest_t, guest);
|
||||
list = linked_list_create();
|
||||
enumerator = guest->create_iface_enumerator(guest);
|
||||
while (enumerator->enumerate(enumerator, &iface))
|
||||
{
|
||||
list->insert_last(list, iface);
|
||||
}
|
||||
enumerator->destroy(enumerator);
|
||||
while (list->remove_first(list, (void**)&iface) == SUCCESS)
|
||||
{
|
||||
rb_yield(Data_Wrap_Struct(rbc_iface, NULL, NULL, iface));
|
||||
}
|
||||
list->destroy(list);
|
||||
return self;
|
||||
}
|
||||
|
||||
static VALUE guest_delete(VALUE self)
|
||||
{
|
||||
guest_t *guest;
|
||||
|
||||
Data_Get_Struct(self, guest_t, guest);
|
||||
if (guest->get_pid(guest))
|
||||
{
|
||||
rb_raise(rb_eRuntimeError, "guest is running");
|
||||
}
|
||||
dumm->delete_guest(dumm, guest);
|
||||
return Qnil;
|
||||
}
|
||||
|
||||
static VALUE guest_add_overlay(VALUE self, VALUE dir)
|
||||
{
|
||||
guest_t *guest;
|
||||
|
||||
Data_Get_Struct(self, guest_t, guest);
|
||||
if (!guest->add_overlay(guest, StringValuePtr(dir)))
|
||||
{
|
||||
rb_raise(rb_eRuntimeError, "loading overlay failed");
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
static VALUE guest_del_overlay(VALUE self, VALUE dir)
|
||||
{
|
||||
guest_t *guest;
|
||||
|
||||
Data_Get_Struct(self, guest_t, guest);
|
||||
return guest->del_overlay(guest, StringValuePtr(dir)) ? Qtrue : Qfalse;
|
||||
}
|
||||
|
||||
static VALUE guest_pop_overlay(VALUE self)
|
||||
{
|
||||
guest_t *guest;
|
||||
|
||||
Data_Get_Struct(self, guest_t, guest);
|
||||
return guest->pop_overlay(guest) ? Qtrue : Qfalse;
|
||||
}
|
||||
|
||||
static void guest_init()
|
||||
{
|
||||
rbc_guest = rb_define_class_under(rbm_dumm , "Guest", rb_cObject);
|
||||
rb_include_module(rb_class_of(rbc_guest), rb_mEnumerable);
|
||||
rb_include_module(rbc_guest, rb_mEnumerable);
|
||||
|
||||
rb_define_singleton_method(rbc_guest, "[]", guest_get, 1);
|
||||
rb_define_singleton_method(rbc_guest, "each", guest_each, -1);
|
||||
rb_define_singleton_method(rbc_guest, "new", guest_new, 4);
|
||||
rb_define_singleton_method(rbc_guest, "include?", guest_find, 1);
|
||||
rb_define_singleton_method(rbc_guest, "guest?", guest_find, 1);
|
||||
|
||||
rb_define_method(rbc_guest, "to_s", guest_to_s, 0);
|
||||
rb_define_method(rbc_guest, "start", guest_start, 0);
|
||||
rb_define_method(rbc_guest, "stop", guest_stop, 0);
|
||||
rb_define_method(rbc_guest, "running?", guest_running, 0);
|
||||
rb_define_method(rbc_guest, "exec", guest_exec, 1);
|
||||
rb_define_method(rbc_guest, "mconsole", guest_mconsole, 1);
|
||||
rb_define_method(rbc_guest, "add", guest_add_iface, 1);
|
||||
rb_define_method(rbc_guest, "[]", guest_get_iface, 1);
|
||||
rb_define_method(rbc_guest, "each", guest_each_iface, -1);
|
||||
rb_define_method(rbc_guest, "include?", guest_find_iface, 1);
|
||||
rb_define_method(rbc_guest, "iface?", guest_find_iface, 1);
|
||||
rb_define_method(rbc_guest, "delete", guest_delete, 0);
|
||||
rb_define_method(rbc_guest, "add_overlay", guest_add_overlay, 1);
|
||||
rb_define_method(rbc_guest, "del_overlay", guest_del_overlay, 1);
|
||||
rb_define_method(rbc_guest, "pop_overlay", guest_pop_overlay, 0);
|
||||
|
||||
rb_define_attr(rbc_guest, "execstatus", 1, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Bridge binding
|
||||
*/
|
||||
static VALUE bridge_find(VALUE class, VALUE key)
|
||||
{
|
||||
enumerator_t *enumerator;
|
||||
bridge_t *bridge, *found = NULL;
|
||||
|
||||
if (TYPE(key) == T_SYMBOL)
|
||||
{
|
||||
key = rb_convert_type(key, T_STRING, "String", "to_s");
|
||||
}
|
||||
enumerator = dumm->create_bridge_enumerator(dumm);
|
||||
while (enumerator->enumerate(enumerator, &bridge))
|
||||
{
|
||||
if (streq(bridge->get_name(bridge), StringValuePtr(key)))
|
||||
{
|
||||
found = bridge;
|
||||
break;
|
||||
}
|
||||
}
|
||||
enumerator->destroy(enumerator);
|
||||
if (!found)
|
||||
{
|
||||
return Qnil;
|
||||
}
|
||||
return Data_Wrap_Struct(class, NULL, NULL, found);
|
||||
}
|
||||
|
||||
static VALUE bridge_get(VALUE class, VALUE key)
|
||||
{
|
||||
VALUE bridge = bridge_find(class, key);
|
||||
if (NIL_P(bridge))
|
||||
{
|
||||
rb_raise(rb_eRuntimeError, "bridge not found");
|
||||
}
|
||||
return bridge;
|
||||
}
|
||||
|
||||
static VALUE bridge_each(int argc, VALUE *argv, VALUE class)
|
||||
{
|
||||
enumerator_t *enumerator;
|
||||
linked_list_t *list;
|
||||
bridge_t *bridge;
|
||||
|
||||
if (!rb_block_given_p())
|
||||
{
|
||||
rb_raise(rb_eArgError, "must be called with a block");
|
||||
}
|
||||
list = linked_list_create();
|
||||
enumerator = dumm->create_bridge_enumerator(dumm);
|
||||
while (enumerator->enumerate(enumerator, &bridge))
|
||||
{
|
||||
list->insert_last(list, bridge);
|
||||
}
|
||||
enumerator->destroy(enumerator);
|
||||
while (list->remove_first(list, (void**)&bridge) == SUCCESS)
|
||||
{
|
||||
rb_yield(Data_Wrap_Struct(class, NULL, NULL, bridge));
|
||||
}
|
||||
list->destroy(list);
|
||||
return class;
|
||||
}
|
||||
|
||||
static VALUE bridge_new(VALUE class, VALUE name)
|
||||
|
||||
{
|
||||
bridge_t *bridge;
|
||||
|
||||
bridge = dumm->create_bridge(dumm, StringValuePtr(name));
|
||||
if (!bridge)
|
||||
{
|
||||
rb_raise(rb_eRuntimeError, "creating bridge failed");
|
||||
}
|
||||
return Data_Wrap_Struct(class, NULL, NULL, bridge);
|
||||
}
|
||||
|
||||
static VALUE bridge_to_s(VALUE self)
|
||||
{
|
||||
bridge_t *bridge;
|
||||
|
||||
Data_Get_Struct(self, bridge_t, bridge);
|
||||
return rb_str_new2(bridge->get_name(bridge));
|
||||
}
|
||||
|
||||
static VALUE bridge_each_iface(int argc, VALUE *argv, VALUE self)
|
||||
{
|
||||
enumerator_t *enumerator;
|
||||
linked_list_t *list;
|
||||
bridge_t *bridge;
|
||||
iface_t *iface;
|
||||
|
||||
if (!rb_block_given_p())
|
||||
{
|
||||
rb_raise(rb_eArgError, "must be called with a block");
|
||||
}
|
||||
Data_Get_Struct(self, bridge_t, bridge);
|
||||
list = linked_list_create();
|
||||
enumerator = bridge->create_iface_enumerator(bridge);
|
||||
while (enumerator->enumerate(enumerator, &iface))
|
||||
{
|
||||
list->insert_last(list, iface);
|
||||
}
|
||||
enumerator->destroy(enumerator);
|
||||
while (list->remove_first(list, (void**)&iface) == SUCCESS)
|
||||
{
|
||||
rb_yield(Data_Wrap_Struct(rbc_iface, NULL, NULL, iface));
|
||||
}
|
||||
list->destroy(list);
|
||||
return self;
|
||||
}
|
||||
|
||||
static VALUE bridge_delete(VALUE self)
|
||||
{
|
||||
bridge_t *bridge;
|
||||
|
||||
Data_Get_Struct(self, bridge_t, bridge);
|
||||
dumm->delete_bridge(dumm, bridge);
|
||||
return Qnil;
|
||||
}
|
||||
|
||||
static void bridge_init()
|
||||
{
|
||||
rbc_bridge = rb_define_class_under(rbm_dumm , "Bridge", rb_cObject);
|
||||
rb_include_module(rb_class_of(rbc_bridge), rb_mEnumerable);
|
||||
rb_include_module(rbc_bridge, rb_mEnumerable);
|
||||
|
||||
rb_define_singleton_method(rbc_bridge, "[]", bridge_get, 1);
|
||||
rb_define_singleton_method(rbc_bridge, "each", bridge_each, -1);
|
||||
rb_define_singleton_method(rbc_bridge, "new", bridge_new, 1);
|
||||
rb_define_singleton_method(rbc_bridge, "include?", bridge_find, 1);
|
||||
rb_define_singleton_method(rbc_bridge, "bridge?", bridge_find, 1);
|
||||
|
||||
rb_define_method(rbc_bridge, "to_s", bridge_to_s, 0);
|
||||
rb_define_method(rbc_bridge, "each", bridge_each_iface, -1);
|
||||
rb_define_method(rbc_bridge, "delete", bridge_delete, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Iface wrapper
|
||||
*/
|
||||
static VALUE iface_to_s(VALUE self)
|
||||
{
|
||||
iface_t *iface;
|
||||
|
||||
Data_Get_Struct(self, iface_t, iface);
|
||||
return rb_str_new2(iface->get_hostif(iface));
|
||||
}
|
||||
|
||||
static VALUE iface_connect(VALUE self, VALUE vbridge)
|
||||
{
|
||||
iface_t *iface;
|
||||
bridge_t *bridge;
|
||||
|
||||
Data_Get_Struct(self, iface_t, iface);
|
||||
Data_Get_Struct(vbridge, bridge_t, bridge);
|
||||
if (!bridge->connect_iface(bridge, iface))
|
||||
{
|
||||
rb_raise(rb_eRuntimeError, "connecting iface failed");
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
static VALUE iface_disconnect(VALUE self)
|
||||
{
|
||||
iface_t *iface;
|
||||
bridge_t *bridge;
|
||||
|
||||
Data_Get_Struct(self, iface_t, iface);
|
||||
bridge = iface->get_bridge(iface);
|
||||
if (!bridge || !bridge->disconnect_iface(bridge, iface))
|
||||
{
|
||||
rb_raise(rb_eRuntimeError, "disconnecting iface failed");
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
static VALUE iface_add_addr(VALUE self, VALUE name)
|
||||
{
|
||||
iface_t *iface;
|
||||
host_t *addr;
|
||||
int bits;
|
||||
|
||||
addr = host_create_from_subnet(StringValuePtr(name), &bits);
|
||||
if (!addr)
|
||||
{
|
||||
rb_raise(rb_eArgError, "invalid IP address");
|
||||
}
|
||||
Data_Get_Struct(self, iface_t, iface);
|
||||
if (!iface->add_address(iface, addr, bits))
|
||||
{
|
||||
addr->destroy(addr);
|
||||
rb_raise(rb_eRuntimeError, "adding address failed");
|
||||
}
|
||||
if (rb_block_given_p()) {
|
||||
rb_yield(self);
|
||||
iface->delete_address(iface, addr, bits);
|
||||
}
|
||||
addr->destroy(addr);
|
||||
return self;
|
||||
}
|
||||
|
||||
static VALUE iface_each_addr(int argc, VALUE *argv, VALUE self)
|
||||
{
|
||||
enumerator_t *enumerator;
|
||||
linked_list_t *list;
|
||||
iface_t *iface;
|
||||
host_t *addr;
|
||||
char buf[64], *fmt = "%H";
|
||||
|
||||
if (!rb_block_given_p())
|
||||
{
|
||||
rb_raise(rb_eArgError, "must be called with a block");
|
||||
}
|
||||
list = linked_list_create();
|
||||
Data_Get_Struct(self, iface_t, iface);
|
||||
enumerator = iface->create_address_enumerator(iface);
|
||||
while (enumerator->enumerate(enumerator, &addr))
|
||||
{
|
||||
list->insert_last(list, addr->clone(addr));
|
||||
}
|
||||
enumerator->destroy(enumerator);
|
||||
while (list->remove_first(list, (void**)&addr) == SUCCESS)
|
||||
{
|
||||
snprintf(buf, sizeof(buf), fmt, addr);
|
||||
addr->destroy(addr);
|
||||
rb_yield(rb_str_new2(buf));
|
||||
}
|
||||
list->destroy(list);
|
||||
return self;
|
||||
}
|
||||
|
||||
static VALUE iface_del_addr(VALUE self, VALUE vaddr)
|
||||
{
|
||||
iface_t *iface;
|
||||
host_t *addr;
|
||||
int bits;
|
||||
|
||||
addr = host_create_from_subnet(StringValuePtr(vaddr), &bits);
|
||||
if (!addr)
|
||||
{
|
||||
rb_raise(rb_eArgError, "invalid IP address");
|
||||
}
|
||||
Data_Get_Struct(self, iface_t, iface);
|
||||
if (!iface->delete_address(iface, addr, bits))
|
||||
{
|
||||
addr->destroy(addr);
|
||||
rb_raise(rb_eRuntimeError, "address not found");
|
||||
}
|
||||
if (rb_block_given_p()) {
|
||||
rb_yield(self);
|
||||
iface->add_address(iface, addr, bits);
|
||||
}
|
||||
addr->destroy(addr);
|
||||
return self;
|
||||
}
|
||||
|
||||
static VALUE iface_delete(VALUE self)
|
||||
{
|
||||
guest_t *guest;
|
||||
iface_t *iface;
|
||||
|
||||
Data_Get_Struct(self, iface_t, iface);
|
||||
guest = iface->get_guest(iface);
|
||||
guest->destroy_iface(guest, iface);
|
||||
return Qnil;
|
||||
}
|
||||
|
||||
static void iface_init()
|
||||
{
|
||||
rbc_iface = rb_define_class_under(rbm_dumm , "Iface", rb_cObject);
|
||||
rb_include_module(rbc_iface, rb_mEnumerable);
|
||||
|
||||
rb_define_method(rbc_iface, "to_s", iface_to_s, 0);
|
||||
rb_define_method(rbc_iface, "connect", iface_connect, 1);
|
||||
rb_define_method(rbc_iface, "disconnect", iface_disconnect, 0);
|
||||
rb_define_method(rbc_iface, "add", iface_add_addr, 1);
|
||||
rb_define_method(rbc_iface, "del", iface_del_addr, 1);
|
||||
rb_define_method(rbc_iface, "each", iface_each_addr, -1);
|
||||
rb_define_method(rbc_iface, "delete", iface_delete, 0);
|
||||
}
|
||||
|
||||
static VALUE template_load(VALUE class, VALUE dir)
|
||||
{
|
||||
if (!dumm->load_template(dumm, StringValuePtr(dir)))
|
||||
{
|
||||
rb_raise(rb_eRuntimeError, "loading template failed");
|
||||
}
|
||||
return class;
|
||||
}
|
||||
|
||||
static VALUE template_unload(VALUE class)
|
||||
{
|
||||
if (!dumm->load_template(dumm, NULL))
|
||||
{
|
||||
rb_raise(rb_eRuntimeError, "unloading template failed");
|
||||
}
|
||||
return class;
|
||||
}
|
||||
|
||||
static VALUE template_each(int argc, VALUE *argv, VALUE class)
|
||||
{
|
||||
enumerator_t *enumerator;
|
||||
char *template;
|
||||
|
||||
if (!rb_block_given_p())
|
||||
{
|
||||
rb_raise(rb_eArgError, "must be called with a block");
|
||||
}
|
||||
enumerator = dumm->create_template_enumerator(dumm);
|
||||
while (enumerator->enumerate(enumerator, &template))
|
||||
{
|
||||
rb_yield(rb_str_new2(template));
|
||||
}
|
||||
enumerator->destroy(enumerator);
|
||||
return class;
|
||||
}
|
||||
|
||||
static void template_init()
|
||||
{
|
||||
rbc_template = rb_define_class_under(rbm_dumm , "Template", rb_cObject);
|
||||
rb_include_module(rb_class_of(rbc_template), rb_mEnumerable);
|
||||
|
||||
rb_define_singleton_method(rbc_template, "load", template_load, 1);
|
||||
rb_define_singleton_method(rbc_template, "unload", template_unload, 0);
|
||||
rb_define_singleton_method(rbc_template, "each", template_each, -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* extension finalization
|
||||
*/
|
||||
void Final_dumm()
|
||||
{
|
||||
struct sigaction action;
|
||||
|
||||
dumm->destroy(dumm);
|
||||
|
||||
sigemptyset(&action.sa_mask);
|
||||
action.sa_handler = SIG_DFL;
|
||||
action.sa_flags = 0;
|
||||
sigaction(SIGCHLD, &action, NULL);
|
||||
|
||||
library_deinit();
|
||||
}
|
||||
|
||||
/**
|
||||
* extension initialization
|
||||
*/
|
||||
void Init_dumm()
|
||||
{
|
||||
struct sigaction action;
|
||||
|
||||
/* there are too many to report, rubyruby... */
|
||||
setenv("LEAK_DETECTIVE_DISABLE", "1", 1);
|
||||
|
||||
library_init(NULL, "dumm");
|
||||
|
||||
dumm = dumm_create(NULL);
|
||||
|
||||
dumm_init();
|
||||
guest_init();
|
||||
bridge_init();
|
||||
iface_init();
|
||||
template_init();
|
||||
|
||||
sigemptyset(&action.sa_mask);
|
||||
action.sa_sigaction = sigchld_handler;
|
||||
action.sa_flags = SA_SIGINFO;
|
||||
sigaction(SIGCHLD, &action, NULL);
|
||||
|
||||
rb_set_end_proc(Final_dumm, 0);
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
#
|
||||
# DUMM for Ruby
|
||||
#
|
||||
|
||||
require 'mkmf'
|
||||
|
||||
$defs << " @DEFS@"
|
||||
$CFLAGS << " -Wno-format -include \"@top_builddir@/config.h\""
|
||||
|
||||
dir_config('dumm', '@top_srcdir@/src/dumm', '../.libs')
|
||||
dir_config('strongswan', '@top_srcdir@/src/libstrongswan', '../../libstrongswan/.libs')
|
||||
|
||||
unless find_library('dumm', 'dumm_create')
|
||||
puts "... failed: 'libdumm' not found!"
|
||||
exit
|
||||
end
|
||||
|
||||
create_makefile('dumm', '@top_srcdir@/src/dumm/ext')
|
||||
|
||||
@@ -1,63 +0,0 @@
|
||||
=begin
|
||||
Copyright (C) 2008-2009 Tobias Brunner
|
||||
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.
|
||||
=end
|
||||
|
||||
require 'dumm.so'
|
||||
require 'dumm/guest'
|
||||
|
||||
module Dumm
|
||||
|
||||
# use guest/bridge indentifiers directly
|
||||
def method_missing(id, *args)
|
||||
if Guest.guest? id
|
||||
return Guest[id]
|
||||
end
|
||||
if Bridge.bridge? id
|
||||
return Bridge[id]
|
||||
end
|
||||
super(id, *args)
|
||||
end
|
||||
|
||||
# shortcut for Template loading
|
||||
def template(name = nil)
|
||||
if name
|
||||
Template.load name
|
||||
else
|
||||
Template.sort.each {|t| puts t }
|
||||
end
|
||||
return Dumm
|
||||
end
|
||||
|
||||
# unload template/overlays, reset all guests and delete bridges
|
||||
def reset
|
||||
Template.unload
|
||||
Guest.each { |guest|
|
||||
guest.reset
|
||||
}
|
||||
Bridge.each { |bridge|
|
||||
bridge.delete
|
||||
}
|
||||
return Dumm
|
||||
end
|
||||
|
||||
# wait until all running guests have booted up
|
||||
def boot
|
||||
Guest.each {|g|
|
||||
g.boot if g.running?
|
||||
}
|
||||
return Dumm
|
||||
end
|
||||
end
|
||||
|
||||
# vim:sw=2 ts=2 et
|
||||
@@ -1,59 +0,0 @@
|
||||
=begin
|
||||
Copyright (C) 2008-2010 Tobias Brunner
|
||||
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.
|
||||
=end
|
||||
|
||||
module Dumm
|
||||
class Guest
|
||||
# accessor for guests
|
||||
# e.g. Guest.sun instead of Guest["sun"]
|
||||
def self.method_missing(id, *args)
|
||||
unless guest? id
|
||||
super(id, *args)
|
||||
end
|
||||
Guest[id]
|
||||
end
|
||||
|
||||
# accessor for interfaces
|
||||
# e.g. guest.eth0 instead of guest["eth0"]
|
||||
def method_missing(id, *args)
|
||||
unless iface? id
|
||||
super(id, *args)
|
||||
end
|
||||
self[id]
|
||||
end
|
||||
|
||||
# remove all overlays, delete all interfaces
|
||||
def reset
|
||||
while pop_overlay; end
|
||||
each {|i|
|
||||
i.delete
|
||||
}
|
||||
end
|
||||
|
||||
# has the guest booted up?
|
||||
def booted?
|
||||
exec("pgrep getty")
|
||||
execstatus == 0
|
||||
end
|
||||
|
||||
# wait until the guest has booted
|
||||
def boot
|
||||
while not booted?
|
||||
sleep(1)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# vim:sw=2 ts=2 et
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -1,222 +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.
|
||||
*/
|
||||
|
||||
#ifndef GUEST_H
|
||||
#define GUEST_H
|
||||
|
||||
#include <library.h>
|
||||
#include <collections/enumerator.h>
|
||||
|
||||
typedef enum guest_state_t guest_state_t;
|
||||
typedef struct guest_t guest_t;
|
||||
|
||||
#include "iface.h"
|
||||
|
||||
/**
|
||||
* State of a guest (started, stopped, ...)
|
||||
*/
|
||||
enum guest_state_t {
|
||||
/** guest kernel not running at all */
|
||||
GUEST_STOPPED,
|
||||
/** kernel started, but not yet available */
|
||||
GUEST_STARTING,
|
||||
/** guest is up and running */
|
||||
GUEST_RUNNING,
|
||||
/** guest has been paused */
|
||||
GUEST_PAUSED,
|
||||
/** guest is stopping (shutting down) */
|
||||
GUEST_STOPPING,
|
||||
};
|
||||
|
||||
/**
|
||||
* string mappings for guest_state_t
|
||||
*/
|
||||
extern enum_name_t *guest_state_names;
|
||||
|
||||
/**
|
||||
* Invoke function which launches the UML guest.
|
||||
*
|
||||
* Consoles are all set to NULL, you may change them by adding additional UML
|
||||
* options to args before invocation.
|
||||
*
|
||||
* @param data callback data
|
||||
* @param guest guest to start
|
||||
* @param args args to use for guest invocation, args[0] is kernel
|
||||
* @param argc number of elements in args
|
||||
* @param idle
|
||||
* @return PID of child, 0 if failed
|
||||
*/
|
||||
typedef pid_t (*invoke_function_t)(void *data, guest_t *guest,
|
||||
char *args[], int argc);
|
||||
|
||||
/**
|
||||
* Idle function to pass to start().
|
||||
*/
|
||||
typedef void (*idle_function_t)(void);
|
||||
|
||||
/**
|
||||
* A guest is a UML instance running on the host.
|
||||
**/
|
||||
struct guest_t {
|
||||
|
||||
/**
|
||||
* Get the name of this guest.
|
||||
*
|
||||
* @return name of the guest
|
||||
*/
|
||||
char* (*get_name) (guest_t *this);
|
||||
|
||||
/**
|
||||
* Get the process ID of the guest child process.
|
||||
*
|
||||
* @return name of the guest
|
||||
*/
|
||||
pid_t (*get_pid) (guest_t *this);
|
||||
|
||||
/**
|
||||
* Get the state of the guest (stopped, started, etc.).
|
||||
*
|
||||
* @return guests state
|
||||
*/
|
||||
guest_state_t (*get_state)(guest_t *this);
|
||||
|
||||
/**
|
||||
* Start the guest.
|
||||
*
|
||||
* @param invoke UML guest invocation function
|
||||
* @param data data to pass back to invoke function
|
||||
* @param idle idle function to call while waiting on child
|
||||
* @return TRUE if guest successfully started
|
||||
*/
|
||||
bool (*start) (guest_t *this, invoke_function_t invoke, void *data,
|
||||
idle_function_t idle);
|
||||
|
||||
/**
|
||||
* Kill the guest.
|
||||
*
|
||||
* @param idle idle function to call while waiting to termination
|
||||
*/
|
||||
void (*stop) (guest_t *this, idle_function_t idle);
|
||||
|
||||
/**
|
||||
* Create a new interface in the current scenario.
|
||||
*
|
||||
* @param name name of the interface in the guest
|
||||
* @return created interface, or NULL if failed
|
||||
*/
|
||||
iface_t* (*create_iface)(guest_t *this, char *name);
|
||||
|
||||
/**
|
||||
* Destroy an interface on guest.
|
||||
*
|
||||
* @param iface interface to destroy
|
||||
*/
|
||||
void (*destroy_iface)(guest_t *this, iface_t *iface);
|
||||
|
||||
/**
|
||||
* Create an enumerator over all guest interfaces.
|
||||
*
|
||||
* @return enumerator over iface_t's
|
||||
*/
|
||||
enumerator_t* (*create_iface_enumerator)(guest_t *this);
|
||||
|
||||
/**
|
||||
* Adds a COWFS overlay. The directory is created if it does not exist.
|
||||
*
|
||||
* @param dir directory where overlay diff should point to
|
||||
* @return FALSE, if failed
|
||||
*/
|
||||
bool (*add_overlay)(guest_t *this, char *dir);
|
||||
|
||||
/**
|
||||
* Removes the specified COWFS overlay.
|
||||
*
|
||||
* @param dir directory where overlay diff points to
|
||||
* @return FALSE, if no found
|
||||
*/
|
||||
bool (*del_overlay)(guest_t *this, char *dir);
|
||||
|
||||
/**
|
||||
* Removes the latest COWFS overlay.
|
||||
*
|
||||
* @return FALSE, if no overlay was found
|
||||
*/
|
||||
bool (*pop_overlay)(guest_t *this);
|
||||
|
||||
/**
|
||||
* Execute a command on the guests mconsole.
|
||||
*
|
||||
* @param cb callback to call for each read block
|
||||
* @param data data to pass to callback
|
||||
* @param cmd command to execute
|
||||
* @param ... printf style argument list for cmd
|
||||
* @return return value
|
||||
*/
|
||||
int (*exec)(guest_t *this, void(*cb)(void*,char*,size_t), void *data,
|
||||
char *cmd, ...);
|
||||
|
||||
/**
|
||||
* Execute a command on the guests mconsole, with output formatter.
|
||||
*
|
||||
* If lines is TRUE, callback is invoked for each output line. Otherwise
|
||||
* the full result is returned in one callback invocation.
|
||||
*
|
||||
* @note This function does not work with binary output.
|
||||
*
|
||||
* @param cb callback to call for each line or for the complete output
|
||||
* @param lines TRUE if the callback should be called for each line
|
||||
* @param data data to pass to callback
|
||||
* @param cmd command to execute
|
||||
* @param ... printf style argument list for cmd
|
||||
* @return return value
|
||||
*/
|
||||
int (*exec_str)(guest_t *this, void(*cb)(void*,char*), bool lines,
|
||||
void *data, char *cmd, ...);
|
||||
|
||||
/**
|
||||
* Called whenever a SIGCHILD for the guests PID is received.
|
||||
*/
|
||||
void (*sigchild)(guest_t *this);
|
||||
|
||||
/**
|
||||
* Close and destroy a guest with all interfaces
|
||||
*/
|
||||
void (*destroy) (guest_t *this);
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a new, unstarted guest.
|
||||
*
|
||||
* @param parent parent directory to create the guest in
|
||||
* @param name name of the guest to create
|
||||
* @param kernel kernel this guest uses
|
||||
* @param master read-only master filesystem for guest
|
||||
* @param args additional args to pass to kernel
|
||||
* @param mem amount of memory to give the guest
|
||||
*/
|
||||
guest_t *guest_create(char *parent, char *name, char *kernel,
|
||||
char *master, char *args);
|
||||
|
||||
/**
|
||||
* Load a guest created with guest_create().
|
||||
*
|
||||
* @param parent parent directory to look for a guest
|
||||
* @param name name of the guest directory
|
||||
*/
|
||||
guest_t *guest_load(char *parent, char *name);
|
||||
|
||||
#endif /* GUEST_H */
|
||||
|
||||
@@ -1,299 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2008 Tobias Brunner
|
||||
* Copyright (C) 2007 Martin Willi
|
||||
* HSR Hochschule fuer Technik Rapperswil
|
||||
* Copyright (C) 2002 Jeff Dike
|
||||
*
|
||||
* Based on the "tunctl" utility from Jeff Dike.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <net/if.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <linux/if_tun.h>
|
||||
|
||||
#include <utils/debug.h>
|
||||
#include <collections/linked_list.h>
|
||||
|
||||
#include "iface.h"
|
||||
|
||||
typedef struct private_iface_t private_iface_t;
|
||||
|
||||
struct private_iface_t {
|
||||
/** public interface */
|
||||
iface_t public;
|
||||
/** device name in guest (eth0) */
|
||||
char *guestif;
|
||||
/** device name at host (tap0) */
|
||||
char *hostif;
|
||||
/** bridge this interface is attached to */
|
||||
bridge_t *bridge;
|
||||
/** guest this interface is attached to */
|
||||
guest_t *guest;
|
||||
/** mconsole for guest */
|
||||
mconsole_t *mconsole;
|
||||
};
|
||||
|
||||
/**
|
||||
* bring an interface up or down (host side)
|
||||
*/
|
||||
bool iface_control(char *name, bool up)
|
||||
{
|
||||
int s;
|
||||
bool good = FALSE;
|
||||
struct ifreq ifr;
|
||||
|
||||
memset(&ifr, 0, sizeof(struct ifreq));
|
||||
strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
|
||||
|
||||
s = socket(AF_INET, SOCK_DGRAM, 0);
|
||||
if (!s)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
if (ioctl(s, SIOCGIFFLAGS, &ifr) == 0)
|
||||
{
|
||||
if (up)
|
||||
{
|
||||
ifr.ifr_flags |= IFF_UP;
|
||||
}
|
||||
else
|
||||
{
|
||||
ifr.ifr_flags &= ~IFF_UP;
|
||||
}
|
||||
if (ioctl(s, SIOCSIFFLAGS, &ifr) == 0)
|
||||
{
|
||||
good = TRUE;
|
||||
}
|
||||
}
|
||||
close(s);
|
||||
return good;
|
||||
}
|
||||
|
||||
METHOD(iface_t, get_guestif, char*,
|
||||
private_iface_t *this)
|
||||
{
|
||||
return this->guestif;
|
||||
}
|
||||
|
||||
METHOD(iface_t, get_hostif, char*,
|
||||
private_iface_t *this)
|
||||
{
|
||||
return this->hostif;
|
||||
}
|
||||
|
||||
METHOD(iface_t, add_address, bool,
|
||||
private_iface_t *this, host_t *addr, int bits)
|
||||
{
|
||||
return (this->guest->exec(this->guest, NULL, NULL,
|
||||
"exec ip addr add %H/%d dev %s", addr, bits, this->guestif) == 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* compile a list of the addresses of an interface
|
||||
*/
|
||||
static void compile_address_list(linked_list_t *list, char *address)
|
||||
{
|
||||
host_t *host = host_create_from_string(address, 0);
|
||||
if (host)
|
||||
{
|
||||
list->insert_last(list, host);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* delete the list of addresses
|
||||
*/
|
||||
static void destroy_address_list(linked_list_t *list)
|
||||
{
|
||||
list->destroy_offset(list, offsetof(host_t, destroy));
|
||||
}
|
||||
|
||||
METHOD(iface_t, create_address_enumerator, enumerator_t*,
|
||||
private_iface_t *this)
|
||||
{
|
||||
linked_list_t *addresses = linked_list_create();
|
||||
this->guest->exec_str(this->guest, (void(*)(void*,char*))compile_address_list,
|
||||
TRUE, addresses,
|
||||
"exec ip addr list dev %s scope global | "
|
||||
"grep '^ \\+\\(inet6\\? \\)' | "
|
||||
"awk -F '( +|/)' '{ print $3 }'", this->guestif);
|
||||
return enumerator_create_cleaner(addresses->create_enumerator(addresses),
|
||||
(void(*)(void*))destroy_address_list, addresses);
|
||||
}
|
||||
|
||||
METHOD(iface_t, delete_address, bool,
|
||||
private_iface_t *this, host_t *addr, int bits)
|
||||
{
|
||||
return (this->guest->exec(this->guest, NULL, NULL,
|
||||
"exec ip addr del %H/%d dev %s", addr, bits, this->guestif) == 0);
|
||||
}
|
||||
|
||||
METHOD(iface_t, set_bridge, void,
|
||||
private_iface_t *this, bridge_t *bridge)
|
||||
{
|
||||
if (this->bridge == NULL && bridge)
|
||||
{
|
||||
this->guest->exec(this->guest, NULL, NULL,
|
||||
"exec ip link set %s up", this->guestif);
|
||||
}
|
||||
else if (this->bridge && bridge == NULL)
|
||||
{
|
||||
this->guest->exec(this->guest, NULL, NULL,
|
||||
"exec ip link set %s down", this->guestif);
|
||||
}
|
||||
this->bridge = bridge;
|
||||
}
|
||||
|
||||
METHOD(iface_t, get_bridge, bridge_t*,
|
||||
private_iface_t *this)
|
||||
{
|
||||
return this->bridge;
|
||||
}
|
||||
|
||||
METHOD(iface_t, get_guest, guest_t*,
|
||||
private_iface_t *this)
|
||||
{
|
||||
return this->guest;
|
||||
}
|
||||
|
||||
/**
|
||||
* destroy the tap device
|
||||
*/
|
||||
static bool destroy_tap(private_iface_t *this)
|
||||
{
|
||||
struct ifreq ifr;
|
||||
int tap;
|
||||
|
||||
if (!iface_control(this->hostif, FALSE))
|
||||
{
|
||||
DBG1(DBG_LIB, "bringing iface down failed: %m");
|
||||
}
|
||||
memset(&ifr, 0, sizeof(ifr));
|
||||
ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
|
||||
strncpy(ifr.ifr_name, this->hostif, sizeof(ifr.ifr_name) - 1);
|
||||
|
||||
tap = open(TAP_DEVICE, O_RDWR);
|
||||
if (tap < 0)
|
||||
{
|
||||
DBG1(DBG_LIB, "unable to open tap device %s: %m", TAP_DEVICE);
|
||||
return FALSE;
|
||||
}
|
||||
if (ioctl(tap, TUNSETIFF, &ifr) < 0 ||
|
||||
ioctl(tap, TUNSETPERSIST, 0) < 0)
|
||||
{
|
||||
DBG1(DBG_LIB, "removing %s failed: %m", this->hostif);
|
||||
close(tap);
|
||||
return FALSE;
|
||||
}
|
||||
close(tap);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* create the tap device
|
||||
*/
|
||||
static char* create_tap(private_iface_t *this)
|
||||
{
|
||||
struct ifreq ifr;
|
||||
int tap;
|
||||
|
||||
memset(&ifr, 0, sizeof(ifr));
|
||||
ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
|
||||
snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s-%s",
|
||||
this->guest->get_name(this->guest), this->guestif);
|
||||
|
||||
tap = open(TAP_DEVICE, O_RDWR);
|
||||
if (tap < 0)
|
||||
{
|
||||
DBG1(DBG_LIB, "unable to open tap device %s: %m", TAP_DEVICE);
|
||||
return NULL;
|
||||
}
|
||||
if (ioctl(tap, TUNSETIFF, &ifr) < 0 ||
|
||||
ioctl(tap, TUNSETPERSIST, 1) < 0 ||
|
||||
ioctl(tap, TUNSETOWNER, 0))
|
||||
{
|
||||
DBG1(DBG_LIB, "creating new tap device failed: %m");
|
||||
close(tap);
|
||||
return NULL;
|
||||
}
|
||||
close(tap);
|
||||
return strdup(ifr.ifr_name);
|
||||
}
|
||||
|
||||
METHOD(iface_t, destroy, void,
|
||||
private_iface_t *this)
|
||||
{
|
||||
if (this->bridge)
|
||||
{
|
||||
this->bridge->disconnect_iface(this->bridge, &this->public);
|
||||
}
|
||||
/* TODO: iface mgmt is not blocking yet, so wait some ticks */
|
||||
usleep(50000);
|
||||
this->mconsole->del_iface(this->mconsole, this->guestif);
|
||||
destroy_tap(this);
|
||||
free(this->guestif);
|
||||
free(this->hostif);
|
||||
free(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* create the iface instance
|
||||
*/
|
||||
iface_t *iface_create(char *name, guest_t *guest, mconsole_t *mconsole)
|
||||
{
|
||||
private_iface_t *this;
|
||||
|
||||
INIT(this,
|
||||
.public = {
|
||||
.get_hostif = _get_hostif,
|
||||
.get_guestif = _get_guestif,
|
||||
.add_address = _add_address,
|
||||
.create_address_enumerator = _create_address_enumerator,
|
||||
.delete_address = _delete_address,
|
||||
.set_bridge = _set_bridge,
|
||||
.get_bridge = _get_bridge,
|
||||
.get_guest = _get_guest,
|
||||
.destroy = _destroy,
|
||||
},
|
||||
.mconsole = mconsole,
|
||||
.guestif = strdup(name),
|
||||
.guest = guest,
|
||||
);
|
||||
this->hostif = create_tap(this);
|
||||
if (this->hostif == NULL)
|
||||
{
|
||||
destroy_tap(this);
|
||||
free(this->guestif);
|
||||
free(this);
|
||||
return NULL;
|
||||
}
|
||||
if (!this->mconsole->add_iface(this->mconsole, this->guestif, this->hostif))
|
||||
{
|
||||
DBG1(DBG_LIB, "creating interface '%s' in guest failed", this->guestif);
|
||||
destroy_tap(this);
|
||||
free(this->guestif);
|
||||
free(this->hostif);
|
||||
free(this);
|
||||
return NULL;
|
||||
}
|
||||
if (!iface_control(this->hostif, TRUE))
|
||||
{
|
||||
DBG1(DBG_LIB, "bringing iface '%s' up failed: %m", this->hostif);
|
||||
}
|
||||
return &this->public;
|
||||
}
|
||||
|
||||
@@ -1,115 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef IFACE_H
|
||||
#define IFACE_H
|
||||
|
||||
#include <library.h>
|
||||
#include <collections/enumerator.h>
|
||||
#include <networking/host.h>
|
||||
|
||||
#define TAP_DEVICE "/dev/net/tun"
|
||||
|
||||
typedef struct iface_t iface_t;
|
||||
|
||||
#include "mconsole.h"
|
||||
#include "bridge.h"
|
||||
#include "guest.h"
|
||||
|
||||
/**
|
||||
* Interface in a guest, connected to a tap device on the host.
|
||||
*/
|
||||
struct iface_t {
|
||||
|
||||
/**
|
||||
* Get the interface name in the guest (e.g. eth0).
|
||||
*
|
||||
* @return guest interface name
|
||||
*/
|
||||
char* (*get_guestif)(iface_t *this);
|
||||
|
||||
/**
|
||||
* Get the interface name at the host (e.g. tap0).
|
||||
*
|
||||
* @return host interface (tap device) name
|
||||
*/
|
||||
char* (*get_hostif)(iface_t *this);
|
||||
|
||||
/**
|
||||
* Add an address to the interface.
|
||||
*
|
||||
* @param addr address to add to the interface
|
||||
* @param bits network prefix length in bits
|
||||
* @return TRUE if address added
|
||||
*/
|
||||
bool (*add_address)(iface_t *this, host_t *addr, int bits);
|
||||
|
||||
/**
|
||||
* Create an enumerator over all installed addresses.
|
||||
*
|
||||
* @return enumerator over host_t*
|
||||
*/
|
||||
enumerator_t* (*create_address_enumerator)(iface_t *this);
|
||||
|
||||
/**
|
||||
* Remove an address from an interface.
|
||||
*
|
||||
* @note The network prefix length has to be the same as used in add_address
|
||||
*
|
||||
* @param addr address to remove
|
||||
* @param bits network prefix length in bits
|
||||
* @return TRUE if address removed
|
||||
*/
|
||||
bool (*delete_address)(iface_t *this, host_t *addr, int bits);
|
||||
|
||||
/**
|
||||
* Set the bridge this interface is attached to.
|
||||
*
|
||||
* @param bridge assigned bridge, or NULL for none
|
||||
*/
|
||||
void (*set_bridge)(iface_t *this, bridge_t *bridge);
|
||||
|
||||
/**
|
||||
* Get the bridge this iface is connected, or NULL.
|
||||
*
|
||||
* @return connected bridge, or NULL
|
||||
*/
|
||||
bridge_t* (*get_bridge)(iface_t *this);
|
||||
|
||||
/**
|
||||
* Get the guest this iface belongs to.
|
||||
*
|
||||
* @return guest of this iface
|
||||
*/
|
||||
guest_t* (*get_guest)(iface_t *this);
|
||||
|
||||
/**
|
||||
* Destroy an interface
|
||||
*/
|
||||
void (*destroy) (iface_t *this);
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a new interface for a guest
|
||||
*
|
||||
* @param name name of the interface in the guest
|
||||
* @param guest guest this iface is connecting
|
||||
* @param mconsole mconsole of guest
|
||||
* @return interface descriptor, or NULL if failed
|
||||
*/
|
||||
iface_t *iface_create(char *name, guest_t *guest, mconsole_t *mconsole);
|
||||
|
||||
#endif /* IFACE_H */
|
||||
|
||||
@@ -1,68 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2008 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.
|
||||
*/
|
||||
|
||||
#undef PACKAGE_NAME
|
||||
#undef PACKAGE_TARNAME
|
||||
#undef PACKAGE_VERSION
|
||||
#undef PACKAGE_STRING
|
||||
#undef PACKAGE_BUGREPORT
|
||||
#undef PACKAGE_URL
|
||||
#undef HAVE_DLADDR
|
||||
#undef HAVE_QSORT_R
|
||||
#include <ruby.h>
|
||||
|
||||
#ifdef HAVE_RB_ERRINFO
|
||||
#define ruby_errinfo rb_errinfo()
|
||||
#endif
|
||||
|
||||
/**
|
||||
* main routine, parses args and reads from console
|
||||
*/
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int state, i;
|
||||
char buf[512];
|
||||
|
||||
ruby_init();
|
||||
ruby_init_loadpath();
|
||||
|
||||
rb_eval_string_protect("require 'dumm' and include Dumm", &state);
|
||||
if (state)
|
||||
{
|
||||
rb_p(ruby_errinfo);
|
||||
printf("Please install the ruby extension first!\n");
|
||||
}
|
||||
for (i = 1; i < argc; i++)
|
||||
{
|
||||
snprintf(buf, sizeof(buf), "load \"%s\"", argv[i]);
|
||||
printf("%s\n", buf);
|
||||
rb_eval_string_protect(buf, &state);
|
||||
if (state)
|
||||
{
|
||||
rb_p(ruby_errinfo);
|
||||
}
|
||||
}
|
||||
rb_require("irb");
|
||||
rb_require("irb/completion");
|
||||
rb_eval_string_protect("IRB.start", &state);
|
||||
if (state)
|
||||
{
|
||||
rb_p(ruby_errinfo);
|
||||
}
|
||||
|
||||
ruby_finalize();
|
||||
return 0;
|
||||
}
|
||||
|
||||
-629
@@ -1,629 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2008 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.
|
||||
*/
|
||||
|
||||
#include "dumm.h"
|
||||
|
||||
#include <collections/linked_list.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#include <sched.h>
|
||||
|
||||
#include <glib.h>
|
||||
#include <gtk/gtk.h>
|
||||
#include <vte/vte.h>
|
||||
#include <vte/reaper.h>
|
||||
|
||||
/**
|
||||
* notebook page with vte and guest
|
||||
*/
|
||||
typedef struct {
|
||||
gint num;
|
||||
GtkWidget *vte;
|
||||
guest_t *guest;
|
||||
} page_t;
|
||||
|
||||
/**
|
||||
* Main window
|
||||
*/
|
||||
GtkWidget *window;
|
||||
|
||||
/**
|
||||
* notebook with guests, vtes
|
||||
*/
|
||||
GtkWidget *notebook;
|
||||
|
||||
/**
|
||||
* dumm context
|
||||
*/
|
||||
dumm_t *dumm;
|
||||
|
||||
/**
|
||||
* pages in notebook, page_t
|
||||
*/
|
||||
linked_list_t *pages;
|
||||
|
||||
/**
|
||||
* handle guest termination, SIGCHILD
|
||||
*/
|
||||
static void child_exited(VteReaper *vtereaper, gint pid, gint status)
|
||||
{
|
||||
enumerator_t *enumerator;
|
||||
page_t *page;
|
||||
|
||||
enumerator = pages->create_enumerator(pages);
|
||||
while (enumerator->enumerate(enumerator, (void**)&page))
|
||||
{
|
||||
if (page->guest->get_pid(page->guest) == pid)
|
||||
{
|
||||
page->guest->sigchild(page->guest);
|
||||
vte_terminal_feed(VTE_TERMINAL(page->vte),
|
||||
"\n\r--- guest terminated ---\n\r", -1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
enumerator->destroy(enumerator);
|
||||
}
|
||||
|
||||
static page_t* get_page(int num)
|
||||
{
|
||||
enumerator_t *enumerator;
|
||||
page_t *page, *found = NULL;
|
||||
|
||||
enumerator = pages->create_enumerator(pages);
|
||||
while (enumerator->enumerate(enumerator, (void**)&page))
|
||||
{
|
||||
if (page->num == num)
|
||||
{
|
||||
found = page;
|
||||
break;
|
||||
}
|
||||
}
|
||||
enumerator->destroy(enumerator);
|
||||
return found;
|
||||
}
|
||||
|
||||
/**
|
||||
* Guest invocation callback
|
||||
*/
|
||||
static pid_t invoke(void *vte, guest_t *guest,
|
||||
char *args[], int argc)
|
||||
{
|
||||
GPid pid;
|
||||
|
||||
if (vte_terminal_fork_command_full(VTE_TERMINAL(vte),
|
||||
VTE_PTY_NO_LASTLOG | VTE_PTY_NO_UTMP | VTE_PTY_NO_WTMP,
|
||||
NULL, args, NULL,
|
||||
G_SPAWN_CHILD_INHERITS_STDIN | G_SPAWN_SEARCH_PATH,
|
||||
NULL, NULL, &pid, NULL))
|
||||
{
|
||||
return pid;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void idle(void)
|
||||
{
|
||||
gtk_main_iteration_do(FALSE);
|
||||
sched_yield();
|
||||
}
|
||||
|
||||
static void start_guest()
|
||||
{
|
||||
page_t *page;
|
||||
|
||||
page = get_page(gtk_notebook_get_current_page(GTK_NOTEBOOK(notebook)));
|
||||
if (page && page->guest->get_state(page->guest) == GUEST_STOPPED)
|
||||
{
|
||||
vte_terminal_feed(VTE_TERMINAL(page->vte),
|
||||
"--- starting guest ---\n\r", -1);
|
||||
page->guest->start(page->guest, invoke, VTE_TERMINAL(page->vte), idle);
|
||||
}
|
||||
}
|
||||
|
||||
static void start_all_guests()
|
||||
{
|
||||
enumerator_t *enumerator;
|
||||
page_t *page;
|
||||
|
||||
enumerator = pages->create_enumerator(pages);
|
||||
while (enumerator->enumerate(enumerator, (void**)&page))
|
||||
{
|
||||
if (page->guest->get_state(page->guest) == GUEST_STOPPED)
|
||||
{
|
||||
vte_terminal_feed(VTE_TERMINAL(page->vte),
|
||||
"--- starting all guests ---\n\r", -1);
|
||||
page->guest->start(page->guest, invoke,
|
||||
VTE_TERMINAL(page->vte), idle);
|
||||
}
|
||||
}
|
||||
enumerator->destroy(enumerator);
|
||||
}
|
||||
|
||||
static void stop_guest()
|
||||
{
|
||||
page_t *page;
|
||||
|
||||
page = get_page(gtk_notebook_get_current_page(GTK_NOTEBOOK(notebook)));
|
||||
if (page && page->guest->get_state(page->guest) == GUEST_RUNNING)
|
||||
{
|
||||
page->guest->stop(page->guest, idle);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* quit signal handler
|
||||
*/
|
||||
static void quit()
|
||||
{
|
||||
enumerator_t *enumerator;
|
||||
page_t *page;
|
||||
|
||||
dumm->load_template(dumm, NULL);
|
||||
|
||||
enumerator = pages->create_enumerator(pages);
|
||||
while (enumerator->enumerate(enumerator, &page))
|
||||
{
|
||||
if (page->guest->get_state(page->guest) != GUEST_STOPPED)
|
||||
{
|
||||
page->guest->stop(page->guest, idle);
|
||||
}
|
||||
}
|
||||
enumerator->destroy(enumerator);
|
||||
gtk_main_quit();
|
||||
}
|
||||
|
||||
static void error_dialog(char *msg)
|
||||
{
|
||||
GtkWidget *error;
|
||||
|
||||
error = gtk_message_dialog_new(GTK_WINDOW(window),
|
||||
GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR,
|
||||
GTK_BUTTONS_CLOSE, msg);
|
||||
gtk_dialog_run(GTK_DIALOG(error));
|
||||
gtk_widget_destroy(error);
|
||||
}
|
||||
|
||||
static void create_switch()
|
||||
{
|
||||
GtkWidget *dialog, *table, *label, *name;
|
||||
bridge_t *bridge;
|
||||
|
||||
dialog = gtk_dialog_new_with_buttons("Create new switch", GTK_WINDOW(window),
|
||||
GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
|
||||
GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
|
||||
GTK_STOCK_NEW, GTK_RESPONSE_ACCEPT, NULL);
|
||||
|
||||
table = gtk_table_new(1, 2, TRUE);
|
||||
gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), table);
|
||||
|
||||
label = gtk_label_new("Switch name");
|
||||
gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, 1, 0, 0, 0, 0);
|
||||
gtk_widget_show(label);
|
||||
|
||||
name = gtk_entry_new();
|
||||
gtk_table_attach(GTK_TABLE(table), name, 1, 2, 0, 1,
|
||||
GTK_FILL | GTK_EXPAND | GTK_SHRINK, 0, 0, 0);
|
||||
gtk_widget_show(name);
|
||||
|
||||
gtk_widget_show(table);
|
||||
|
||||
while (TRUE)
|
||||
{
|
||||
switch (gtk_dialog_run(GTK_DIALOG(dialog)))
|
||||
{
|
||||
case GTK_RESPONSE_ACCEPT:
|
||||
{
|
||||
if (streq(gtk_entry_get_text(GTK_ENTRY(name)), ""))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
bridge = dumm->create_bridge(dumm,
|
||||
(char*)gtk_entry_get_text(GTK_ENTRY(name)));
|
||||
if (!bridge)
|
||||
{
|
||||
error_dialog("creating bridge failed!");
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
gtk_widget_destroy(dialog);
|
||||
}
|
||||
|
||||
static void delete_switch()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
static void connect_guest()
|
||||
{
|
||||
page_t *page;
|
||||
GtkWidget *dialog, *table, *label, *name, *box;
|
||||
bridge_t *bridge;
|
||||
iface_t *iface;
|
||||
enumerator_t *enumerator;
|
||||
|
||||
page = get_page(gtk_notebook_get_current_page(GTK_NOTEBOOK(notebook)));
|
||||
if (!page || page->guest->get_state(page->guest) != GUEST_RUNNING)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
dialog = gtk_dialog_new_with_buttons("Connect guest", GTK_WINDOW(window),
|
||||
GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
|
||||
GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
|
||||
GTK_STOCK_NEW, GTK_RESPONSE_ACCEPT, NULL);
|
||||
|
||||
table = gtk_table_new(2, 2, TRUE);
|
||||
gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), table);
|
||||
|
||||
label = gtk_label_new("Interface name");
|
||||
gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, 1, 0, 0, 0, 0);
|
||||
gtk_widget_show(label);
|
||||
|
||||
name = gtk_entry_new();
|
||||
gtk_table_attach(GTK_TABLE(table), name, 1, 2, 0, 1,
|
||||
GTK_FILL | GTK_EXPAND | GTK_SHRINK, 0, 0, 0);
|
||||
gtk_widget_show(name);
|
||||
|
||||
label = gtk_label_new("Connected switch");
|
||||
gtk_table_attach(GTK_TABLE(table), label, 0, 1, 1, 2, 0, 0, 0, 0);
|
||||
gtk_widget_show(label);
|
||||
|
||||
box = gtk_combo_box_new_text();
|
||||
gtk_table_attach(GTK_TABLE(table), box, 1, 2, 1, 2,
|
||||
GTK_FILL | GTK_EXPAND | GTK_SHRINK, 0, 0, 0);
|
||||
enumerator = dumm->create_bridge_enumerator(dumm);
|
||||
while (enumerator->enumerate(enumerator, &bridge))
|
||||
{
|
||||
gtk_combo_box_append_text(GTK_COMBO_BOX(box), bridge->get_name(bridge));
|
||||
}
|
||||
enumerator->destroy(enumerator);
|
||||
gtk_widget_show(box);
|
||||
|
||||
gtk_widget_show(table);
|
||||
|
||||
while (TRUE)
|
||||
{
|
||||
switch (gtk_dialog_run(GTK_DIALOG(dialog)))
|
||||
{
|
||||
case GTK_RESPONSE_ACCEPT:
|
||||
{
|
||||
if (streq(gtk_entry_get_text(GTK_ENTRY(name)), ""))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
iface = page->guest->create_iface(page->guest,
|
||||
(char*)gtk_entry_get_text(GTK_ENTRY(name)));
|
||||
if (!iface)
|
||||
{
|
||||
error_dialog("creating interface failed!");
|
||||
continue;
|
||||
}
|
||||
enumerator = dumm->create_bridge_enumerator(dumm);
|
||||
while (enumerator->enumerate(enumerator, &bridge))
|
||||
{
|
||||
if (!bridge->connect_iface(bridge, iface))
|
||||
{
|
||||
error_dialog("connecting interface failed!");
|
||||
}
|
||||
break;
|
||||
}
|
||||
enumerator->destroy(enumerator);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
gtk_widget_destroy(dialog);
|
||||
}
|
||||
|
||||
static void disconnect_guest()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
static void delete_guest()
|
||||
{
|
||||
page_t *page;
|
||||
|
||||
page = get_page(gtk_notebook_get_current_page(GTK_NOTEBOOK(notebook)));
|
||||
if (page)
|
||||
{
|
||||
page->guest->stop(page->guest, idle);
|
||||
dumm->delete_guest(dumm, page->guest);
|
||||
gtk_notebook_remove_page(GTK_NOTEBOOK(notebook), page->num);
|
||||
pages->remove(pages, page, NULL);
|
||||
g_free(page);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* create a new page for a guest
|
||||
*/
|
||||
static page_t* create_page(guest_t *guest)
|
||||
{
|
||||
GtkWidget *label;
|
||||
page_t *page;
|
||||
|
||||
page = g_new(page_t, 1);
|
||||
page->guest = guest;
|
||||
page->vte = vte_terminal_new();
|
||||
label = gtk_label_new(guest->get_name(guest));
|
||||
page->num = gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
|
||||
page->vte, label);
|
||||
gtk_widget_show(page->vte);
|
||||
pages->insert_last(pages, page);
|
||||
return page;
|
||||
}
|
||||
|
||||
/**
|
||||
* create a new guest
|
||||
*/
|
||||
static void create_guest()
|
||||
{
|
||||
guest_t *guest;
|
||||
GtkWidget *dialog, *table, *label, *name, *kernel, *master, *args;
|
||||
|
||||
dialog = gtk_dialog_new_with_buttons("Create new guest", GTK_WINDOW(window),
|
||||
GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
|
||||
GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
|
||||
GTK_STOCK_NEW, GTK_RESPONSE_ACCEPT, NULL);
|
||||
|
||||
table = gtk_table_new(4, 2, TRUE);
|
||||
gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), table);
|
||||
|
||||
label = gtk_label_new("Guest name");
|
||||
gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, 1, 0, 0, 0, 0);
|
||||
gtk_widget_show(label);
|
||||
|
||||
label = gtk_label_new("UML kernel");
|
||||
gtk_table_attach(GTK_TABLE(table), label, 0, 1, 1, 2, 0, 0, 0, 0);
|
||||
gtk_widget_show(label);
|
||||
|
||||
label = gtk_label_new("Master filesystem");
|
||||
gtk_table_attach(GTK_TABLE(table), label, 0, 1, 2, 3, 0, 0, 0, 0);
|
||||
gtk_widget_show(label);
|
||||
|
||||
label = gtk_label_new("Kernel arguments");
|
||||
gtk_table_attach(GTK_TABLE(table), label, 0, 1, 3, 4, 0, 0, 0, 0);
|
||||
gtk_widget_show(label);
|
||||
|
||||
name = gtk_entry_new();
|
||||
gtk_table_attach(GTK_TABLE(table), name, 1, 2, 0, 1,
|
||||
GTK_FILL | GTK_EXPAND | GTK_SHRINK, 0, 0, 0);
|
||||
gtk_widget_show(name);
|
||||
|
||||
kernel = gtk_file_chooser_button_new("Select UML kernel image",
|
||||
GTK_FILE_CHOOSER_ACTION_OPEN);
|
||||
gtk_table_attach(GTK_TABLE(table), kernel, 1, 2, 1, 2,
|
||||
GTK_FILL | GTK_EXPAND | GTK_SHRINK, 0, 0, 0);
|
||||
gtk_widget_show(kernel);
|
||||
|
||||
master = gtk_file_chooser_button_new("Select master filesystem",
|
||||
GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER);
|
||||
gtk_table_attach(GTK_TABLE(table), master, 1, 2, 2, 3,
|
||||
GTK_FILL | GTK_EXPAND | GTK_SHRINK, 0, 0, 0);
|
||||
gtk_widget_show(master);
|
||||
|
||||
args = gtk_entry_new();
|
||||
gtk_table_attach(GTK_TABLE(table), args, 1, 2, 3, 4,
|
||||
GTK_FILL | GTK_EXPAND | GTK_SHRINK, 0, 0, 0);
|
||||
gtk_widget_show(args);
|
||||
|
||||
gtk_widget_show(table);
|
||||
|
||||
while (TRUE)
|
||||
{
|
||||
switch (gtk_dialog_run(GTK_DIALOG(dialog)))
|
||||
{
|
||||
case GTK_RESPONSE_ACCEPT:
|
||||
{
|
||||
char *sname, *skernel, *smaster, *sargs;
|
||||
page_t *page;
|
||||
|
||||
sname = (char*)gtk_entry_get_text(GTK_ENTRY(name));
|
||||
skernel = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(kernel));
|
||||
smaster = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(master));
|
||||
sargs = (char*)gtk_entry_get_text(GTK_ENTRY(args));
|
||||
|
||||
if (!sname[0] || !skernel || !smaster)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
guest = dumm->create_guest(dumm, sname, skernel, smaster, sargs);
|
||||
if (!guest)
|
||||
{
|
||||
error_dialog("creating guest failed!");
|
||||
continue;
|
||||
}
|
||||
page = create_page(guest);
|
||||
gtk_notebook_set_current_page(GTK_NOTEBOOK(notebook), page->num);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
gtk_widget_destroy(dialog);
|
||||
}
|
||||
|
||||
/**
|
||||
* main routine, parses args and reads from console
|
||||
*/
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
GtkWidget *menubar, *menu, *menuitem, *vbox;
|
||||
GtkWidget *dummMenu, *guestMenu, *switchMenu;
|
||||
enumerator_t *enumerator;
|
||||
guest_t *guest;
|
||||
|
||||
library_init(NULL, "dumm");
|
||||
gtk_init(&argc, &argv);
|
||||
|
||||
pages = linked_list_create();
|
||||
dumm = dumm_create(NULL);
|
||||
|
||||
/* setup window */
|
||||
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
|
||||
g_signal_connect(G_OBJECT(window), "destroy", G_CALLBACK(quit), NULL);
|
||||
gtk_window_set_title(GTK_WINDOW (window), "Dumm");
|
||||
gtk_window_set_default_size(GTK_WINDOW (window), 1000, 500);
|
||||
g_signal_connect(G_OBJECT(vte_reaper_get()), "child-exited",
|
||||
G_CALLBACK(child_exited), NULL);
|
||||
|
||||
/* add vbox with menubar, notebook */
|
||||
vbox = gtk_vbox_new(FALSE, 0);
|
||||
gtk_container_add(GTK_CONTAINER(window), vbox);
|
||||
menubar = gtk_menu_bar_new();
|
||||
gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, TRUE, 0);
|
||||
notebook = gtk_notebook_new();
|
||||
g_object_set(G_OBJECT(notebook), "homogeneous", TRUE, NULL);
|
||||
gtk_notebook_set_tab_pos(GTK_NOTEBOOK(notebook), GTK_POS_BOTTOM);
|
||||
gtk_container_add(GTK_CONTAINER(vbox), notebook);
|
||||
|
||||
/* Dumm menu */
|
||||
menu = gtk_menu_new();
|
||||
dummMenu = gtk_menu_item_new_with_mnemonic("_Dumm");
|
||||
gtk_menu_bar_append(GTK_MENU_BAR(menubar), dummMenu);
|
||||
gtk_widget_show(dummMenu);
|
||||
gtk_menu_item_set_submenu(GTK_MENU_ITEM(dummMenu), menu);
|
||||
|
||||
/* Dumm -> exit */
|
||||
menuitem = gtk_image_menu_item_new_from_stock(GTK_STOCK_QUIT, NULL);
|
||||
g_signal_connect(G_OBJECT(menuitem), "activate",
|
||||
G_CALLBACK(quit), NULL);
|
||||
gtk_menu_append(GTK_MENU(menu), menuitem);
|
||||
gtk_widget_show(menuitem);
|
||||
|
||||
/* Guest menu */
|
||||
menu = gtk_menu_new();
|
||||
guestMenu = gtk_menu_item_new_with_mnemonic("_Guest");
|
||||
gtk_menu_bar_append(GTK_MENU_BAR(menubar), guestMenu);
|
||||
gtk_widget_show(guestMenu);
|
||||
gtk_menu_item_set_submenu(GTK_MENU_ITEM(guestMenu), menu);
|
||||
|
||||
/* Guest -> new */
|
||||
menuitem = gtk_image_menu_item_new_from_stock(GTK_STOCK_NEW, NULL);
|
||||
g_signal_connect(G_OBJECT(menuitem), "activate",
|
||||
G_CALLBACK(create_guest), NULL);
|
||||
gtk_menu_append(GTK_MENU(menu), menuitem);
|
||||
gtk_widget_show(menuitem);
|
||||
|
||||
/* Guest -> delete */
|
||||
menuitem = gtk_image_menu_item_new_from_stock(GTK_STOCK_DELETE, NULL);
|
||||
g_signal_connect(G_OBJECT(menuitem), "activate",
|
||||
G_CALLBACK(delete_guest), NULL);
|
||||
gtk_menu_append(GTK_MENU(menu), menuitem);
|
||||
gtk_widget_show(menuitem);
|
||||
|
||||
menuitem = gtk_separator_menu_item_new();
|
||||
gtk_menu_append(GTK_MENU(menu), menuitem);
|
||||
gtk_widget_show(menuitem);
|
||||
|
||||
/* Guest -> start */
|
||||
menuitem = gtk_menu_item_new_with_mnemonic("_Start");
|
||||
g_signal_connect(G_OBJECT(menuitem), "activate",
|
||||
G_CALLBACK(start_guest), NULL);
|
||||
gtk_menu_append(GTK_MENU(menu), menuitem);
|
||||
gtk_widget_show(menuitem);
|
||||
|
||||
/* Guest -> startall */
|
||||
menuitem = gtk_menu_item_new_with_mnemonic("Start _all");
|
||||
g_signal_connect(G_OBJECT(menuitem), "activate",
|
||||
G_CALLBACK(start_all_guests), NULL);
|
||||
gtk_menu_append(GTK_MENU(menu), menuitem);
|
||||
gtk_widget_show(menuitem);
|
||||
|
||||
/* Guest -> stop */
|
||||
menuitem = gtk_image_menu_item_new_from_stock(GTK_STOCK_STOP, NULL);
|
||||
g_signal_connect(G_OBJECT(menuitem), "activate",
|
||||
G_CALLBACK(stop_guest), NULL);
|
||||
gtk_menu_append(GTK_MENU(menu), menuitem);
|
||||
gtk_widget_show(menuitem);
|
||||
|
||||
menuitem = gtk_separator_menu_item_new();
|
||||
gtk_menu_append(GTK_MENU(menu), menuitem);
|
||||
gtk_widget_show(menuitem);
|
||||
|
||||
/* Guest -> connect */
|
||||
menuitem = gtk_image_menu_item_new_from_stock(GTK_STOCK_CONNECT, NULL);
|
||||
g_signal_connect(G_OBJECT(menuitem), "activate",
|
||||
G_CALLBACK(connect_guest), NULL);
|
||||
gtk_menu_append(GTK_MENU(menu), menuitem);
|
||||
gtk_widget_show(menuitem);
|
||||
|
||||
/* Guest -> disconnect */
|
||||
menuitem = gtk_image_menu_item_new_from_stock(GTK_STOCK_DISCONNECT, NULL);
|
||||
g_signal_connect(G_OBJECT(menuitem), "activate",
|
||||
G_CALLBACK(disconnect_guest), NULL);
|
||||
gtk_menu_append(GTK_MENU(menu), menuitem);
|
||||
gtk_widget_set_sensitive(menuitem, FALSE);
|
||||
gtk_widget_show(menuitem);
|
||||
|
||||
/* Switch menu */
|
||||
menu = gtk_menu_new();
|
||||
switchMenu = gtk_menu_item_new_with_mnemonic("_Switch");
|
||||
gtk_menu_bar_append(GTK_MENU_BAR(menubar), switchMenu);
|
||||
gtk_widget_show(switchMenu);
|
||||
gtk_menu_item_set_submenu(GTK_MENU_ITEM(switchMenu), menu);
|
||||
|
||||
/* Switch -> new */
|
||||
menuitem = gtk_image_menu_item_new_from_stock(GTK_STOCK_NEW, NULL);
|
||||
g_signal_connect(G_OBJECT(menuitem), "activate",
|
||||
G_CALLBACK(create_switch), NULL);
|
||||
gtk_menu_append(GTK_MENU(menu), menuitem);
|
||||
gtk_widget_show(menuitem);
|
||||
|
||||
/* Switch -> delete */
|
||||
menuitem = gtk_image_menu_item_new_from_stock(GTK_STOCK_DELETE, NULL);
|
||||
g_signal_connect(G_OBJECT(menuitem), "activate",
|
||||
G_CALLBACK(delete_switch), NULL);
|
||||
gtk_menu_append(GTK_MENU(menu), menuitem);
|
||||
gtk_widget_set_sensitive(menuitem, FALSE);
|
||||
gtk_widget_show(menuitem);
|
||||
|
||||
/* show widgets */
|
||||
gtk_widget_show(menubar);
|
||||
gtk_widget_show(notebook);
|
||||
gtk_widget_show(vbox);
|
||||
gtk_widget_show(window);
|
||||
|
||||
/* fill notebook with guests */
|
||||
enumerator = dumm->create_guest_enumerator(dumm);
|
||||
while (enumerator->enumerate(enumerator, (void**)&guest))
|
||||
{
|
||||
create_page(guest);
|
||||
}
|
||||
enumerator->destroy(enumerator);
|
||||
|
||||
gtk_main();
|
||||
|
||||
dumm->destroy(dumm);
|
||||
pages->destroy_function(pages, g_free);
|
||||
|
||||
library_deinit();
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1,353 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2007 Martin Willi
|
||||
* HSR Hochschule fuer Technik Rapperswil
|
||||
* Copyright (C) 2001-2004 Jeff Dike
|
||||
*
|
||||
* Based on the "uml_mconsole" utility from Jeff Dike.
|
||||
*
|
||||
* 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 <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/socket.h>
|
||||
#include <errno.h>
|
||||
#include <sys/un.h>
|
||||
|
||||
#include <utils/debug.h>
|
||||
|
||||
#include "mconsole.h"
|
||||
|
||||
#define MCONSOLE_MAGIC 0xcafebabe
|
||||
#define MCONSOLE_VERSION 2
|
||||
#define MCONSOLE_MAX_DATA 512
|
||||
|
||||
typedef struct private_mconsole_t private_mconsole_t;
|
||||
|
||||
struct private_mconsole_t {
|
||||
/** public interface */
|
||||
mconsole_t public;
|
||||
/** mconsole socket */
|
||||
int console;
|
||||
/** notify socket */
|
||||
int notify;
|
||||
/** address of uml socket */
|
||||
struct sockaddr_un uml;
|
||||
/** idle function */
|
||||
void (*idle)(void);
|
||||
};
|
||||
|
||||
/**
|
||||
* mconsole message format from "arch/um/include/mconsole.h"
|
||||
*/
|
||||
typedef struct mconsole_request mconsole_request;
|
||||
/** mconsole request message */
|
||||
struct mconsole_request {
|
||||
uint32_t magic;
|
||||
uint32_t version;
|
||||
uint32_t len;
|
||||
char data[MCONSOLE_MAX_DATA];
|
||||
};
|
||||
|
||||
|
||||
typedef struct mconsole_reply mconsole_reply;
|
||||
/** mconsole reply message */
|
||||
struct mconsole_reply {
|
||||
uint32_t err;
|
||||
uint32_t more;
|
||||
uint32_t len;
|
||||
char data[MCONSOLE_MAX_DATA];
|
||||
};
|
||||
|
||||
typedef struct mconsole_notify mconsole_notify;
|
||||
/** mconsole notify message */
|
||||
struct mconsole_notify {
|
||||
uint32_t magic;
|
||||
uint32_t version;
|
||||
enum {
|
||||
MCONSOLE_SOCKET,
|
||||
MCONSOLE_PANIC,
|
||||
MCONSOLE_HANG,
|
||||
MCONSOLE_USER_NOTIFY,
|
||||
} type;
|
||||
uint32_t len;
|
||||
char data[MCONSOLE_MAX_DATA];
|
||||
};
|
||||
|
||||
/**
|
||||
* send a request to UML using mconsole
|
||||
*/
|
||||
static int request(private_mconsole_t *this, void(*cb)(void*,char*,size_t),
|
||||
void *data, char *command, ...)
|
||||
{
|
||||
mconsole_request request;
|
||||
mconsole_reply reply;
|
||||
int len, flags = 0;
|
||||
va_list args;
|
||||
|
||||
memset(&request, 0, sizeof(request));
|
||||
request.magic = MCONSOLE_MAGIC;
|
||||
request.version = MCONSOLE_VERSION;
|
||||
va_start(args, command);
|
||||
request.len = vsnprintf(request.data, sizeof(request.data), command, args);
|
||||
va_end(args);
|
||||
|
||||
if (this->idle)
|
||||
{
|
||||
flags = MSG_DONTWAIT;
|
||||
}
|
||||
do
|
||||
{
|
||||
if (this->idle)
|
||||
{
|
||||
this->idle();
|
||||
}
|
||||
len = sendto(this->console, &request, sizeof(request), flags,
|
||||
(struct sockaddr*)&this->uml, sizeof(this->uml));
|
||||
}
|
||||
while (len < 0 && (errno == EINTR || errno == EAGAIN));
|
||||
|
||||
if (len < 0)
|
||||
{
|
||||
DBG1(DBG_LIB, "sending mconsole command to UML failed: %m");
|
||||
return -1;
|
||||
}
|
||||
do
|
||||
{
|
||||
len = recv(this->console, &reply, sizeof(reply), flags);
|
||||
if (len < 0 && (errno == EINTR || errno == EAGAIN))
|
||||
{
|
||||
if (this->idle)
|
||||
{
|
||||
this->idle();
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (len < 0)
|
||||
{
|
||||
DBG1(DBG_LIB, "receiving from mconsole failed: %m");
|
||||
return -1;
|
||||
}
|
||||
if (len > 0)
|
||||
{
|
||||
if (cb)
|
||||
{
|
||||
cb(data, reply.data, reply.len);
|
||||
}
|
||||
else if (reply.err)
|
||||
{
|
||||
if (reply.len && *reply.data)
|
||||
{
|
||||
DBG1(DBG_LIB, "received mconsole error %d: %.*s",
|
||||
reply.err, (int)reply.len, reply.data);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
while (reply.more);
|
||||
|
||||
return reply.err;
|
||||
}
|
||||
|
||||
/**
|
||||
* ignore error message
|
||||
*/
|
||||
static void ignore(void *data, char *buf, size_t len)
|
||||
{
|
||||
}
|
||||
|
||||
METHOD(mconsole_t, add_iface, bool,
|
||||
private_mconsole_t *this, char *guest, char *host)
|
||||
{
|
||||
int tries = 0;
|
||||
|
||||
while (tries++ < 5)
|
||||
{
|
||||
if (request(this, ignore, NULL, "config %s=tuntap,%s", guest, host) == 0)
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
usleep(10000 * tries * tries);
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
METHOD(mconsole_t, del_iface, bool,
|
||||
private_mconsole_t *this, char *guest)
|
||||
{
|
||||
if (request(this, NULL, NULL, "remove %s", guest) != 0)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
METHOD(mconsole_t, exec, int,
|
||||
private_mconsole_t *this, void(*cb)(void*,char*,size_t), void *data,
|
||||
char *cmd)
|
||||
{
|
||||
return request(this, cb, data, "%s", cmd);
|
||||
}
|
||||
|
||||
/**
|
||||
* Poll until guest is ready
|
||||
*/
|
||||
static void wait_bootup(private_mconsole_t *this)
|
||||
{
|
||||
/* wait for init process to appear */
|
||||
while (request(this, ignore, NULL, "exec ps -p 1 > /dev/null"))
|
||||
{
|
||||
if (this->idle)
|
||||
{
|
||||
this->idle();
|
||||
}
|
||||
usleep(100000);
|
||||
}
|
||||
}
|
||||
|
||||
METHOD(mconsole_t, destroy, void,
|
||||
private_mconsole_t *this)
|
||||
{
|
||||
close(this->console);
|
||||
close(this->notify);
|
||||
free(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* setup the mconsole notify connection and wait for its readiness
|
||||
*/
|
||||
static bool wait_for_notify(private_mconsole_t *this, char *nsock)
|
||||
{
|
||||
struct sockaddr_un addr;
|
||||
mconsole_notify notify;
|
||||
int len, flags = 0;
|
||||
|
||||
this->notify = socket(AF_UNIX, SOCK_DGRAM, 0);
|
||||
if (this->notify < 0)
|
||||
{
|
||||
DBG1(DBG_LIB, "opening mconsole notify socket failed: %m");
|
||||
return FALSE;
|
||||
}
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
addr.sun_family = AF_UNIX;
|
||||
strncpy(addr.sun_path, nsock, sizeof(addr.sun_path));
|
||||
if (bind(this->notify, (struct sockaddr*)&addr, sizeof(addr)) < 0)
|
||||
{
|
||||
DBG1(DBG_LIB, "binding mconsole notify socket to '%s' failed: %m",
|
||||
nsock);
|
||||
close(this->notify);
|
||||
return FALSE;
|
||||
}
|
||||
if (this->idle)
|
||||
{
|
||||
flags = MSG_DONTWAIT;
|
||||
}
|
||||
do
|
||||
{
|
||||
if (this->idle)
|
||||
{
|
||||
this->idle();
|
||||
}
|
||||
len = recvfrom(this->notify, ¬ify, sizeof(notify), flags, NULL, 0);
|
||||
}
|
||||
while (len < 0 && (errno == EINTR || errno == EAGAIN));
|
||||
|
||||
if (len < 0 || len >= sizeof(notify))
|
||||
{
|
||||
DBG1(DBG_LIB, "reading from mconsole notify socket failed: %m");
|
||||
close(this->notify);
|
||||
unlink(nsock);
|
||||
return FALSE;
|
||||
}
|
||||
if (notify.magic != MCONSOLE_MAGIC ||
|
||||
notify.version != MCONSOLE_VERSION ||
|
||||
notify.type != MCONSOLE_SOCKET)
|
||||
{
|
||||
DBG1(DBG_LIB, "received unexpected message from mconsole notify"
|
||||
" socket: %b", ¬ify, sizeof(notify));
|
||||
close(this->notify);
|
||||
unlink(nsock);
|
||||
return FALSE;
|
||||
}
|
||||
memset(&this->uml, 0, sizeof(this->uml));
|
||||
this->uml.sun_family = AF_UNIX;
|
||||
strncpy(this->uml.sun_path, (char*)¬ify.data, sizeof(this->uml.sun_path));
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* setup the mconsole console connection
|
||||
*/
|
||||
static bool setup_console(private_mconsole_t *this)
|
||||
{
|
||||
struct sockaddr_un addr;
|
||||
|
||||
this->console = socket(AF_UNIX, SOCK_DGRAM, 0);
|
||||
if (this->console < 0)
|
||||
{
|
||||
DBG1(DBG_LIB, "opening mconsole socket failed: %m");
|
||||
return FALSE;
|
||||
}
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
addr.sun_family = AF_UNIX;
|
||||
snprintf(&addr.sun_path[1], sizeof(addr.sun_path)-1, "%5d-%d",
|
||||
getpid(), this->console);
|
||||
if (bind(this->console, (struct sockaddr*)&addr, sizeof(addr)) < 0)
|
||||
{
|
||||
DBG1(DBG_LIB, "binding mconsole socket to '%s' failed: %m",
|
||||
&addr.sun_path[1]);
|
||||
close(this->console);
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* create the mconsole instance
|
||||
*/
|
||||
mconsole_t *mconsole_create(char *notify, void(*idle)(void))
|
||||
{
|
||||
private_mconsole_t *this;
|
||||
|
||||
INIT(this,
|
||||
.public = {
|
||||
.add_iface = _add_iface,
|
||||
.del_iface = _del_iface,
|
||||
.exec = _exec,
|
||||
.destroy = _destroy,
|
||||
},
|
||||
.idle = idle,
|
||||
);
|
||||
|
||||
if (!wait_for_notify(this, notify))
|
||||
{
|
||||
free(this);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!setup_console(this))
|
||||
{
|
||||
close(this->notify);
|
||||
unlink(notify);
|
||||
free(this);
|
||||
return NULL;
|
||||
}
|
||||
unlink(notify);
|
||||
|
||||
wait_bootup(this);
|
||||
|
||||
return &this->public;
|
||||
}
|
||||
|
||||
@@ -1,75 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef MCONSOLE_H
|
||||
#define MCONSOLE_H
|
||||
|
||||
#include <library.h>
|
||||
|
||||
typedef struct mconsole_t mconsole_t;
|
||||
|
||||
/**
|
||||
* UML mconsole, change running UML configuration using mconsole.
|
||||
*/
|
||||
struct mconsole_t {
|
||||
|
||||
/**
|
||||
* Create a guest interface and connect it to tap host interface.
|
||||
*
|
||||
* @param guest name of the interface to create in the guest
|
||||
* @param host name of the tap device to connect guest to
|
||||
* @return TRUE if interface created
|
||||
*/
|
||||
bool (*add_iface)(mconsole_t *this, char *guest, char *host);
|
||||
|
||||
/**
|
||||
* Delete a guest interface.
|
||||
*
|
||||
* @param guest name of the interface to delete on the guest
|
||||
* @return TRUE if interface deleted
|
||||
*/
|
||||
bool (*del_iface)(mconsole_t *this, char *guest);
|
||||
|
||||
/**
|
||||
* Execute a command on the mconsole.
|
||||
*
|
||||
* @param cb callback function to invoke for each line
|
||||
* @param data data to pass to callback
|
||||
* @param cmd command to invoke
|
||||
* @return return value of command
|
||||
*/
|
||||
int (*exec)(mconsole_t *this, void(*cb)(void*,char*,size_t), void *data,
|
||||
char *cmd);
|
||||
|
||||
/**
|
||||
* Destroy the mconsole instance
|
||||
*/
|
||||
void (*destroy) (mconsole_t *this);
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a new mconsole connection to a guest.
|
||||
*
|
||||
* Waits for a notification from the guest through the notify socket and tries
|
||||
* to connect to the mconsole socket supplied in the received notification.
|
||||
*
|
||||
* @param notify unix notify socket path
|
||||
* @param idle idle function to call while waiting for responses
|
||||
* @return mconsole instance, or NULL if failed
|
||||
*/
|
||||
mconsole_t *mconsole_create(char *notify, void(*idle)(void));
|
||||
|
||||
#endif /* MCONSOLE_H */
|
||||
|
||||
@@ -1,335 +0,0 @@
|
||||
--- a/arch/um/drivers/mconsole_kern.c 2008-07-13 23:51:29.000000000 +0200
|
||||
+++ b/arch/um/drivers/mconsole_kern.c 2008-08-06 09:32:52.000000000 +0200
|
||||
@@ -4,6 +4,7 @@
|
||||
* Licensed under the GPL
|
||||
*/
|
||||
|
||||
+#include "linux/kmod.h"
|
||||
#include <linux/console.h>
|
||||
#include <linux/ctype.h>
|
||||
#include <linux/interrupt.h>
|
||||
@@ -18,6 +19,8 @@
|
||||
#include <linux/utsname.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/mutex.h>
|
||||
+#include <linux/completion.h>
|
||||
+#include <linux/file.h>
|
||||
#include <asm/uaccess.h>
|
||||
|
||||
#include "init.h"
|
||||
@@ -199,6 +202,65 @@
|
||||
}
|
||||
#endif
|
||||
|
||||
+void mconsole_exec(struct mc_request *req)
|
||||
+{
|
||||
+ DECLARE_COMPLETION_ONSTACK(done);
|
||||
+ struct subprocess_info *sub_info;
|
||||
+ int res, len;
|
||||
+ struct file *out;
|
||||
+ char buf[MCONSOLE_MAX_DATA];
|
||||
+
|
||||
+ char *envp[] = {
|
||||
+ "HOME=/", "TERM=linux",
|
||||
+ "PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin",
|
||||
+ NULL
|
||||
+ };
|
||||
+ char *argv[] = {
|
||||
+ "/bin/sh", "-c",
|
||||
+ req->request.data + strlen("exec "),
|
||||
+ NULL
|
||||
+ };
|
||||
+
|
||||
+ sub_info = call_usermodehelper_setup("/bin/sh", argv, envp);
|
||||
+ if (sub_info == NULL) {
|
||||
+ mconsole_reply(req, "call_usermodehelper_setup failed", 1, 0);
|
||||
+ return;
|
||||
+ }
|
||||
+ res = call_usermodehelper_stdoutpipe(sub_info, &out);
|
||||
+ if (res < 0) {
|
||||
+ call_usermodehelper_freeinfo(sub_info);
|
||||
+ mconsole_reply(req, "call_usermodehelper_stdoutpipe failed", 1, 0);
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ call_usermodehelper_setcomplete(sub_info, &done);
|
||||
+
|
||||
+ res = call_usermodehelper_exec(sub_info, UMH_WAIT_EXT);
|
||||
+ if (res < 0) {
|
||||
+ call_usermodehelper_freeinfo(sub_info);
|
||||
+ mconsole_reply(req, "call_usermodehelper_exec failed", 1, 0);
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ for (;;) {
|
||||
+ len = out->f_op->read(out, buf, sizeof(buf), 0);
|
||||
+ if (len < 0) {
|
||||
+ mconsole_reply(req, "reading output failed", 1, 0);
|
||||
+ break;
|
||||
+ }
|
||||
+ if (len == 0)
|
||||
+ break;
|
||||
+ mconsole_reply_len(req, buf, len, 0, 1);
|
||||
+ }
|
||||
+ fput(out);
|
||||
+
|
||||
+ wait_for_completion(&done);
|
||||
+ res = call_usermodehelper_getretval(sub_info) >> 8;
|
||||
+ call_usermodehelper_freeinfo(sub_info);
|
||||
+
|
||||
+ mconsole_reply_len(req, buf, len, res, 0);
|
||||
+}
|
||||
+
|
||||
void mconsole_proc(struct mc_request *req)
|
||||
{
|
||||
char path[64];
|
||||
@@ -270,6 +332,7 @@
|
||||
stop - pause the UML; it will do nothing until it receives a 'go' \n\
|
||||
go - continue the UML after a 'stop' \n\
|
||||
log <string> - make UML enter <string> into the kernel log\n\
|
||||
+ exec <string> - pass <string> to /bin/sh -c synchronously\n\
|
||||
proc <file> - returns the contents of the UML's /proc/<file>\n\
|
||||
stack <pid> - returns the stack of the specified pid\n\
|
||||
"
|
||||
--- a/arch/um/drivers/mconsole_user.c 2008-07-13 23:51:29.000000000 +0200
|
||||
+++ b/arch/um/drivers/mconsole_user.c 2008-07-15 15:41:54.000000000 +0200
|
||||
@@ -32,6 +32,7 @@
|
||||
{ "stop", mconsole_stop, MCONSOLE_PROC },
|
||||
{ "go", mconsole_go, MCONSOLE_INTR },
|
||||
{ "log", mconsole_log, MCONSOLE_INTR },
|
||||
+ { "exec", mconsole_exec, MCONSOLE_PROC },
|
||||
{ "proc", mconsole_proc, MCONSOLE_PROC },
|
||||
{ "stack", mconsole_stack, MCONSOLE_INTR },
|
||||
};
|
||||
--- a/arch/um/include/mconsole.h 2008-07-13 23:51:29.000000000 +0200
|
||||
+++ b/arch/um/include/mconsole.h 2008-07-15 15:41:54.000000000 +0200
|
||||
@@ -85,6 +85,7 @@
|
||||
extern void mconsole_stop(struct mc_request *req);
|
||||
extern void mconsole_go(struct mc_request *req);
|
||||
extern void mconsole_log(struct mc_request *req);
|
||||
+extern void mconsole_exec(struct mc_request *req);
|
||||
extern void mconsole_proc(struct mc_request *req);
|
||||
extern void mconsole_stack(struct mc_request *req);
|
||||
|
||||
--- a/kernel/kmod.c 2008-07-13 23:51:29.000000000 +0200
|
||||
+++ b/kernel/kmod.c 2008-07-31 14:46:31.000000000 +0200
|
||||
@@ -118,6 +118,7 @@
|
||||
struct subprocess_info {
|
||||
struct work_struct work;
|
||||
struct completion *complete;
|
||||
+ struct completion *executed;
|
||||
char *path;
|
||||
char **argv;
|
||||
char **envp;
|
||||
@@ -125,6 +126,7 @@
|
||||
enum umh_wait wait;
|
||||
int retval;
|
||||
struct file *stdin;
|
||||
+ struct file *stdout;
|
||||
void (*cleanup)(char **argv, char **envp);
|
||||
};
|
||||
|
||||
@@ -160,8 +162,26 @@
|
||||
FD_SET(0, fdt->open_fds);
|
||||
FD_CLR(0, fdt->close_on_exec);
|
||||
spin_unlock(&f->file_lock);
|
||||
-
|
||||
- /* and disallow core files too */
|
||||
+ }
|
||||
+ if (sub_info->stdout) {
|
||||
+ struct files_struct *f = current->files;
|
||||
+ struct fdtable *fdt;
|
||||
+
|
||||
+ sys_close(1);
|
||||
+ sys_close(2);
|
||||
+ get_file(sub_info->stdout);
|
||||
+ fd_install(1, sub_info->stdout);
|
||||
+ fd_install(2, sub_info->stdout);
|
||||
+ spin_lock(&f->file_lock);
|
||||
+ fdt = files_fdtable(f);
|
||||
+ FD_SET(1, fdt->open_fds);
|
||||
+ FD_CLR(1, fdt->close_on_exec);
|
||||
+ FD_SET(2, fdt->open_fds);
|
||||
+ FD_CLR(2, fdt->close_on_exec);
|
||||
+ spin_unlock(&f->file_lock);
|
||||
+ }
|
||||
+ if (sub_info->stdin || sub_info->stdout) {
|
||||
+ /* disallow core files */
|
||||
current->signal->rlim[RLIMIT_CORE] = (struct rlimit){0, 0};
|
||||
}
|
||||
|
||||
@@ -243,7 +263,7 @@
|
||||
/* CLONE_VFORK: wait until the usermode helper has execve'd
|
||||
* successfully We need the data structures to stay around
|
||||
* until that is done. */
|
||||
- if (wait == UMH_WAIT_PROC || wait == UMH_NO_WAIT)
|
||||
+ if (wait == UMH_WAIT_PROC || wait == UMH_NO_WAIT || wait == UMH_WAIT_EXT)
|
||||
pid = kernel_thread(wait_for_helper, sub_info,
|
||||
CLONE_FS | CLONE_FILES | SIGCHLD);
|
||||
else
|
||||
@@ -254,6 +274,16 @@
|
||||
case UMH_NO_WAIT:
|
||||
break;
|
||||
|
||||
+ case UMH_WAIT_EXT:
|
||||
+ if (pid > 0) {
|
||||
+ complete(sub_info->executed);
|
||||
+ break;
|
||||
+ }
|
||||
+ sub_info->retval = pid;
|
||||
+ complete(sub_info->executed);
|
||||
+ complete(sub_info->complete);
|
||||
+ break;
|
||||
+
|
||||
case UMH_WAIT_PROC:
|
||||
if (pid > 0)
|
||||
break;
|
||||
@@ -404,6 +434,19 @@
|
||||
}
|
||||
EXPORT_SYMBOL(call_usermodehelper_setcleanup);
|
||||
|
||||
+void call_usermodehelper_setcomplete(struct subprocess_info *info,
|
||||
+ struct completion* complete)
|
||||
+{
|
||||
+ info->complete = complete;
|
||||
+}
|
||||
+EXPORT_SYMBOL(call_usermodehelper_setcomplete);
|
||||
+
|
||||
+int call_usermodehelper_getretval(struct subprocess_info *info)
|
||||
+{
|
||||
+ return info->retval;
|
||||
+}
|
||||
+EXPORT_SYMBOL(call_usermodehelper_getretval);
|
||||
+
|
||||
/**
|
||||
* call_usermodehelper_stdinpipe - set up a pipe to be used for stdin
|
||||
* @sub_info: a subprocess_info returned by call_usermodehelper_setup
|
||||
@@ -433,6 +476,29 @@
|
||||
}
|
||||
EXPORT_SYMBOL(call_usermodehelper_stdinpipe);
|
||||
|
||||
+int call_usermodehelper_stdoutpipe(struct subprocess_info *sub_info,
|
||||
+ struct file **filp)
|
||||
+{
|
||||
+ struct file *f;
|
||||
+
|
||||
+ f = create_write_pipe();
|
||||
+ if (IS_ERR(f))
|
||||
+ return PTR_ERR(f);
|
||||
+ sub_info->stdout = f;
|
||||
+
|
||||
+ f = create_read_pipe(f);
|
||||
+ if (IS_ERR(f)) {
|
||||
+ free_write_pipe(sub_info->stdout);
|
||||
+ sub_info->stdout = NULL;
|
||||
+ return PTR_ERR(f);
|
||||
+ }
|
||||
+ *filp = f;
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+EXPORT_SYMBOL(call_usermodehelper_stdoutpipe);
|
||||
+
|
||||
+
|
||||
/**
|
||||
* call_usermodehelper_exec - start a usermode application
|
||||
* @sub_info: information about the subprocessa
|
||||
@@ -460,15 +526,22 @@
|
||||
goto out;
|
||||
}
|
||||
|
||||
- sub_info->complete = &done;
|
||||
+ if (wait == UMH_WAIT_EXT)
|
||||
+ sub_info->executed = &done;
|
||||
+ else
|
||||
+ sub_info->complete = &done;
|
||||
+
|
||||
sub_info->wait = wait;
|
||||
|
||||
queue_work(khelper_wq, &sub_info->work);
|
||||
if (wait == UMH_NO_WAIT) /* task has freed sub_info */
|
||||
goto unlock;
|
||||
+
|
||||
wait_for_completion(&done);
|
||||
- retval = sub_info->retval;
|
||||
|
||||
+ retval = sub_info->retval;
|
||||
+ if (wait == UMH_WAIT_EXT) /* caller will free sub_info */
|
||||
+ goto unlock;
|
||||
out:
|
||||
call_usermodehelper_freeinfo(sub_info);
|
||||
unlock:
|
||||
--- a/include/linux/kmod.h 2008-07-13 23:51:29.000000000 +0200
|
||||
+++ b/include/linux/kmod.h 2008-07-31 14:43:33.000000000 +0200
|
||||
@@ -38,6 +38,7 @@
|
||||
struct key;
|
||||
struct file;
|
||||
struct subprocess_info;
|
||||
+struct completion;
|
||||
|
||||
/* Allocate a subprocess_info structure */
|
||||
struct subprocess_info *call_usermodehelper_setup(char *path,
|
||||
@@ -48,13 +49,20 @@
|
||||
struct key *session_keyring);
|
||||
int call_usermodehelper_stdinpipe(struct subprocess_info *sub_info,
|
||||
struct file **filp);
|
||||
+int call_usermodehelper_stdoutpipe(struct subprocess_info *sub_info,
|
||||
+ struct file **filp);
|
||||
+void call_usermodehelper_setcomplete(struct subprocess_info *sub_info,
|
||||
+ struct completion *complete);
|
||||
void call_usermodehelper_setcleanup(struct subprocess_info *info,
|
||||
void (*cleanup)(char **argv, char **envp));
|
||||
+int call_usermodehelper_getretval(struct subprocess_info *sub_info);
|
||||
|
||||
enum umh_wait {
|
||||
UMH_NO_WAIT = -1, /* don't wait at all */
|
||||
UMH_WAIT_EXEC = 0, /* wait for the exec, but not the process */
|
||||
UMH_WAIT_PROC = 1, /* wait for the process to complete */
|
||||
+ UMH_WAIT_EXT = 2, /* wait for the exec then return and signal
|
||||
+ when the process is complete */
|
||||
};
|
||||
|
||||
/* Actually execute the sub-process */
|
||||
--- a/arch/um/Makefile 2008-07-13 23:51:29.000000000 +0200
|
||||
+++ b/arch/um/Makefile 2008-07-15 16:27:02.000000000 +0200
|
||||
@@ -77,6 +77,7 @@
|
||||
KERNEL_DEFINES = $(strip -Derrno=kernel_errno -Dsigprocmask=kernel_sigprocmask \
|
||||
-Dmktime=kernel_mktime $(ARCH_KERNEL_DEFINES))
|
||||
KBUILD_CFLAGS += $(KERNEL_DEFINES)
|
||||
+KBUILD_CFLAGS += $(call cc-option,-fno-unit-at-a-time,)
|
||||
|
||||
PHONY += linux
|
||||
|
||||
--- a/arch/um/Makefile-i386 2008-07-13 23:51:29.000000000 +0200
|
||||
+++ b/arch/um/Makefile-i386 2008-07-15 16:23:57.000000000 +0200
|
||||
@@ -32,11 +32,4 @@
|
||||
# an unresolved reference.
|
||||
cflags-y += -ffreestanding
|
||||
|
||||
-# Disable unit-at-a-time mode on pre-gcc-4.0 compilers, it makes gcc use
|
||||
-# a lot more stack due to the lack of sharing of stacklots. Also, gcc
|
||||
-# 4.3.0 needs -funit-at-a-time for extern inline functions.
|
||||
-KBUILD_CFLAGS += $(shell if [ $(call cc-version) -lt 0400 ] ; then \
|
||||
- echo $(call cc-option,-fno-unit-at-a-time); \
|
||||
- else echo $(call cc-option,-funit-at-a-time); fi ;)
|
||||
-
|
||||
KBUILD_CFLAGS += $(cflags-y)
|
||||
--- a/arch/um/Makefile-x86_64 2008-07-13 23:51:29.000000000 +0200
|
||||
+++ b/arch/um/Makefile-x86_64 2008-07-15 16:24:20.000000000 +0200
|
||||
@@ -21,6 +21,3 @@
|
||||
|
||||
LINK-$(CONFIG_LD_SCRIPT_DYN) += -Wl,-rpath,/lib64
|
||||
LINK-y += -m64
|
||||
-
|
||||
-# Do unit-at-a-time unconditionally on x86_64, following the host
|
||||
-KBUILD_CFLAGS += $(call cc-option,-funit-at-a-time)
|
||||
--- a/arch/um/drivers/line.c 2008-07-13 23:51:29.000000000 +0200
|
||||
+++ b/arch/um/drivers/line.c 2008-07-28 15:13:19.000000000 +0200
|
||||
@@ -876,6 +876,6 @@
|
||||
return base;
|
||||
}
|
||||
|
||||
- snprintf(title, len, "%s (%s)", base, umid);
|
||||
+ snprintf(title, len, "%s (%s)", umid, base);
|
||||
return title;
|
||||
}
|
||||
@@ -1,335 +0,0 @@
|
||||
--- a/arch/um/drivers/mconsole_kern.c 2008-07-13 23:51:29.000000000 +0200
|
||||
+++ b/arch/um/drivers/mconsole_kern.c 2008-10-07 11:03:31.000000000 +0200
|
||||
@@ -4,6 +4,7 @@
|
||||
* Licensed under the GPL
|
||||
*/
|
||||
|
||||
+#include "linux/kmod.h"
|
||||
#include <linux/console.h>
|
||||
#include <linux/ctype.h>
|
||||
#include <linux/interrupt.h>
|
||||
@@ -18,6 +19,8 @@
|
||||
#include <linux/utsname.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/mutex.h>
|
||||
+#include <linux/completion.h>
|
||||
+#include <linux/file.h>
|
||||
#include <asm/uaccess.h>
|
||||
|
||||
#include "init.h"
|
||||
@@ -199,6 +202,65 @@
|
||||
}
|
||||
#endif
|
||||
|
||||
+void mconsole_exec(struct mc_request *req)
|
||||
+{
|
||||
+ DECLARE_COMPLETION_ONSTACK(done);
|
||||
+ struct subprocess_info *sub_info;
|
||||
+ int res, len;
|
||||
+ struct file *out;
|
||||
+ char buf[MCONSOLE_MAX_DATA];
|
||||
+
|
||||
+ char *envp[] = {
|
||||
+ "HOME=/", "TERM=linux",
|
||||
+ "PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin",
|
||||
+ NULL
|
||||
+ };
|
||||
+ char *argv[] = {
|
||||
+ "/bin/sh", "-c",
|
||||
+ req->request.data + strlen("exec "),
|
||||
+ NULL
|
||||
+ };
|
||||
+
|
||||
+ sub_info = call_usermodehelper_setup("/bin/sh", argv, envp, GFP_ATOMIC);
|
||||
+ if (sub_info == NULL) {
|
||||
+ mconsole_reply(req, "call_usermodehelper_setup failed", 1, 0);
|
||||
+ return;
|
||||
+ }
|
||||
+ res = call_usermodehelper_stdoutpipe(sub_info, &out);
|
||||
+ if (res < 0) {
|
||||
+ call_usermodehelper_freeinfo(sub_info);
|
||||
+ mconsole_reply(req, "call_usermodehelper_stdoutpipe failed", 1, 0);
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ call_usermodehelper_setcomplete(sub_info, &done);
|
||||
+
|
||||
+ res = call_usermodehelper_exec(sub_info, UMH_WAIT_EXT);
|
||||
+ if (res < 0) {
|
||||
+ call_usermodehelper_freeinfo(sub_info);
|
||||
+ mconsole_reply(req, "call_usermodehelper_exec failed", 1, 0);
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ for (;;) {
|
||||
+ len = out->f_op->read(out, buf, sizeof(buf), 0);
|
||||
+ if (len < 0) {
|
||||
+ mconsole_reply(req, "reading output failed", 1, 0);
|
||||
+ break;
|
||||
+ }
|
||||
+ if (len == 0)
|
||||
+ break;
|
||||
+ mconsole_reply_len(req, buf, len, 0, 1);
|
||||
+ }
|
||||
+ fput(out);
|
||||
+
|
||||
+ wait_for_completion(&done);
|
||||
+ res = call_usermodehelper_getretval(sub_info) >> 8;
|
||||
+ call_usermodehelper_freeinfo(sub_info);
|
||||
+
|
||||
+ mconsole_reply_len(req, buf, len, res, 0);
|
||||
+}
|
||||
+
|
||||
void mconsole_proc(struct mc_request *req)
|
||||
{
|
||||
char path[64];
|
||||
@@ -270,6 +332,7 @@
|
||||
stop - pause the UML; it will do nothing until it receives a 'go' \n\
|
||||
go - continue the UML after a 'stop' \n\
|
||||
log <string> - make UML enter <string> into the kernel log\n\
|
||||
+ exec <string> - pass <string> to /bin/sh -c synchronously\n\
|
||||
proc <file> - returns the contents of the UML's /proc/<file>\n\
|
||||
stack <pid> - returns the stack of the specified pid\n\
|
||||
"
|
||||
--- a/arch/um/drivers/mconsole_user.c 2008-07-13 23:51:29.000000000 +0200
|
||||
+++ b/arch/um/drivers/mconsole_user.c 2008-10-07 11:00:55.000000000 +0200
|
||||
@@ -32,6 +32,7 @@
|
||||
{ "stop", mconsole_stop, MCONSOLE_PROC },
|
||||
{ "go", mconsole_go, MCONSOLE_INTR },
|
||||
{ "log", mconsole_log, MCONSOLE_INTR },
|
||||
+ { "exec", mconsole_exec, MCONSOLE_PROC },
|
||||
{ "proc", mconsole_proc, MCONSOLE_PROC },
|
||||
{ "stack", mconsole_stack, MCONSOLE_INTR },
|
||||
};
|
||||
--- a/arch/um/include/mconsole.h 2008-07-13 23:51:29.000000000 +0200
|
||||
+++ b/arch/um/include/mconsole.h 2008-10-07 11:00:55.000000000 +0200
|
||||
@@ -85,6 +85,7 @@
|
||||
extern void mconsole_stop(struct mc_request *req);
|
||||
extern void mconsole_go(struct mc_request *req);
|
||||
extern void mconsole_log(struct mc_request *req);
|
||||
+extern void mconsole_exec(struct mc_request *req);
|
||||
extern void mconsole_proc(struct mc_request *req);
|
||||
extern void mconsole_stack(struct mc_request *req);
|
||||
|
||||
--- a/kernel/kmod.c 2008-10-07 10:31:12.000000000 +0200
|
||||
+++ b/kernel/kmod.c 2008-10-07 11:07:37.000000000 +0200
|
||||
@@ -118,6 +118,7 @@
|
||||
struct subprocess_info {
|
||||
struct work_struct work;
|
||||
struct completion *complete;
|
||||
+ struct completion *executed;
|
||||
char *path;
|
||||
char **argv;
|
||||
char **envp;
|
||||
@@ -125,6 +126,7 @@
|
||||
enum umh_wait wait;
|
||||
int retval;
|
||||
struct file *stdin;
|
||||
+ struct file *stdout;
|
||||
void (*cleanup)(char **argv, char **envp);
|
||||
};
|
||||
|
||||
@@ -160,8 +162,26 @@
|
||||
FD_SET(0, fdt->open_fds);
|
||||
FD_CLR(0, fdt->close_on_exec);
|
||||
spin_unlock(&f->file_lock);
|
||||
-
|
||||
- /* and disallow core files too */
|
||||
+ }
|
||||
+ if (sub_info->stdout) {
|
||||
+ struct files_struct *f = current->files;
|
||||
+ struct fdtable *fdt;
|
||||
+
|
||||
+ sys_close(1);
|
||||
+ sys_close(2);
|
||||
+ get_file(sub_info->stdout);
|
||||
+ fd_install(1, sub_info->stdout);
|
||||
+ fd_install(2, sub_info->stdout);
|
||||
+ spin_lock(&f->file_lock);
|
||||
+ fdt = files_fdtable(f);
|
||||
+ FD_SET(1, fdt->open_fds);
|
||||
+ FD_CLR(1, fdt->close_on_exec);
|
||||
+ FD_SET(2, fdt->open_fds);
|
||||
+ FD_CLR(2, fdt->close_on_exec);
|
||||
+ spin_unlock(&f->file_lock);
|
||||
+ }
|
||||
+ if (sub_info->stdin || sub_info->stdout) {
|
||||
+ /* disallow core files */
|
||||
current->signal->rlim[RLIMIT_CORE] = (struct rlimit){0, 0};
|
||||
}
|
||||
|
||||
@@ -243,7 +263,7 @@
|
||||
/* CLONE_VFORK: wait until the usermode helper has execve'd
|
||||
* successfully We need the data structures to stay around
|
||||
* until that is done. */
|
||||
- if (wait == UMH_WAIT_PROC || wait == UMH_NO_WAIT)
|
||||
+ if (wait == UMH_WAIT_PROC || wait == UMH_NO_WAIT || wait == UMH_WAIT_EXT)
|
||||
pid = kernel_thread(wait_for_helper, sub_info,
|
||||
CLONE_FS | CLONE_FILES | SIGCHLD);
|
||||
else
|
||||
@@ -254,6 +274,16 @@
|
||||
case UMH_NO_WAIT:
|
||||
break;
|
||||
|
||||
+ case UMH_WAIT_EXT:
|
||||
+ if (pid > 0) {
|
||||
+ complete(sub_info->executed);
|
||||
+ break;
|
||||
+ }
|
||||
+ sub_info->retval = pid;
|
||||
+ complete(sub_info->executed);
|
||||
+ complete(sub_info->complete);
|
||||
+ break;
|
||||
+
|
||||
case UMH_WAIT_PROC:
|
||||
if (pid > 0)
|
||||
break;
|
||||
@@ -405,6 +435,19 @@
|
||||
}
|
||||
EXPORT_SYMBOL(call_usermodehelper_setcleanup);
|
||||
|
||||
+void call_usermodehelper_setcomplete(struct subprocess_info *info,
|
||||
+ struct completion* complete)
|
||||
+{
|
||||
+ info->complete = complete;
|
||||
+}
|
||||
+EXPORT_SYMBOL(call_usermodehelper_setcomplete);
|
||||
+
|
||||
+int call_usermodehelper_getretval(struct subprocess_info *info)
|
||||
+{
|
||||
+ return info->retval;
|
||||
+}
|
||||
+EXPORT_SYMBOL(call_usermodehelper_getretval);
|
||||
+
|
||||
/**
|
||||
* call_usermodehelper_stdinpipe - set up a pipe to be used for stdin
|
||||
* @sub_info: a subprocess_info returned by call_usermodehelper_setup
|
||||
@@ -434,6 +477,29 @@
|
||||
}
|
||||
EXPORT_SYMBOL(call_usermodehelper_stdinpipe);
|
||||
|
||||
+int call_usermodehelper_stdoutpipe(struct subprocess_info *sub_info,
|
||||
+ struct file **filp)
|
||||
+{
|
||||
+ struct file *f;
|
||||
+
|
||||
+ f = create_write_pipe(0);
|
||||
+ if (IS_ERR(f))
|
||||
+ return PTR_ERR(f);
|
||||
+ sub_info->stdout = f;
|
||||
+
|
||||
+ f = create_read_pipe(f, 0);
|
||||
+ if (IS_ERR(f)) {
|
||||
+ free_write_pipe(sub_info->stdout);
|
||||
+ sub_info->stdout = NULL;
|
||||
+ return PTR_ERR(f);
|
||||
+ }
|
||||
+ *filp = f;
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+EXPORT_SYMBOL(call_usermodehelper_stdoutpipe);
|
||||
+
|
||||
+
|
||||
/**
|
||||
* call_usermodehelper_exec - start a usermode application
|
||||
* @sub_info: information about the subprocessa
|
||||
@@ -461,15 +527,22 @@
|
||||
goto out;
|
||||
}
|
||||
|
||||
- sub_info->complete = &done;
|
||||
+ if (wait == UMH_WAIT_EXT)
|
||||
+ sub_info->executed = &done;
|
||||
+ else
|
||||
+ sub_info->complete = &done;
|
||||
+
|
||||
sub_info->wait = wait;
|
||||
|
||||
queue_work(khelper_wq, &sub_info->work);
|
||||
if (wait == UMH_NO_WAIT) /* task has freed sub_info */
|
||||
goto unlock;
|
||||
+
|
||||
wait_for_completion(&done);
|
||||
- retval = sub_info->retval;
|
||||
|
||||
+ retval = sub_info->retval;
|
||||
+ if (wait == UMH_WAIT_EXT) /* caller will free sub_info */
|
||||
+ goto unlock;
|
||||
out:
|
||||
call_usermodehelper_freeinfo(sub_info);
|
||||
unlock:
|
||||
--- a/include/linux/kmod.h 2008-10-07 10:31:11.000000000 +0200
|
||||
+++ b/include/linux/kmod.h 2008-10-07 11:00:55.000000000 +0200
|
||||
@@ -40,6 +40,7 @@
|
||||
struct key;
|
||||
struct file;
|
||||
struct subprocess_info;
|
||||
+struct completion;
|
||||
|
||||
/* Allocate a subprocess_info structure */
|
||||
struct subprocess_info *call_usermodehelper_setup(char *path, char **argv,
|
||||
@@ -50,13 +51,20 @@
|
||||
struct key *session_keyring);
|
||||
int call_usermodehelper_stdinpipe(struct subprocess_info *sub_info,
|
||||
struct file **filp);
|
||||
+int call_usermodehelper_stdoutpipe(struct subprocess_info *sub_info,
|
||||
+ struct file **filp);
|
||||
+void call_usermodehelper_setcomplete(struct subprocess_info *sub_info,
|
||||
+ struct completion *complete);
|
||||
void call_usermodehelper_setcleanup(struct subprocess_info *info,
|
||||
void (*cleanup)(char **argv, char **envp));
|
||||
+int call_usermodehelper_getretval(struct subprocess_info *sub_info);
|
||||
|
||||
enum umh_wait {
|
||||
UMH_NO_WAIT = -1, /* don't wait at all */
|
||||
UMH_WAIT_EXEC = 0, /* wait for the exec, but not the process */
|
||||
UMH_WAIT_PROC = 1, /* wait for the process to complete */
|
||||
+ UMH_WAIT_EXT = 2, /* wait for the exec then return and signal
|
||||
+ when the process is complete */
|
||||
};
|
||||
|
||||
/* Actually execute the sub-process */
|
||||
--- a/arch/um/Makefile 2008-07-13 23:51:29.000000000 +0200
|
||||
+++ b/arch/um/Makefile 2008-10-07 11:00:55.000000000 +0200
|
||||
@@ -77,6 +77,7 @@
|
||||
KERNEL_DEFINES = $(strip -Derrno=kernel_errno -Dsigprocmask=kernel_sigprocmask \
|
||||
-Dmktime=kernel_mktime $(ARCH_KERNEL_DEFINES))
|
||||
KBUILD_CFLAGS += $(KERNEL_DEFINES)
|
||||
+KBUILD_CFLAGS += $(call cc-option,-fno-unit-at-a-time,)
|
||||
|
||||
PHONY += linux
|
||||
|
||||
--- a/arch/um/Makefile-i386 2008-07-13 23:51:29.000000000 +0200
|
||||
+++ b/arch/um/Makefile-i386 2008-10-07 11:00:55.000000000 +0200
|
||||
@@ -32,11 +32,4 @@
|
||||
# an unresolved reference.
|
||||
cflags-y += -ffreestanding
|
||||
|
||||
-# Disable unit-at-a-time mode on pre-gcc-4.0 compilers, it makes gcc use
|
||||
-# a lot more stack due to the lack of sharing of stacklots. Also, gcc
|
||||
-# 4.3.0 needs -funit-at-a-time for extern inline functions.
|
||||
-KBUILD_CFLAGS += $(shell if [ $(call cc-version) -lt 0400 ] ; then \
|
||||
- echo $(call cc-option,-fno-unit-at-a-time); \
|
||||
- else echo $(call cc-option,-funit-at-a-time); fi ;)
|
||||
-
|
||||
KBUILD_CFLAGS += $(cflags-y)
|
||||
--- a/arch/um/Makefile-x86_64 2008-07-13 23:51:29.000000000 +0200
|
||||
+++ b/arch/um/Makefile-x86_64 2008-10-07 11:00:55.000000000 +0200
|
||||
@@ -21,6 +21,3 @@
|
||||
|
||||
LINK-$(CONFIG_LD_SCRIPT_DYN) += -Wl,-rpath,/lib64
|
||||
LINK-y += -m64
|
||||
-
|
||||
-# Do unit-at-a-time unconditionally on x86_64, following the host
|
||||
-KBUILD_CFLAGS += $(call cc-option,-funit-at-a-time)
|
||||
--- a/arch/um/drivers/line.c 2008-10-07 10:31:00.000000000 +0200
|
||||
+++ b/arch/um/drivers/line.c 2008-10-07 11:00:55.000000000 +0200
|
||||
@@ -864,6 +864,6 @@
|
||||
return base;
|
||||
}
|
||||
|
||||
- snprintf(title, len, "%s (%s)", base, umid);
|
||||
+ snprintf(title, len, "%s (%s)", umid, base);
|
||||
return title;
|
||||
}
|
||||
@@ -1,302 +0,0 @@
|
||||
--- a/arch/um/drivers/line.c
|
||||
+++ b/arch/um/drivers/line.c
|
||||
@@ -866,6 +866,6 @@ char *add_xterm_umid(char *base)
|
||||
return base;
|
||||
}
|
||||
|
||||
- snprintf(title, len, "%s (%s)", base, umid);
|
||||
+ snprintf(title, len, "%s (%s)", umid, base);
|
||||
return title;
|
||||
}
|
||||
--- a/arch/um/drivers/mconsole_kern.c
|
||||
+++ b/arch/um/drivers/mconsole_kern.c
|
||||
@@ -4,6 +4,7 @@
|
||||
* Licensed under the GPL
|
||||
*/
|
||||
|
||||
+#include "linux/kmod.h"
|
||||
#include <linux/console.h>
|
||||
#include <linux/ctype.h>
|
||||
#include <linux/interrupt.h>
|
||||
@@ -20,6 +21,8 @@
|
||||
#include <linux/un.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/mutex.h>
|
||||
+#include <linux/completion.h>
|
||||
+#include <linux/file.h>
|
||||
#include <asm/uaccess.h>
|
||||
|
||||
#include "init.h"
|
||||
@@ -201,6 +204,65 @@ void mconsole_proc(struct mc_request *req)
|
||||
}
|
||||
#endif
|
||||
|
||||
+void mconsole_exec(struct mc_request *req)
|
||||
+{
|
||||
+ DECLARE_COMPLETION_ONSTACK(done);
|
||||
+ struct subprocess_info *sub_info;
|
||||
+ int res, len;
|
||||
+ struct file *out;
|
||||
+ char buf[MCONSOLE_MAX_DATA];
|
||||
+
|
||||
+ char *envp[] = {
|
||||
+ "HOME=/", "TERM=linux",
|
||||
+ "PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin",
|
||||
+ NULL
|
||||
+ };
|
||||
+ char *argv[] = {
|
||||
+ "/bin/sh", "-c",
|
||||
+ req->request.data + strlen("exec "),
|
||||
+ NULL
|
||||
+ };
|
||||
+
|
||||
+ sub_info = call_usermodehelper_setup("/bin/sh", argv, envp, GFP_ATOMIC);
|
||||
+ if (sub_info == NULL) {
|
||||
+ mconsole_reply(req, "call_usermodehelper_setup failed", 1, 0);
|
||||
+ return;
|
||||
+ }
|
||||
+ res = call_usermodehelper_stdoutpipe(sub_info, &out);
|
||||
+ if (res < 0) {
|
||||
+ call_usermodehelper_freeinfo(sub_info);
|
||||
+ mconsole_reply(req, "call_usermodehelper_stdoutpipe failed", 1, 0);
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ call_usermodehelper_setcomplete(sub_info, &done);
|
||||
+
|
||||
+ res = call_usermodehelper_exec(sub_info, UMH_WAIT_EXT);
|
||||
+ if (res < 0) {
|
||||
+ call_usermodehelper_freeinfo(sub_info);
|
||||
+ mconsole_reply(req, "call_usermodehelper_exec failed", 1, 0);
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ for (;;) {
|
||||
+ len = out->f_op->read(out, buf, sizeof(buf), 0);
|
||||
+ if (len < 0) {
|
||||
+ mconsole_reply(req, "reading output failed", 1, 0);
|
||||
+ break;
|
||||
+ }
|
||||
+ if (len == 0)
|
||||
+ break;
|
||||
+ mconsole_reply_len(req, buf, len, 0, 1);
|
||||
+ }
|
||||
+ fput(out);
|
||||
+
|
||||
+ wait_for_completion(&done);
|
||||
+ res = call_usermodehelper_getretval(sub_info) >> 8;
|
||||
+ call_usermodehelper_freeinfo(sub_info);
|
||||
+
|
||||
+ mconsole_reply_len(req, buf, len, res, 0);
|
||||
+}
|
||||
+
|
||||
void mconsole_proc(struct mc_request *req)
|
||||
{
|
||||
char path[64];
|
||||
@@ -272,6 +334,7 @@ void mconsole_proc(struct mc_request *req)
|
||||
stop - pause the UML; it will do nothing until it receives a 'go' \n\
|
||||
go - continue the UML after a 'stop' \n\
|
||||
log <string> - make UML enter <string> into the kernel log\n\
|
||||
+ exec <string> - pass <string> to /bin/sh -c synchronously\n\
|
||||
proc <file> - returns the contents of the UML's /proc/<file>\n\
|
||||
stack <pid> - returns the stack of the specified pid\n\
|
||||
"
|
||||
--- a/arch/um/drivers/mconsole_user.c
|
||||
+++ b/arch/um/drivers/mconsole_user.c
|
||||
@@ -32,6 +32,7 @@ static struct mconsole_command commands[] = {
|
||||
{ "stop", mconsole_stop, MCONSOLE_PROC },
|
||||
{ "go", mconsole_go, MCONSOLE_INTR },
|
||||
{ "log", mconsole_log, MCONSOLE_INTR },
|
||||
+ { "exec", mconsole_exec, MCONSOLE_PROC },
|
||||
{ "proc", mconsole_proc, MCONSOLE_PROC },
|
||||
{ "stack", mconsole_stack, MCONSOLE_INTR },
|
||||
};
|
||||
--- a/arch/um/include/shared/mconsole.h
|
||||
+++ b/arch/um/include/shared/mconsole.h
|
||||
@@ -85,6 +85,7 @@ extern void mconsole_cad(struct mc_request *req);
|
||||
extern void mconsole_stop(struct mc_request *req);
|
||||
extern void mconsole_go(struct mc_request *req);
|
||||
extern void mconsole_log(struct mc_request *req);
|
||||
+extern void mconsole_exec(struct mc_request *req);
|
||||
extern void mconsole_proc(struct mc_request *req);
|
||||
extern void mconsole_stack(struct mc_request *req);
|
||||
|
||||
--- a/include/linux/kmod.h
|
||||
+++ b/include/linux/kmod.h
|
||||
@@ -40,6 +40,7 @@ static inline int request_module(const char * name, ...) { return -ENOSYS; }
|
||||
struct key;
|
||||
struct file;
|
||||
struct subprocess_info;
|
||||
+struct completion;
|
||||
|
||||
/* Allocate a subprocess_info structure */
|
||||
struct subprocess_info *call_usermodehelper_setup(char *path, char **argv,
|
||||
@@ -50,13 +51,20 @@ void call_usermodehelper_setkeys(struct subprocess_info *info,
|
||||
struct key *session_keyring);
|
||||
int call_usermodehelper_stdinpipe(struct subprocess_info *sub_info,
|
||||
struct file **filp);
|
||||
+int call_usermodehelper_stdoutpipe(struct subprocess_info *sub_info,
|
||||
+ struct file **filp);
|
||||
+void call_usermodehelper_setcomplete(struct subprocess_info *sub_info,
|
||||
+ struct completion *complete);
|
||||
void call_usermodehelper_setcleanup(struct subprocess_info *info,
|
||||
void (*cleanup)(char **argv, char **envp));
|
||||
+int call_usermodehelper_getretval(struct subprocess_info *sub_info);
|
||||
|
||||
enum umh_wait {
|
||||
UMH_NO_WAIT = -1, /* don't wait at all */
|
||||
UMH_WAIT_EXEC = 0, /* wait for the exec, but not the process */
|
||||
UMH_WAIT_PROC = 1, /* wait for the process to complete */
|
||||
+ UMH_WAIT_EXT = 2, /* wait for the exec then return and signal
|
||||
+ when the process is complete */
|
||||
};
|
||||
|
||||
/* Actually execute the sub-process */
|
||||
--- a/kernel/kmod.c
|
||||
+++ b/kernel/kmod.c
|
||||
@@ -118,6 +118,7 @@ EXPORT_SYMBOL(request_module);
|
||||
struct subprocess_info {
|
||||
struct work_struct work;
|
||||
struct completion *complete;
|
||||
+ struct completion *executed;
|
||||
char *path;
|
||||
char **argv;
|
||||
char **envp;
|
||||
@@ -125,6 +126,7 @@ struct subprocess_info {
|
||||
enum umh_wait wait;
|
||||
int retval;
|
||||
struct file *stdin;
|
||||
+ struct file *stdout;
|
||||
void (*cleanup)(char **argv, char **envp);
|
||||
};
|
||||
|
||||
@@ -160,8 +162,26 @@ static int ____call_usermodehelper(void *data)
|
||||
FD_SET(0, fdt->open_fds);
|
||||
FD_CLR(0, fdt->close_on_exec);
|
||||
spin_unlock(&f->file_lock);
|
||||
-
|
||||
- /* and disallow core files too */
|
||||
+ }
|
||||
+ if (sub_info->stdout) {
|
||||
+ struct files_struct *f = current->files;
|
||||
+ struct fdtable *fdt;
|
||||
+
|
||||
+ sys_close(1);
|
||||
+ sys_close(2);
|
||||
+ get_file(sub_info->stdout);
|
||||
+ fd_install(1, sub_info->stdout);
|
||||
+ fd_install(2, sub_info->stdout);
|
||||
+ spin_lock(&f->file_lock);
|
||||
+ fdt = files_fdtable(f);
|
||||
+ FD_SET(1, fdt->open_fds);
|
||||
+ FD_CLR(1, fdt->close_on_exec);
|
||||
+ FD_SET(2, fdt->open_fds);
|
||||
+ FD_CLR(2, fdt->close_on_exec);
|
||||
+ spin_unlock(&f->file_lock);
|
||||
+ }
|
||||
+ if (sub_info->stdin || sub_info->stdout) {
|
||||
+ /* disallow core files */
|
||||
current->signal->rlim[RLIMIT_CORE] = (struct rlimit){0, 0};
|
||||
}
|
||||
|
||||
@@ -243,7 +263,7 @@ static void __call_usermodehelper(struct work_struct *work)
|
||||
/* CLONE_VFORK: wait until the usermode helper has execve'd
|
||||
* successfully We need the data structures to stay around
|
||||
* until that is done. */
|
||||
- if (wait == UMH_WAIT_PROC || wait == UMH_NO_WAIT)
|
||||
+ if (wait == UMH_WAIT_PROC || wait == UMH_NO_WAIT || wait == UMH_WAIT_EXT)
|
||||
pid = kernel_thread(wait_for_helper, sub_info,
|
||||
CLONE_FS | CLONE_FILES | SIGCHLD);
|
||||
else
|
||||
@@ -254,6 +274,16 @@ static void __call_usermodehelper(struct work_struct *work)
|
||||
case UMH_NO_WAIT:
|
||||
break;
|
||||
|
||||
+ case UMH_WAIT_EXT:
|
||||
+ if (pid > 0) {
|
||||
+ complete(sub_info->executed);
|
||||
+ break;
|
||||
+ }
|
||||
+ sub_info->retval = pid;
|
||||
+ complete(sub_info->executed);
|
||||
+ complete(sub_info->complete);
|
||||
+ break;
|
||||
+
|
||||
case UMH_WAIT_PROC:
|
||||
if (pid > 0)
|
||||
break;
|
||||
@@ -397,6 +427,19 @@ void call_usermodehelper_setcleanup(struct subprocess_info *info,
|
||||
}
|
||||
EXPORT_SYMBOL(call_usermodehelper_setcleanup);
|
||||
|
||||
+void call_usermodehelper_setcomplete(struct subprocess_info *info,
|
||||
+ struct completion* complete)
|
||||
+{
|
||||
+ info->complete = complete;
|
||||
+}
|
||||
+EXPORT_SYMBOL(call_usermodehelper_setcomplete);
|
||||
+
|
||||
+int call_usermodehelper_getretval(struct subprocess_info *info)
|
||||
+{
|
||||
+ return info->retval;
|
||||
+}
|
||||
+EXPORT_SYMBOL(call_usermodehelper_getretval);
|
||||
+
|
||||
/**
|
||||
* call_usermodehelper_stdinpipe - set up a pipe to be used for stdin
|
||||
* @sub_info: a subprocess_info returned by call_usermodehelper_setup
|
||||
@@ -426,6 +469,29 @@ int call_usermodehelper_stdinpipe(struct subprocess_info *sub_info,
|
||||
}
|
||||
EXPORT_SYMBOL(call_usermodehelper_stdinpipe);
|
||||
|
||||
+int call_usermodehelper_stdoutpipe(struct subprocess_info *sub_info,
|
||||
+ struct file **filp)
|
||||
+{
|
||||
+ struct file *f;
|
||||
+
|
||||
+ f = create_write_pipe(0);
|
||||
+ if (IS_ERR(f))
|
||||
+ return PTR_ERR(f);
|
||||
+ sub_info->stdout = f;
|
||||
+
|
||||
+ f = create_read_pipe(f, 0);
|
||||
+ if (IS_ERR(f)) {
|
||||
+ free_write_pipe(sub_info->stdout);
|
||||
+ sub_info->stdout = NULL;
|
||||
+ return PTR_ERR(f);
|
||||
+ }
|
||||
+ *filp = f;
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+EXPORT_SYMBOL(call_usermodehelper_stdoutpipe);
|
||||
+
|
||||
+
|
||||
/**
|
||||
* call_usermodehelper_exec - start a usermode application
|
||||
* @sub_info: information about the subprocessa
|
||||
@@ -453,15 +519,22 @@ int call_usermodehelper_exec(struct subprocess_info *sub_info,
|
||||
goto out;
|
||||
}
|
||||
|
||||
- sub_info->complete = &done;
|
||||
+ if (wait == UMH_WAIT_EXT)
|
||||
+ sub_info->executed = &done;
|
||||
+ else
|
||||
+ sub_info->complete = &done;
|
||||
+
|
||||
sub_info->wait = wait;
|
||||
|
||||
queue_work(khelper_wq, &sub_info->work);
|
||||
if (wait == UMH_NO_WAIT) /* task has freed sub_info */
|
||||
goto unlock;
|
||||
+
|
||||
wait_for_completion(&done);
|
||||
- retval = sub_info->retval;
|
||||
|
||||
+ retval = sub_info->retval;
|
||||
+ if (wait == UMH_WAIT_EXT) /* caller will free sub_info */
|
||||
+ goto unlock;
|
||||
out:
|
||||
call_usermodehelper_freeinfo(sub_info);
|
||||
unlock:
|
||||
@@ -1,354 +0,0 @@
|
||||
diff --git a/.gitignore b/.gitignore
|
||||
index 869e1a3..978e430 100644
|
||||
--- a/.gitignore
|
||||
+++ b/.gitignore
|
||||
@@ -32,6 +32,7 @@
|
||||
tags
|
||||
TAGS
|
||||
vmlinux
|
||||
+linux
|
||||
System.map
|
||||
Module.markers
|
||||
Module.symvers
|
||||
diff --git a/arch/um/drivers/line.c b/arch/um/drivers/line.c
|
||||
index 14a102e..0acfaeb 100644
|
||||
--- a/arch/um/drivers/line.c
|
||||
+++ b/arch/um/drivers/line.c
|
||||
@@ -866,6 +866,6 @@ char *add_xterm_umid(char *base)
|
||||
return base;
|
||||
}
|
||||
|
||||
- snprintf(title, len, "%s (%s)", base, umid);
|
||||
+ snprintf(title, len, "%s (%s)", umid, base);
|
||||
return title;
|
||||
}
|
||||
diff --git a/arch/um/drivers/mconsole_kern.c b/arch/um/drivers/mconsole_kern.c
|
||||
index e14629c..c488930 100644
|
||||
--- a/arch/um/drivers/mconsole_kern.c
|
||||
+++ b/arch/um/drivers/mconsole_kern.c
|
||||
@@ -4,6 +4,7 @@
|
||||
* Licensed under the GPL
|
||||
*/
|
||||
|
||||
+#include "linux/kmod.h"
|
||||
#include <linux/console.h>
|
||||
#include <linux/ctype.h>
|
||||
#include <linux/interrupt.h>
|
||||
@@ -20,6 +21,8 @@
|
||||
#include <linux/un.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/mutex.h>
|
||||
+#include <linux/completion.h>
|
||||
+#include <linux/file.h>
|
||||
#include <asm/uaccess.h>
|
||||
|
||||
#include "init.h"
|
||||
@@ -202,6 +205,65 @@ void mconsole_proc(struct mc_request *req)
|
||||
}
|
||||
#endif
|
||||
|
||||
+void mconsole_exec(struct mc_request *req)
|
||||
+{
|
||||
+ DECLARE_COMPLETION_ONSTACK(done);
|
||||
+ struct subprocess_info *sub_info;
|
||||
+ int res, len;
|
||||
+ struct file *out;
|
||||
+ char buf[MCONSOLE_MAX_DATA];
|
||||
+
|
||||
+ char *envp[] = {
|
||||
+ "HOME=/", "TERM=linux",
|
||||
+ "PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin",
|
||||
+ NULL
|
||||
+ };
|
||||
+ char *argv[] = {
|
||||
+ "/bin/sh", "-c",
|
||||
+ req->request.data + strlen("exec "),
|
||||
+ NULL
|
||||
+ };
|
||||
+
|
||||
+ sub_info = call_usermodehelper_setup("/bin/sh", argv, envp, GFP_ATOMIC);
|
||||
+ if (sub_info == NULL) {
|
||||
+ mconsole_reply(req, "call_usermodehelper_setup failed", 1, 0);
|
||||
+ return;
|
||||
+ }
|
||||
+ res = call_usermodehelper_stdoutpipe(sub_info, &out);
|
||||
+ if (res < 0) {
|
||||
+ call_usermodehelper_freeinfo(sub_info);
|
||||
+ mconsole_reply(req, "call_usermodehelper_stdoutpipe failed", 1, 0);
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ call_usermodehelper_setcomplete(sub_info, &done);
|
||||
+
|
||||
+ res = call_usermodehelper_exec(sub_info, UMH_WAIT_EXT);
|
||||
+ if (res < 0) {
|
||||
+ call_usermodehelper_freeinfo(sub_info);
|
||||
+ mconsole_reply(req, "call_usermodehelper_exec failed", 1, 0);
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ for (;;) {
|
||||
+ len = out->f_op->read(out, buf, sizeof(buf), &out->f_pos);
|
||||
+ if (len < 0) {
|
||||
+ mconsole_reply(req, "reading output failed", 1, 0);
|
||||
+ break;
|
||||
+ }
|
||||
+ if (len == 0)
|
||||
+ break;
|
||||
+ mconsole_reply_len(req, buf, len, 0, 1);
|
||||
+ }
|
||||
+ fput(out);
|
||||
+
|
||||
+ wait_for_completion(&done);
|
||||
+ res = call_usermodehelper_getretval(sub_info) >> 8;
|
||||
+ call_usermodehelper_freeinfo(sub_info);
|
||||
+
|
||||
+ mconsole_reply_len(req, buf, len, res, 0);
|
||||
+}
|
||||
+
|
||||
void mconsole_proc(struct mc_request *req)
|
||||
{
|
||||
char path[64];
|
||||
@@ -273,6 +335,7 @@ void mconsole_proc(struct mc_request *req)
|
||||
stop - pause the UML; it will do nothing until it receives a 'go' \n\
|
||||
go - continue the UML after a 'stop' \n\
|
||||
log <string> - make UML enter <string> into the kernel log\n\
|
||||
+ exec <string> - pass <string> to /bin/sh -c synchronously\n\
|
||||
proc <file> - returns the contents of the UML's /proc/<file>\n\
|
||||
stack <pid> - returns the stack of the specified pid\n\
|
||||
"
|
||||
diff --git a/arch/um/drivers/mconsole_user.c b/arch/um/drivers/mconsole_user.c
|
||||
index f8cf4c8..019d89f 100644
|
||||
--- a/arch/um/drivers/mconsole_user.c
|
||||
+++ b/arch/um/drivers/mconsole_user.c
|
||||
@@ -32,6 +32,7 @@ static struct mconsole_command commands[] = {
|
||||
{ "stop", mconsole_stop, MCONSOLE_PROC },
|
||||
{ "go", mconsole_go, MCONSOLE_INTR },
|
||||
{ "log", mconsole_log, MCONSOLE_INTR },
|
||||
+ { "exec", mconsole_exec, MCONSOLE_PROC },
|
||||
{ "proc", mconsole_proc, MCONSOLE_PROC },
|
||||
{ "stack", mconsole_stack, MCONSOLE_INTR },
|
||||
};
|
||||
diff --git a/arch/um/include/.gitignore b/arch/um/include/.gitignore
|
||||
new file mode 100644
|
||||
index 0000000..4c14eba
|
||||
--- /dev/null
|
||||
+++ b/arch/um/include/.gitignore
|
||||
@@ -0,0 +1,2 @@
|
||||
+shared/kern_constants.h
|
||||
+shared/user_constants.h
|
||||
diff --git a/arch/um/include/shared/mconsole.h b/arch/um/include/shared/mconsole.h
|
||||
index c139ae1..dfb83b1 100644
|
||||
--- a/arch/um/include/shared/mconsole.h
|
||||
+++ b/arch/um/include/shared/mconsole.h
|
||||
@@ -85,6 +85,7 @@ extern void mconsole_cad(struct mc_request *req);
|
||||
extern void mconsole_stop(struct mc_request *req);
|
||||
extern void mconsole_go(struct mc_request *req);
|
||||
extern void mconsole_log(struct mc_request *req);
|
||||
+extern void mconsole_exec(struct mc_request *req);
|
||||
extern void mconsole_proc(struct mc_request *req);
|
||||
extern void mconsole_stack(struct mc_request *req);
|
||||
|
||||
diff --git a/arch/um/kernel/.gitignore b/arch/um/kernel/.gitignore
|
||||
new file mode 100644
|
||||
index 0000000..803397e
|
||||
--- /dev/null
|
||||
+++ b/arch/um/kernel/.gitignore
|
||||
@@ -0,0 +1,3 @@
|
||||
+config.c
|
||||
+config.tmp
|
||||
+vmlinux.lds
|
||||
diff --git a/arch/um/os-Linux/file.c b/arch/um/os-Linux/file.c
|
||||
index b5afcfd..b598128 100644
|
||||
--- a/arch/um/os-Linux/file.c
|
||||
+++ b/arch/um/os-Linux/file.c
|
||||
@@ -521,6 +521,8 @@ int os_create_unix_socket(const char *file, int len, int close_on_exec)
|
||||
|
||||
addr.sun_family = AF_UNIX;
|
||||
|
||||
+ if (len > sizeof(addr.sun_path))
|
||||
+ len = sizeof(addr.sun_path);
|
||||
snprintf(addr.sun_path, len, "%s", file);
|
||||
|
||||
err = bind(sock, (struct sockaddr *) &addr, sizeof(addr));
|
||||
diff --git a/include/linux/kmod.h b/include/linux/kmod.h
|
||||
index 92213a9..db07018 100644
|
||||
--- a/include/linux/kmod.h
|
||||
+++ b/include/linux/kmod.h
|
||||
@@ -40,6 +40,7 @@ static inline int request_module(const char * name, ...) { return -ENOSYS; }
|
||||
struct key;
|
||||
struct file;
|
||||
struct subprocess_info;
|
||||
+struct completion;
|
||||
|
||||
/* Allocate a subprocess_info structure */
|
||||
struct subprocess_info *call_usermodehelper_setup(char *path, char **argv,
|
||||
@@ -50,13 +51,20 @@ void call_usermodehelper_setkeys(struct subprocess_info *info,
|
||||
struct key *session_keyring);
|
||||
int call_usermodehelper_stdinpipe(struct subprocess_info *sub_info,
|
||||
struct file **filp);
|
||||
+int call_usermodehelper_stdoutpipe(struct subprocess_info *sub_info,
|
||||
+ struct file **filp);
|
||||
+void call_usermodehelper_setcomplete(struct subprocess_info *sub_info,
|
||||
+ struct completion *complete);
|
||||
void call_usermodehelper_setcleanup(struct subprocess_info *info,
|
||||
void (*cleanup)(char **argv, char **envp));
|
||||
+int call_usermodehelper_getretval(struct subprocess_info *sub_info);
|
||||
|
||||
enum umh_wait {
|
||||
UMH_NO_WAIT = -1, /* don't wait at all */
|
||||
UMH_WAIT_EXEC = 0, /* wait for the exec, but not the process */
|
||||
UMH_WAIT_PROC = 1, /* wait for the process to complete */
|
||||
+ UMH_WAIT_EXT = 2, /* wait for the exec then return and signal
|
||||
+ when the process is complete */
|
||||
};
|
||||
|
||||
/* Actually execute the sub-process */
|
||||
diff --git a/kernel/kmod.c b/kernel/kmod.c
|
||||
index a27a5f6..61479bf 100644
|
||||
--- a/kernel/kmod.c
|
||||
+++ b/kernel/kmod.c
|
||||
@@ -119,12 +119,14 @@ struct subprocess_info {
|
||||
struct work_struct work;
|
||||
struct completion *complete;
|
||||
struct cred *cred;
|
||||
+ struct completion *executed;
|
||||
char *path;
|
||||
char **argv;
|
||||
char **envp;
|
||||
enum umh_wait wait;
|
||||
int retval;
|
||||
struct file *stdin;
|
||||
+ struct file *stdout;
|
||||
void (*cleanup)(char **argv, char **envp);
|
||||
};
|
||||
|
||||
@@ -161,8 +163,26 @@ static int ____call_usermodehelper(void *data)
|
||||
FD_SET(0, fdt->open_fds);
|
||||
FD_CLR(0, fdt->close_on_exec);
|
||||
spin_unlock(&f->file_lock);
|
||||
+ }
|
||||
+ if (sub_info->stdout) {
|
||||
+ struct files_struct *f = current->files;
|
||||
+ struct fdtable *fdt;
|
||||
|
||||
- /* and disallow core files too */
|
||||
+ sys_close(1);
|
||||
+ sys_close(2);
|
||||
+ get_file(sub_info->stdout);
|
||||
+ fd_install(1, sub_info->stdout);
|
||||
+ fd_install(2, sub_info->stdout);
|
||||
+ spin_lock(&f->file_lock);
|
||||
+ fdt = files_fdtable(f);
|
||||
+ FD_SET(1, fdt->open_fds);
|
||||
+ FD_CLR(1, fdt->close_on_exec);
|
||||
+ FD_SET(2, fdt->open_fds);
|
||||
+ FD_CLR(2, fdt->close_on_exec);
|
||||
+ spin_unlock(&f->file_lock);
|
||||
+ }
|
||||
+ if (sub_info->stdin || sub_info->stdout) {
|
||||
+ /* disallow core files */
|
||||
current->signal->rlim[RLIMIT_CORE] = (struct rlimit){0, 0};
|
||||
}
|
||||
|
||||
@@ -248,7 +268,7 @@ static void __call_usermodehelper(struct work_struct *work)
|
||||
/* CLONE_VFORK: wait until the usermode helper has execve'd
|
||||
* successfully We need the data structures to stay around
|
||||
* until that is done. */
|
||||
- if (wait == UMH_WAIT_PROC || wait == UMH_NO_WAIT)
|
||||
+ if (wait == UMH_WAIT_PROC || wait == UMH_NO_WAIT || wait == UMH_WAIT_EXT)
|
||||
pid = kernel_thread(wait_for_helper, sub_info,
|
||||
CLONE_FS | CLONE_FILES | SIGCHLD);
|
||||
else
|
||||
@@ -259,6 +279,16 @@ static void __call_usermodehelper(struct work_struct *work)
|
||||
case UMH_NO_WAIT:
|
||||
break;
|
||||
|
||||
+ case UMH_WAIT_EXT:
|
||||
+ if (pid > 0) {
|
||||
+ complete(sub_info->executed);
|
||||
+ break;
|
||||
+ }
|
||||
+ sub_info->retval = pid;
|
||||
+ complete(sub_info->executed);
|
||||
+ complete(sub_info->complete);
|
||||
+ break;
|
||||
+
|
||||
case UMH_WAIT_PROC:
|
||||
if (pid > 0)
|
||||
break;
|
||||
@@ -411,6 +441,19 @@ void call_usermodehelper_setcleanup(struct subprocess_info *info,
|
||||
}
|
||||
EXPORT_SYMBOL(call_usermodehelper_setcleanup);
|
||||
|
||||
+void call_usermodehelper_setcomplete(struct subprocess_info *info,
|
||||
+ struct completion* complete)
|
||||
+{
|
||||
+ info->complete = complete;
|
||||
+}
|
||||
+EXPORT_SYMBOL(call_usermodehelper_setcomplete);
|
||||
+
|
||||
+int call_usermodehelper_getretval(struct subprocess_info *info)
|
||||
+{
|
||||
+ return info->retval;
|
||||
+}
|
||||
+EXPORT_SYMBOL(call_usermodehelper_getretval);
|
||||
+
|
||||
/**
|
||||
* call_usermodehelper_stdinpipe - set up a pipe to be used for stdin
|
||||
* @sub_info: a subprocess_info returned by call_usermodehelper_setup
|
||||
@@ -440,6 +483,29 @@ int call_usermodehelper_stdinpipe(struct subprocess_info *sub_info,
|
||||
}
|
||||
EXPORT_SYMBOL(call_usermodehelper_stdinpipe);
|
||||
|
||||
+int call_usermodehelper_stdoutpipe(struct subprocess_info *sub_info,
|
||||
+ struct file **filp)
|
||||
+{
|
||||
+ struct file *f;
|
||||
+
|
||||
+ f = create_write_pipe(0);
|
||||
+ if (IS_ERR(f))
|
||||
+ return PTR_ERR(f);
|
||||
+ sub_info->stdout = f;
|
||||
+
|
||||
+ f = create_read_pipe(f, 0);
|
||||
+ if (IS_ERR(f)) {
|
||||
+ free_write_pipe(sub_info->stdout);
|
||||
+ sub_info->stdout = NULL;
|
||||
+ return PTR_ERR(f);
|
||||
+ }
|
||||
+ *filp = f;
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+EXPORT_SYMBOL(call_usermodehelper_stdoutpipe);
|
||||
+
|
||||
+
|
||||
/**
|
||||
* call_usermodehelper_exec - start a usermode application
|
||||
* @sub_info: information about the subprocessa
|
||||
@@ -469,15 +535,22 @@ int call_usermodehelper_exec(struct subprocess_info *sub_info,
|
||||
goto out;
|
||||
}
|
||||
|
||||
- sub_info->complete = &done;
|
||||
+ if (wait == UMH_WAIT_EXT)
|
||||
+ sub_info->executed = &done;
|
||||
+ else
|
||||
+ sub_info->complete = &done;
|
||||
+
|
||||
sub_info->wait = wait;
|
||||
|
||||
queue_work(khelper_wq, &sub_info->work);
|
||||
if (wait == UMH_NO_WAIT) /* task has freed sub_info */
|
||||
goto unlock;
|
||||
+
|
||||
wait_for_completion(&done);
|
||||
- retval = sub_info->retval;
|
||||
|
||||
+ retval = sub_info->retval;
|
||||
+ if (wait == UMH_WAIT_EXT) /* caller will free sub_info */
|
||||
+ goto unlock;
|
||||
out:
|
||||
call_usermodehelper_freeinfo(sub_info);
|
||||
unlock:
|
||||
@@ -1,354 +0,0 @@
|
||||
diff --git a/.gitignore b/.gitignore
|
||||
index 51bd99d..7ac27b0 100644
|
||||
--- a/.gitignore
|
||||
+++ b/.gitignore
|
||||
@@ -32,6 +32,7 @@
|
||||
tags
|
||||
TAGS
|
||||
vmlinux
|
||||
+linux
|
||||
System.map
|
||||
Module.markers
|
||||
Module.symvers
|
||||
diff --git a/arch/um/drivers/line.c b/arch/um/drivers/line.c
|
||||
index 14a102e..0acfaeb 100644
|
||||
--- a/arch/um/drivers/line.c
|
||||
+++ b/arch/um/drivers/line.c
|
||||
@@ -866,6 +866,6 @@ char *add_xterm_umid(char *base)
|
||||
return base;
|
||||
}
|
||||
|
||||
- snprintf(title, len, "%s (%s)", base, umid);
|
||||
+ snprintf(title, len, "%s (%s)", umid, base);
|
||||
return title;
|
||||
}
|
||||
diff --git a/arch/um/drivers/mconsole_kern.c b/arch/um/drivers/mconsole_kern.c
|
||||
index e14629c..c488930 100644
|
||||
--- a/arch/um/drivers/mconsole_kern.c
|
||||
+++ b/arch/um/drivers/mconsole_kern.c
|
||||
@@ -4,6 +4,7 @@
|
||||
* Licensed under the GPL
|
||||
*/
|
||||
|
||||
+#include "linux/kmod.h"
|
||||
#include <linux/console.h>
|
||||
#include <linux/ctype.h>
|
||||
#include <linux/interrupt.h>
|
||||
@@ -20,6 +21,8 @@
|
||||
#include <linux/un.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/mutex.h>
|
||||
+#include <linux/completion.h>
|
||||
+#include <linux/file.h>
|
||||
#include <asm/uaccess.h>
|
||||
|
||||
#include "init.h"
|
||||
@@ -202,6 +205,65 @@ void mconsole_proc(struct mc_request *req)
|
||||
}
|
||||
#endif
|
||||
|
||||
+void mconsole_exec(struct mc_request *req)
|
||||
+{
|
||||
+ DECLARE_COMPLETION_ONSTACK(done);
|
||||
+ struct subprocess_info *sub_info;
|
||||
+ int res, len;
|
||||
+ struct file *out;
|
||||
+ char buf[MCONSOLE_MAX_DATA];
|
||||
+
|
||||
+ char *envp[] = {
|
||||
+ "HOME=/", "TERM=linux",
|
||||
+ "PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin",
|
||||
+ NULL
|
||||
+ };
|
||||
+ char *argv[] = {
|
||||
+ "/bin/sh", "-c",
|
||||
+ req->request.data + strlen("exec "),
|
||||
+ NULL
|
||||
+ };
|
||||
+
|
||||
+ sub_info = call_usermodehelper_setup("/bin/sh", argv, envp, GFP_ATOMIC);
|
||||
+ if (sub_info == NULL) {
|
||||
+ mconsole_reply(req, "call_usermodehelper_setup failed", 1, 0);
|
||||
+ return;
|
||||
+ }
|
||||
+ res = call_usermodehelper_stdoutpipe(sub_info, &out);
|
||||
+ if (res < 0) {
|
||||
+ call_usermodehelper_freeinfo(sub_info);
|
||||
+ mconsole_reply(req, "call_usermodehelper_stdoutpipe failed", 1, 0);
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ call_usermodehelper_setcomplete(sub_info, &done);
|
||||
+
|
||||
+ res = call_usermodehelper_exec(sub_info, UMH_WAIT_EXT);
|
||||
+ if (res < 0) {
|
||||
+ call_usermodehelper_freeinfo(sub_info);
|
||||
+ mconsole_reply(req, "call_usermodehelper_exec failed", 1, 0);
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ for (;;) {
|
||||
+ len = out->f_op->read(out, buf, sizeof(buf), &out->f_pos);
|
||||
+ if (len < 0) {
|
||||
+ mconsole_reply(req, "reading output failed", 1, 0);
|
||||
+ break;
|
||||
+ }
|
||||
+ if (len == 0)
|
||||
+ break;
|
||||
+ mconsole_reply_len(req, buf, len, 0, 1);
|
||||
+ }
|
||||
+ fput(out);
|
||||
+
|
||||
+ wait_for_completion(&done);
|
||||
+ res = call_usermodehelper_getretval(sub_info) >> 8;
|
||||
+ call_usermodehelper_freeinfo(sub_info);
|
||||
+
|
||||
+ mconsole_reply_len(req, buf, len, res, 0);
|
||||
+}
|
||||
+
|
||||
void mconsole_proc(struct mc_request *req)
|
||||
{
|
||||
char path[64];
|
||||
@@ -273,6 +335,7 @@ void mconsole_proc(struct mc_request *req)
|
||||
stop - pause the UML; it will do nothing until it receives a 'go' \n\
|
||||
go - continue the UML after a 'stop' \n\
|
||||
log <string> - make UML enter <string> into the kernel log\n\
|
||||
+ exec <string> - pass <string> to /bin/sh -c synchronously\n\
|
||||
proc <file> - returns the contents of the UML's /proc/<file>\n\
|
||||
stack <pid> - returns the stack of the specified pid\n\
|
||||
"
|
||||
diff --git a/arch/um/drivers/mconsole_user.c b/arch/um/drivers/mconsole_user.c
|
||||
index f8cf4c8..019d89f 100644
|
||||
--- a/arch/um/drivers/mconsole_user.c
|
||||
+++ b/arch/um/drivers/mconsole_user.c
|
||||
@@ -32,6 +32,7 @@ static struct mconsole_command commands[] = {
|
||||
{ "stop", mconsole_stop, MCONSOLE_PROC },
|
||||
{ "go", mconsole_go, MCONSOLE_INTR },
|
||||
{ "log", mconsole_log, MCONSOLE_INTR },
|
||||
+ { "exec", mconsole_exec, MCONSOLE_PROC },
|
||||
{ "proc", mconsole_proc, MCONSOLE_PROC },
|
||||
{ "stack", mconsole_stack, MCONSOLE_INTR },
|
||||
};
|
||||
diff --git a/arch/um/include/.gitignore b/arch/um/include/.gitignore
|
||||
new file mode 100644
|
||||
index 0000000..4c14eba
|
||||
--- /dev/null
|
||||
+++ b/arch/um/include/.gitignore
|
||||
@@ -0,0 +1,2 @@
|
||||
+shared/kern_constants.h
|
||||
+shared/user_constants.h
|
||||
diff --git a/arch/um/include/shared/mconsole.h b/arch/um/include/shared/mconsole.h
|
||||
index c139ae1..dfb83b1 100644
|
||||
--- a/arch/um/include/shared/mconsole.h
|
||||
+++ b/arch/um/include/shared/mconsole.h
|
||||
@@ -85,6 +85,7 @@ extern void mconsole_cad(struct mc_request *req);
|
||||
extern void mconsole_stop(struct mc_request *req);
|
||||
extern void mconsole_go(struct mc_request *req);
|
||||
extern void mconsole_log(struct mc_request *req);
|
||||
+extern void mconsole_exec(struct mc_request *req);
|
||||
extern void mconsole_proc(struct mc_request *req);
|
||||
extern void mconsole_stack(struct mc_request *req);
|
||||
|
||||
diff --git a/arch/um/kernel/.gitignore b/arch/um/kernel/.gitignore
|
||||
new file mode 100644
|
||||
index 0000000..803397e
|
||||
--- /dev/null
|
||||
+++ b/arch/um/kernel/.gitignore
|
||||
@@ -0,0 +1,3 @@
|
||||
+config.c
|
||||
+config.tmp
|
||||
+vmlinux.lds
|
||||
diff --git a/arch/um/os-Linux/file.c b/arch/um/os-Linux/file.c
|
||||
index b5afcfd..b598128 100644
|
||||
--- a/arch/um/os-Linux/file.c
|
||||
+++ b/arch/um/os-Linux/file.c
|
||||
@@ -521,6 +521,8 @@ int os_create_unix_socket(const char *file, int len, int close_on_exec)
|
||||
|
||||
addr.sun_family = AF_UNIX;
|
||||
|
||||
+ if (len > sizeof(addr.sun_path))
|
||||
+ len = sizeof(addr.sun_path);
|
||||
snprintf(addr.sun_path, len, "%s", file);
|
||||
|
||||
err = bind(sock, (struct sockaddr *) &addr, sizeof(addr));
|
||||
diff --git a/include/linux/kmod.h b/include/linux/kmod.h
|
||||
index 384ca8b..b59c12e 100644
|
||||
--- a/include/linux/kmod.h
|
||||
+++ b/include/linux/kmod.h
|
||||
@@ -45,6 +45,7 @@ static inline int request_module_nowait(const char *name, ...) { return -ENOSYS;
|
||||
struct key;
|
||||
struct file;
|
||||
struct subprocess_info;
|
||||
+struct completion;
|
||||
|
||||
/* Allocate a subprocess_info structure */
|
||||
struct subprocess_info *call_usermodehelper_setup(char *path, char **argv,
|
||||
@@ -55,13 +56,20 @@ void call_usermodehelper_setkeys(struct subprocess_info *info,
|
||||
struct key *session_keyring);
|
||||
int call_usermodehelper_stdinpipe(struct subprocess_info *sub_info,
|
||||
struct file **filp);
|
||||
+int call_usermodehelper_stdoutpipe(struct subprocess_info *sub_info,
|
||||
+ struct file **filp);
|
||||
+void call_usermodehelper_setcomplete(struct subprocess_info *sub_info,
|
||||
+ struct completion *complete);
|
||||
void call_usermodehelper_setcleanup(struct subprocess_info *info,
|
||||
void (*cleanup)(char **argv, char **envp));
|
||||
+int call_usermodehelper_getretval(struct subprocess_info *sub_info);
|
||||
|
||||
enum umh_wait {
|
||||
UMH_NO_WAIT = -1, /* don't wait at all */
|
||||
UMH_WAIT_EXEC = 0, /* wait for the exec, but not the process */
|
||||
UMH_WAIT_PROC = 1, /* wait for the process to complete */
|
||||
+ UMH_WAIT_EXT = 2, /* wait for the exec then return and signal
|
||||
+ when the process is complete */
|
||||
};
|
||||
|
||||
/* Actually execute the sub-process */
|
||||
diff --git a/kernel/kmod.c b/kernel/kmod.c
|
||||
index 7e95bed..3f86902 100644
|
||||
--- a/kernel/kmod.c
|
||||
+++ b/kernel/kmod.c
|
||||
@@ -121,12 +121,14 @@ struct subprocess_info {
|
||||
struct work_struct work;
|
||||
struct completion *complete;
|
||||
struct cred *cred;
|
||||
+ struct completion *executed;
|
||||
char *path;
|
||||
char **argv;
|
||||
char **envp;
|
||||
enum umh_wait wait;
|
||||
int retval;
|
||||
struct file *stdin;
|
||||
+ struct file *stdout;
|
||||
void (*cleanup)(char **argv, char **envp);
|
||||
};
|
||||
|
||||
@@ -163,8 +165,26 @@ static int ____call_usermodehelper(void *data)
|
||||
FD_SET(0, fdt->open_fds);
|
||||
FD_CLR(0, fdt->close_on_exec);
|
||||
spin_unlock(&f->file_lock);
|
||||
+ }
|
||||
+ if (sub_info->stdout) {
|
||||
+ struct files_struct *f = current->files;
|
||||
+ struct fdtable *fdt;
|
||||
|
||||
- /* and disallow core files too */
|
||||
+ sys_close(1);
|
||||
+ sys_close(2);
|
||||
+ get_file(sub_info->stdout);
|
||||
+ fd_install(1, sub_info->stdout);
|
||||
+ fd_install(2, sub_info->stdout);
|
||||
+ spin_lock(&f->file_lock);
|
||||
+ fdt = files_fdtable(f);
|
||||
+ FD_SET(1, fdt->open_fds);
|
||||
+ FD_CLR(1, fdt->close_on_exec);
|
||||
+ FD_SET(2, fdt->open_fds);
|
||||
+ FD_CLR(2, fdt->close_on_exec);
|
||||
+ spin_unlock(&f->file_lock);
|
||||
+ }
|
||||
+ if (sub_info->stdin || sub_info->stdout) {
|
||||
+ /* disallow core files */
|
||||
current->signal->rlim[RLIMIT_CORE] = (struct rlimit){0, 0};
|
||||
}
|
||||
|
||||
@@ -250,7 +270,7 @@ static void __call_usermodehelper(struct work_struct *work)
|
||||
/* CLONE_VFORK: wait until the usermode helper has execve'd
|
||||
* successfully We need the data structures to stay around
|
||||
* until that is done. */
|
||||
- if (wait == UMH_WAIT_PROC || wait == UMH_NO_WAIT)
|
||||
+ if (wait == UMH_WAIT_PROC || wait == UMH_NO_WAIT || wait == UMH_WAIT_EXT)
|
||||
pid = kernel_thread(wait_for_helper, sub_info,
|
||||
CLONE_FS | CLONE_FILES | SIGCHLD);
|
||||
else
|
||||
@@ -261,6 +281,16 @@ static void __call_usermodehelper(struct work_struct *work)
|
||||
case UMH_NO_WAIT:
|
||||
break;
|
||||
|
||||
+ case UMH_WAIT_EXT:
|
||||
+ if (pid > 0) {
|
||||
+ complete(sub_info->executed);
|
||||
+ break;
|
||||
+ }
|
||||
+ sub_info->retval = pid;
|
||||
+ complete(sub_info->executed);
|
||||
+ complete(sub_info->complete);
|
||||
+ break;
|
||||
+
|
||||
case UMH_WAIT_PROC:
|
||||
if (pid > 0)
|
||||
break;
|
||||
@@ -415,6 +445,19 @@ void call_usermodehelper_setcleanup(struct subprocess_info *info,
|
||||
}
|
||||
EXPORT_SYMBOL(call_usermodehelper_setcleanup);
|
||||
|
||||
+void call_usermodehelper_setcomplete(struct subprocess_info *info,
|
||||
+ struct completion* complete)
|
||||
+{
|
||||
+ info->complete = complete;
|
||||
+}
|
||||
+EXPORT_SYMBOL(call_usermodehelper_setcomplete);
|
||||
+
|
||||
+int call_usermodehelper_getretval(struct subprocess_info *info)
|
||||
+{
|
||||
+ return info->retval;
|
||||
+}
|
||||
+EXPORT_SYMBOL(call_usermodehelper_getretval);
|
||||
+
|
||||
/**
|
||||
* call_usermodehelper_stdinpipe - set up a pipe to be used for stdin
|
||||
* @sub_info: a subprocess_info returned by call_usermodehelper_setup
|
||||
@@ -444,6 +487,29 @@ int call_usermodehelper_stdinpipe(struct subprocess_info *sub_info,
|
||||
}
|
||||
EXPORT_SYMBOL(call_usermodehelper_stdinpipe);
|
||||
|
||||
+int call_usermodehelper_stdoutpipe(struct subprocess_info *sub_info,
|
||||
+ struct file **filp)
|
||||
+{
|
||||
+ struct file *f;
|
||||
+
|
||||
+ f = create_write_pipe(0);
|
||||
+ if (IS_ERR(f))
|
||||
+ return PTR_ERR(f);
|
||||
+ sub_info->stdout = f;
|
||||
+
|
||||
+ f = create_read_pipe(f, 0);
|
||||
+ if (IS_ERR(f)) {
|
||||
+ free_write_pipe(sub_info->stdout);
|
||||
+ sub_info->stdout = NULL;
|
||||
+ return PTR_ERR(f);
|
||||
+ }
|
||||
+ *filp = f;
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+EXPORT_SYMBOL(call_usermodehelper_stdoutpipe);
|
||||
+
|
||||
+
|
||||
/**
|
||||
* call_usermodehelper_exec - start a usermode application
|
||||
* @sub_info: information about the subprocessa
|
||||
@@ -473,15 +539,22 @@ int call_usermodehelper_exec(struct subprocess_info *sub_info,
|
||||
goto out;
|
||||
}
|
||||
|
||||
- sub_info->complete = &done;
|
||||
+ if (wait == UMH_WAIT_EXT)
|
||||
+ sub_info->executed = &done;
|
||||
+ else
|
||||
+ sub_info->complete = &done;
|
||||
+
|
||||
sub_info->wait = wait;
|
||||
|
||||
queue_work(khelper_wq, &sub_info->work);
|
||||
if (wait == UMH_NO_WAIT) /* task has freed sub_info */
|
||||
goto unlock;
|
||||
+
|
||||
wait_for_completion(&done);
|
||||
- retval = sub_info->retval;
|
||||
|
||||
+ retval = sub_info->retval;
|
||||
+ if (wait == UMH_WAIT_EXT) /* caller will free sub_info */
|
||||
+ goto unlock;
|
||||
out:
|
||||
call_usermodehelper_freeinfo(sub_info);
|
||||
unlock:
|
||||
@@ -1,56 +0,0 @@
|
||||
br0 = Bridge.new("br0")
|
||||
br1 = Bridge.new("br1")
|
||||
br2 = Bridge.new("br2")
|
||||
|
||||
alice = Guest["alice"]
|
||||
venus = Guest["venus"]
|
||||
moon = Guest["moon"]
|
||||
carol = Guest["carol"]
|
||||
winnetou = Guest["winnetou"]
|
||||
dave = Guest["dave"]
|
||||
sun = Guest["sun"]
|
||||
bob = Guest["bob"]
|
||||
|
||||
alice.start
|
||||
venus.start
|
||||
moon.start
|
||||
carol.start
|
||||
winnetou.start
|
||||
dave.start
|
||||
sun.start
|
||||
bob.start
|
||||
|
||||
alice.add("eth0").connect(br1).add("10.1.0.10")
|
||||
venus.add("eth0").connect(br1).add("10.1.0.20")
|
||||
moon.add("eth1").connect(br1).add("10.1.0.1")
|
||||
moon.add("eth0").connect(br0).add("192.168.0.1")
|
||||
carol.add("eth0").connect(br0).add("192.168.0.100")
|
||||
winnetou.add("eth0").connect(br0).add("192.168.0.150")
|
||||
dave.add("eth0").connect(br0).add("192.168.0.200")
|
||||
sun.add("eth0").connect(br0).add("192.168.0.2")
|
||||
sun.add("eth1").connect(br2).add("10.2.0.1")
|
||||
bob.add("eth0").connect(br2).add("10.2.0.10")
|
||||
|
||||
alice.exec("ip route add dev eth0 10.1.0.0/16 src 10.1.0.10")
|
||||
venus.exec("ip route add dev eth0 10.1.0.0/16 src 10.1.0.20")
|
||||
moon.exec("ip route add dev eth1 10.1.0.0/16 src 10.1.0.1")
|
||||
moon.exec("ip route add dev eth0 192.168.0.0/24 src 192.168.0.1")
|
||||
carol.exec("ip route add dev eth0 192.168.0.0/24 src 192.168.0.100")
|
||||
winnetou.exec("ip route add dev eth0 192.168.0.0/24 src 192.168.0.150")
|
||||
dave.exec("ip route add dev eth0 192.168.0.0/24 src 192.168.0.200")
|
||||
sun.exec("ip route add dev eth0 192.168.0.0/24 src 192.168.0.2")
|
||||
sun.exec("ip route add dev eth1 10.2.0.0/16 src 10.2.0.1")
|
||||
bob.exec("ip route add dev eth0 10.2.0.0/16 src 10.2.0.10")
|
||||
|
||||
alice.exec("ip route add default via 10.1.0.1")
|
||||
venus.exec("ip route add default via 10.1.0.1")
|
||||
moon.exec("ip route add default via 192.168.0.254")
|
||||
carol.exec("ip route add default via 192.168.0.254")
|
||||
winnetou.exec("ip route add default via 192.168.0.254")
|
||||
dave.exec("ip route add default via 192.168.0.254")
|
||||
sun.exec("ip route add default via 192.168.0.254")
|
||||
bob.exec("ip route add default via 10.2.0.1")
|
||||
|
||||
moon.exec("echo 1 > /proc/sys/net/ipv4/ip_forward")
|
||||
sun.exec("echo 1 > /proc/sys/net/ipv4/ip_forward")
|
||||
|
||||
Reference in New Issue
Block a user