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:
Tobias Brunner
2018-09-12 15:53:55 +02:00
parent 948c42ab2e
commit 66c4735f99
31 changed files with 1 additions and 7088 deletions
-16
View File
@@ -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
View File
@@ -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
-4
View File
@@ -88,10 +88,6 @@ if USE_CONFTEST
SUBDIRS += conftest
endif
if USE_DUMM
SUBDIRS += dumm
endif
if USE_FAST
SUBDIRS += libfast
endif
-2
View File
@@ -1,2 +0,0 @@
dumm
irdumm
-34
View File
@@ -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
-181
View File
@@ -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**)&current))
{
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;
}
-76
View File
@@ -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 */
-980
View File
@@ -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**)&current, &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;
}
-72
View File
@@ -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
View File
@@ -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
View File
@@ -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 */
-3
View File
@@ -1,3 +0,0 @@
dumm.so
mkmf.log
extconf.rb
-8
View File
@@ -1,8 +0,0 @@
DUMM Ruby Extension
===================
Build and Install
$ ruby extconf.rb
$ make
# make install
-797
View File
@@ -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);
}
-19
View File
@@ -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')
-63
View File
@@ -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
-59
View File
@@ -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
-682
View File
@@ -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**)&current))
{
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;
}
-222
View File
@@ -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 */
-299
View File
@@ -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;
}
-115
View File
@@ -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 */
-68
View File
@@ -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
View File
@@ -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;
}
-353
View File
@@ -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, &notify, 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", &notify, 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*)&notify.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;
}
-75
View File
@@ -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 */
-335
View File
@@ -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;
}
-335
View File
@@ -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;
}
-302
View File
@@ -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:
-354
View File
@@ -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:
-354
View File
@@ -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:
-56
View File
@@ -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")