diff options
author | Rene Mayrhofer <rene@mayrhofer.eu.org> | 2008-10-29 11:11:01 +0000 |
---|---|---|
committer | Rene Mayrhofer <rene@mayrhofer.eu.org> | 2008-10-29 11:11:01 +0000 |
commit | 8b80ab5a6950ce6515f477624794defd7531642a (patch) | |
tree | aa8303f3806c5615fbeafc4dc82febe3cd7c24dc /src/dumm | |
parent | db67c87db3c9089ea8d2e14f617bf3d9e2af261f (diff) | |
download | vyos-strongswan-8b80ab5a6950ce6515f477624794defd7531642a.tar.gz vyos-strongswan-8b80ab5a6950ce6515f477624794defd7531642a.zip |
[svn-upgrade] Integrating new upstream version, strongswan (4.2.8)
Diffstat (limited to 'src/dumm')
-rw-r--r-- | src/dumm/Makefile.am | 15 | ||||
-rw-r--r-- | src/dumm/Makefile.in | 38 | ||||
-rw-r--r-- | src/dumm/bridge.c | 22 | ||||
-rw-r--r-- | src/dumm/cowfs.c | 9 | ||||
-rw-r--r-- | src/dumm/dumm.c | 55 | ||||
-rw-r--r-- | src/dumm/dumm.h | 9 | ||||
-rw-r--r-- | src/dumm/ext/README | 8 | ||||
-rw-r--r-- | src/dumm/ext/dumm.c | 633 | ||||
-rw-r--r-- | src/dumm/ext/extconf.rb | 21 | ||||
-rw-r--r-- | src/dumm/ext/lib/dumm.rb | 21 | ||||
-rw-r--r-- | src/dumm/ext/lib/dumm/guest.rb | 40 | ||||
-rw-r--r-- | src/dumm/guest.c | 174 | ||||
-rw-r--r-- | src/dumm/guest.h | 41 | ||||
-rw-r--r-- | src/dumm/iface.c | 112 | ||||
-rw-r--r-- | src/dumm/iface.h | 48 | ||||
-rw-r--r-- | src/dumm/irdumm.c | 60 | ||||
-rw-r--r-- | src/dumm/main.c | 18 | ||||
-rw-r--r-- | src/dumm/mconsole.c | 110 | ||||
-rw-r--r-- | src/dumm/mconsole.h | 11 | ||||
-rw-r--r-- | src/dumm/testing.c | 171 |
20 files changed, 1251 insertions, 365 deletions
diff --git a/src/dumm/Makefile.am b/src/dumm/Makefile.am index 1e47e8907..11d65bba9 100644 --- a/src/dumm/Makefile.am +++ b/src/dumm/Makefile.am @@ -1,14 +1,19 @@ +EXTRA_DIST = ext/dumm.c ext/extconf.rb ext/README \ + ext/lib/dumm.rb ext/lib/dumm/guest.rb + lib_LTLIBRARIES = libdumm.la -ipsec_PROGRAMS = dumm testing +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 -testing_SOURCES = testing.c +irdumm_SOURCES = irdumm.c -libdumm_la_LIBADD = $(top_builddir)/src/libstrongswan/libstrongswan.la -lbridge -lfuse -lutil +libdumm_la_LIBADD = $(top_builddir)/src/libstrongswan/libstrongswan.la \ + -lbridge -lfuse -lutil dumm_LDADD = -ldumm ${gtk_LIBS} -testing_LDADD = -ldumm +irdumm_LDADD = -ldumm -lruby1.8 -INCLUDES = -I$(top_srcdir)/src/libstrongswan ${gtk_CFLAGS} +INCLUDES = -I$(top_srcdir)/src/libstrongswan ${gtk_CFLAGS} \ + -I/usr/lib/ruby/1.8/i486-linux/ AM_CFLAGS = -D_FILE_OFFSET_BITS=64 diff --git a/src/dumm/Makefile.in b/src/dumm/Makefile.in index 7dc53b8c0..333e616d3 100644 --- a/src/dumm/Makefile.in +++ b/src/dumm/Makefile.in @@ -33,7 +33,7 @@ PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ -ipsec_PROGRAMS = dumm$(EXEEXT) testing$(EXEEXT) +ipsec_PROGRAMS = dumm$(EXEEXT) irdumm$(EXEEXT) subdir = src/dumm DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 @@ -62,9 +62,9 @@ am_dumm_OBJECTS = main.$(OBJEXT) dumm_OBJECTS = $(am_dumm_OBJECTS) am__DEPENDENCIES_1 = dumm_DEPENDENCIES = $(am__DEPENDENCIES_1) -am_testing_OBJECTS = testing.$(OBJEXT) -testing_OBJECTS = $(am_testing_OBJECTS) -testing_DEPENDENCIES = +am_irdumm_OBJECTS = irdumm.$(OBJEXT) +irdumm_OBJECTS = $(am_irdumm_OBJECTS) +irdumm_DEPENDENCIES = DEFAULT_INCLUDES = -I.@am__isrc@ depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles @@ -77,9 +77,8 @@ CCLD = $(CC) LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \ $(LDFLAGS) -o $@ -SOURCES = $(libdumm_la_SOURCES) $(dumm_SOURCES) $(testing_SOURCES) -DIST_SOURCES = $(libdumm_la_SOURCES) $(dumm_SOURCES) \ - $(testing_SOURCES) +SOURCES = $(libdumm_la_SOURCES) $(dumm_SOURCES) $(irdumm_SOURCES) +DIST_SOURCES = $(libdumm_la_SOURCES) $(dumm_SOURCES) $(irdumm_SOURCES) ETAGS = etags CTAGS = ctags DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) @@ -198,6 +197,8 @@ localedir = @localedir@ localstatedir = @localstatedir@ mandir = @mandir@ mkdir_p = @mkdir_p@ +nm_CFLAGS = @nm_CFLAGS@ +nm_LIBS = @nm_LIBS@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ piddir = @piddir@ @@ -217,16 +218,23 @@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ xml_CFLAGS = @xml_CFLAGS@ xml_LIBS = @xml_LIBS@ +EXTRA_DIST = ext/dumm.c ext/extconf.rb ext/README \ + ext/lib/dumm.rb ext/lib/dumm/guest.rb + lib_LTLIBRARIES = libdumm.la 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 -testing_SOURCES = testing.c -libdumm_la_LIBADD = $(top_builddir)/src/libstrongswan/libstrongswan.la -lbridge -lfuse -lutil +irdumm_SOURCES = irdumm.c +libdumm_la_LIBADD = $(top_builddir)/src/libstrongswan/libstrongswan.la \ + -lbridge -lfuse -lutil + dumm_LDADD = -ldumm ${gtk_LIBS} -testing_LDADD = -ldumm -INCLUDES = -I$(top_srcdir)/src/libstrongswan ${gtk_CFLAGS} +irdumm_LDADD = -ldumm -lruby1.8 +INCLUDES = -I$(top_srcdir)/src/libstrongswan ${gtk_CFLAGS} \ + -I/usr/lib/ruby/1.8/i486-linux/ + AM_CFLAGS = -D_FILE_OFFSET_BITS=64 all: all-am @@ -321,9 +329,9 @@ clean-ipsecPROGRAMS: dumm$(EXEEXT): $(dumm_OBJECTS) $(dumm_DEPENDENCIES) @rm -f dumm$(EXEEXT) $(LINK) $(dumm_OBJECTS) $(dumm_LDADD) $(LIBS) -testing$(EXEEXT): $(testing_OBJECTS) $(testing_DEPENDENCIES) - @rm -f testing$(EXEEXT) - $(LINK) $(testing_OBJECTS) $(testing_LDADD) $(LIBS) +irdumm$(EXEEXT): $(irdumm_OBJECTS) $(irdumm_DEPENDENCIES) + @rm -f irdumm$(EXEEXT) + $(LINK) $(irdumm_OBJECTS) $(irdumm_LDADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) @@ -336,9 +344,9 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dumm.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/guest.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/iface.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/irdumm.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/main.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mconsole.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/testing.Po@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< diff --git a/src/dumm/bridge.c b/src/dumm/bridge.c index cb8017c78..209e54fc1 100644 --- a/src/dumm/bridge.c +++ b/src/dumm/bridge.c @@ -112,19 +112,23 @@ static bool connect_iface(private_bridge_t *this, iface_t *iface) static int instances = 0; /** - * unregister an interface from bridge - */ -static void unregister(iface_t *iface) -{ - iface->set_bridge(iface, NULL); -} - -/** * Implementation of bridge_t.destroy. */ static void destroy(private_bridge_t *this) { - this->ifaces->invoke_function(this->ifaces, (linked_list_invoke_t)unregister); + 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("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) diff --git a/src/dumm/cowfs.c b/src/dumm/cowfs.c index 4c16c7c5d..88041811e 100644 --- a/src/dumm/cowfs.c +++ b/src/dumm/cowfs.c @@ -709,8 +709,9 @@ static int cowfs_write(const char *path, const char *buf, size_t size, rel(&path); - fd = get_rd(path); - if (fd == this->master_fd) + fd = get_wr(path); + if (fd == this->master_fd || + (this->over_fd > 0 && fd == this->host_fd)) { fd = copy(path); if (fd < 0) @@ -858,13 +859,15 @@ cowfs_t *cowfs_create(char *master, char *host, char *mount) { DBG1("failed to open master filesystem '%s'", master); free(this); + return NULL; } this->host_fd = open(host, O_RDONLY | O_DIRECTORY); - if (this->master_fd < 0) + if (this->host_fd < 0) { DBG1("failed to open host filesystem '%s'", host); close(this->master_fd); free(this); + return NULL; } this->over_fd = -1; diff --git a/src/dumm/dumm.c b/src/dumm/dumm.c index 5db8eaee0..eaefddb60 100644 --- a/src/dumm/dumm.c +++ b/src/dumm/dumm.c @@ -1,4 +1,5 @@ /* + * Copyright (C) 2008 Tobias Brunner * Copyright (C) 2007 Martin Willi * Hochschule fuer Technik Rapperswil * @@ -29,8 +30,6 @@ #define PERME (S_IRWXU | S_IRWXG) #define GUEST_DIR "guests" -#define TEMPLATE_DIR "templates" -#define TEMPLATE_DIR_DIR "diff" typedef struct private_dumm_t private_dumm_t; @@ -41,27 +40,23 @@ struct private_dumm_t { char *dir; /** directory of guests */ char *guest_dir; - /** directory of templates */ - char *template_dir; /** directory of loaded template */ char *template; /** list of managed guests */ linked_list_t *guests; /** list of managed bridges */ linked_list_t *bridges; - /** do not catch signals if we are destroying */ - bool destroying; }; /** * Implementation of dumm_t.create_guest. */ static guest_t* create_guest(private_dumm_t *this, char *name, char *kernel, - char *master, int mem) + char *master, char *args) { guest_t *guest; - guest = guest_create(this->guest_dir, name, kernel, master, mem); + guest = guest_create(this->guest_dir, name, kernel, master, args); if (guest) { this->guests->insert_last(this->guests, guest); @@ -153,45 +148,36 @@ static void clear_template(private_dumm_t *this) /** * Implementation of dumm_t.load_template. */ -static bool load_template(private_dumm_t *this, char *name) +static bool load_template(private_dumm_t *this, char *dir) { enumerator_t *enumerator; guest_t *guest; - char dir[PATH_MAX]; - size_t len; clear_template(this); - if (name == NULL) + if (dir == NULL) { return TRUE; } - - free(this->template); - asprintf(&this->template, "%s/%s", this->template_dir, name); - len = snprintf(dir, sizeof(dir), "%s/%s", this->template, TEMPLATE_DIR_DIR); - if (len < 0 || len >= sizeof(dir)) + if (strlen(dir) > PATH_MAX) { + DBG1("template directory string '%s' is too long", dir); return FALSE; } + this->template = strdup(dir); if (access(this->template, F_OK) != 0) { /* does not exist, create template */ - if (mkdir(this->template, PERME) != 0) + if (!mkdir_p(this->template, PERME)) { DBG1("creating template directory '%s' failed: %m", this->template); return FALSE; } - if (mkdir(dir, PERME) != 0) - { - DBG1("creating template overlay directory '%s' failed: %m", dir); - return FALSE; - } } enumerator = this->guests->create_enumerator(this->guests); while (enumerator->enumerate(enumerator, (void**)&guest)) { - if (!guest->load_template(guest, dir)) + if (!guest->load_template(guest, this->template)) { enumerator->destroy(enumerator); clear_template(this); @@ -219,10 +205,12 @@ static void destroy(private_dumm_t *this) } enumerator->destroy(enumerator); - this->destroying = TRUE; - this->guests->destroy_offset(this->guests, offsetof(guest_t, destroy)); + 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_dir); free(this->template); free(this->dir); free(this); @@ -270,7 +258,7 @@ dumm_t *dumm_create(char *dir) char cwd[PATH_MAX]; private_dumm_t *this = malloc_thing(private_dumm_t); - this->public.create_guest = (guest_t*(*)(dumm_t*,char*,char*,char*,int))create_guest; + this->public.create_guest = (guest_t*(*)(dumm_t*,char*,char*,char*,char*))create_guest; this->public.create_guest_enumerator = (enumerator_t*(*)(dumm_t*))create_guest_enumerator; this->public.delete_guest = (void(*)(dumm_t*,guest_t*))delete_guest; this->public.create_bridge = (bridge_t*(*)(dumm_t*, char *name))create_bridge; @@ -279,8 +267,6 @@ dumm_t *dumm_create(char *dir) this->public.load_template = (bool(*)(dumm_t*, char *name))load_template; this->public.destroy = (void(*)(dumm_t*))destroy; - this->destroying = FALSE; - if (dir && *dir == '/') { this->dir = strdup(dir); @@ -303,7 +289,6 @@ dumm_t *dumm_create(char *dir) } this->template = NULL; asprintf(&this->guest_dir, "%s/%s", this->dir, GUEST_DIR); - asprintf(&this->template_dir, "%s/%s", this->dir, TEMPLATE_DIR); this->guests = linked_list_create(); this->bridges = linked_list_create(); @@ -313,13 +298,7 @@ dumm_t *dumm_create(char *dir) destroy(this); return NULL; } - if (mkdir(this->template_dir, PERME) < 0 && errno != EEXIST) - { - DBG1("creating template directory '%s' failed: %m", this->template_dir); - destroy(this); - return NULL; - } - + load_guests(this); return &this->public; } diff --git a/src/dumm/dumm.h b/src/dumm/dumm.h index 6abf4fc92..f5db0e45b 100644 --- a/src/dumm/dumm.h +++ b/src/dumm/dumm.h @@ -1,4 +1,5 @@ /* + * Copyright (C) 2008 Tobias Brunner * Copyright (C) 2007 Martin Willi * Hochschule fuer Technik Rapperswil * @@ -39,11 +40,11 @@ struct dumm_t { * @param name name of the guest * @param kernel UML kernel to use for guest * @param master mounted read only master filesystem - * @param mem amount of memory for guest, in MB + * @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, int mem); + char *master, char *args); /** * @brief Create an enumerator over all guests. @@ -84,10 +85,10 @@ struct dumm_t { /** * @brief Loads a template, create a new one if it does not exist. * - * @param name name of the template, NULL to close + * @param name dir to the template, NULL to close * @return FALSE if load/create failed */ - bool (*load_template)(dumm_t *this, char *name); + bool (*load_template)(dumm_t *this, char *dir); /** * @brief stop all guests and destroy the modeler diff --git a/src/dumm/ext/README b/src/dumm/ext/README new file mode 100644 index 000000000..270d9d59d --- /dev/null +++ b/src/dumm/ext/README @@ -0,0 +1,8 @@ +DUMM Ruby Extension +=================== + +Build and Install + + $ ruby extconf.rb + $ make + # make install diff --git a/src/dumm/ext/dumm.c b/src/dumm/ext/dumm.c new file mode 100644 index 000000000..97f14ef85 --- /dev/null +++ b/src/dumm/ext/dumm.c @@ -0,0 +1,633 @@ +/* + * Copyright (C) 2008 Tobias Brunner + * Copyright (C) 2008 Martin Willi + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * $Id: dumm.c 4410 2008-10-10 11:20:04Z martin $ + */ + +#include <stdio.h> +#include <signal.h> +#include <unistd.h> +#include <fcntl.h> + +#include <library.h> +#include <dumm.h> +#include <debug.h> + +#undef PACKAGE_NAME +#undef PACKAGE_TARNAME +#undef PACKAGE_VERSION +#undef PACKAGE_STRING +#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); +} + + + +/** + * Guest bindings + */ +static VALUE guest_find(VALUE class, VALUE key) +{ + enumerator_t *enumerator; + guest_t *guest, *found = NULL; + if (TYPE(key) == T_SYMBOL) { + key = rb_convert_type(key, T_STRING, "String", "to_s"); + } + enumerator = dumm->create_guest_enumerator(dumm); + while (enumerator->enumerate(enumerator, &guest)) + { + if (streq(guest->get_name(guest), StringValuePtr(key))) + { + found = guest; + break; + } + } + enumerator->destroy(enumerator); + if (!found) + { + return Qnil; + } + return Data_Wrap_Struct(class, NULL, NULL, found); +} + +static VALUE guest_get(VALUE class, VALUE key) +{ + VALUE guest = guest_find(class, key); + if (NIL_P(guest)) + { + rb_raise(rb_eRuntimeError, "guest not found"); + } + return guest; +} + +static VALUE guest_each(int argc, VALUE *argv, VALUE class) +{ + enumerator_t *enumerator; + guest_t *guest; + + if (!rb_block_given_p()) + { + rb_raise(rb_eArgError, "must be called with a block"); + } + enumerator = dumm->create_guest_enumerator(dumm); + while (enumerator->enumerate(enumerator, &guest)) + { + rb_yield(Data_Wrap_Struct(class, NULL, NULL, guest)); + } + enumerator->destroy(enumerator); + return class; +} + +static VALUE guest_new(VALUE class, VALUE name, VALUE kernel, + VALUE master, VALUE args) +{ + 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"); + } + return Data_Wrap_Struct(class, NULL, NULL, guest); +} + +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); + 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; + 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); + enumerator = guest->create_iface_enumerator(guest); + while (enumerator->enumerate(enumerator, &iface)) + { + rb_yield(Data_Wrap_Struct(rbc_iface, NULL, NULL, iface)); + } + enumerator->destroy(enumerator); + return self; +} + +static VALUE guest_delete(VALUE self) +{ + guest_t *guest; + + Data_Get_Struct(self, guest_t, guest); + dumm->delete_guest(dumm, guest); + return Qnil; +} + +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, "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); +} + +/** + * Bridge binding + */ +static VALUE bridge_get(VALUE class, VALUE key) +{ + enumerator_t *enumerator; + bridge_t *bridge, *found = NULL; + + 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) + { + rb_raise(rb_eRuntimeError, "bridge not found"); + } + return Data_Wrap_Struct(class, NULL, NULL, found); +} + +static VALUE bridge_each(int argc, VALUE *argv, VALUE class) +{ + enumerator_t *enumerator; + bridge_t *bridge; + + if (!rb_block_given_p()) + { + rb_raise(rb_eArgError, "must be called with a block"); + } + enumerator = dumm->create_bridge_enumerator(dumm); + while (enumerator->enumerate(enumerator, &bridge)) + { + rb_yield(Data_Wrap_Struct(class, NULL, NULL, bridge)); + } + enumerator->destroy(enumerator); + 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; + 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); + enumerator = bridge->create_iface_enumerator(bridge); + while (enumerator->enumerate(enumerator, &iface)) + { + rb_yield(Data_Wrap_Struct(rbc_iface, NULL, NULL, iface)); + } + enumerator->destroy(enumerator); + 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_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; + + addr = host_create_from_string(StringValuePtr(name), 0); + if (!addr) + { + rb_raise(rb_eArgError, "invalid IP address"); + } + Data_Get_Struct(self, iface_t, iface); + if (!iface->add_address(iface, addr)) + { + addr->destroy(addr); + rb_raise(rb_eRuntimeError, "adding address failed"); + } + if (rb_block_given_p()) { + rb_yield(self); + iface->delete_address(iface, addr); + } + addr->destroy(addr); + return self; +} + +static VALUE iface_each_addr(int argc, VALUE *argv, VALUE self) +{ + enumerator_t *enumerator; + iface_t *iface; + host_t *addr; + char buf[64]; + + if (!rb_block_given_p()) + { + rb_raise(rb_eArgError, "must be called with a block"); + } + Data_Get_Struct(self, iface_t, iface); + enumerator = iface->create_address_enumerator(iface); + while (enumerator->enumerate(enumerator, &addr)) + { + snprintf(buf, sizeof(buf), "%H", addr); + rb_yield(rb_str_new2(buf)); + } + enumerator->destroy(enumerator); + return self; +} + +static VALUE iface_del_addr(VALUE self, VALUE vaddr) +{ + iface_t *iface; + host_t *addr; + + addr = host_create_from_string(StringValuePtr(vaddr), 0); + if (!addr) + { + rb_raise(rb_eArgError, "invalid IP address"); + } + Data_Get_Struct(self, iface_t, iface); + if (!iface->delete_address(iface, addr)) + { + addr->destroy(addr); + rb_raise(rb_eRuntimeError, "address not found"); + } + if (rb_block_given_p()) { + rb_yield(self); + iface->add_address(iface, addr); + } + 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 void template_init() +{ + rbc_template = rb_define_class_under(rbm_dumm , "Template", rb_cObject); + + rb_define_singleton_method(rbc_template, "load", template_load, 1); + rb_define_singleton_method(rbc_template, "unload", template_unload, 0); +} + +/** + * 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_create(NULL); + + rbm_dumm = rb_define_module("Dumm"); + + 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); +} diff --git a/src/dumm/ext/extconf.rb b/src/dumm/ext/extconf.rb new file mode 100644 index 000000000..136be5c2c --- /dev/null +++ b/src/dumm/ext/extconf.rb @@ -0,0 +1,21 @@ +# +# DUMM for Ruby +# + +require "mkmf" + +dir_config("dumm") + +unless find_header('library.h', '../../libstrongswan') and + find_header('dumm.h', '..') + puts "... failed: one or more header files not found!" + exit +end + +unless find_library('dumm', 'dumm_create') + puts "... failed: 'libdumm' not found!" + exit +end + +create_makefile("dumm") + diff --git a/src/dumm/ext/lib/dumm.rb b/src/dumm/ext/lib/dumm.rb new file mode 100644 index 000000000..2e860ae9f --- /dev/null +++ b/src/dumm/ext/lib/dumm.rb @@ -0,0 +1,21 @@ +=begin + Copyright (C) 2008 Tobias Brunner + 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. + + $Id: dumm.rb 4295 2008-08-27 07:35:20Z tobias $ +=end + +require 'dumm.so' +require 'dumm/guest' + +# vim:sw=2 ts=2 et diff --git a/src/dumm/ext/lib/dumm/guest.rb b/src/dumm/ext/lib/dumm/guest.rb new file mode 100644 index 000000000..bdd0c33d8 --- /dev/null +++ b/src/dumm/ext/lib/dumm/guest.rb @@ -0,0 +1,40 @@ +=begin + Copyright (C) 2008 Tobias Brunner + 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. + + $Id: guest.rb 4295 2008-08-27 07:35:20Z tobias $ +=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 + end +end + +# vim:sw=2 ts=2 et diff --git a/src/dumm/guest.c b/src/dumm/guest.c index 4b2652688..aed2a3e18 100644 --- a/src/dumm/guest.c +++ b/src/dumm/guest.c @@ -1,4 +1,5 @@ /* + * Copyright (C) 2008 Tobias Brunner * Copyright (C) 2007 Martin Willi * Hochschule fuer Technik Rapperswil * @@ -25,6 +26,7 @@ #include <signal.h> #include <dirent.h> #include <termios.h> +#include <stdarg.h> #include <debug.h> #include <utils/linked_list.h> @@ -40,7 +42,7 @@ #define MASTER_DIR "master" #define DIFF_DIR "diff" #define UNION_DIR "union" -#define MEMORY_FILE "mem" +#define ARGS_FILE "args" #define PID_FILE "pid" #define KERNEL_FILE "linux" #define LOG_FILE "boot.log" @@ -58,8 +60,8 @@ struct private_guest_t { int dir; /** directory name of guest */ char *dirname; - /** amount of memory for guest, in MB */ - int mem; + /** additional args to pass to guest */ + char *args; /** pid of guest child process */ int pid; /** state of guest */ @@ -114,7 +116,7 @@ static iface_t* create_iface(private_guest_t *this, char *name) } enumerator->destroy(enumerator); - iface = iface_create(this->name, name, this->mconsole); + iface = iface_create(name, &this->public, this->mconsole); if (iface) { this->ifaces->insert_last(this->ifaces, iface); @@ -212,6 +214,7 @@ static void stop(private_guest_t *this, idle_function_t idle) } } unlinkat(this->dir, PID_FILE, 0); + this->pid = 0; } } @@ -255,15 +258,19 @@ static bool start(private_guest_t *this, invoke_function_t invoke, void* data, 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, "mem=%dM", this->mem); 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) @@ -307,7 +314,7 @@ static bool load_template(private_guest_t *this, char *path) } if (access(dir, F_OK) != 0) { - if (mkdir(dir, PERME) != 0) + if (!mkdir_p(dir, PERME)) { DBG1("creating overlay for guest '%s' failed: %m", this->name); return FALSE; @@ -325,6 +332,119 @@ static bool load_template(private_guest_t *this, char *path) } /** + * 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; +} + +/** + * Implementation of guest_t.exec + */ +static int exec(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); + } + } +} + +/** + * Implementation of guest_t.exec_str + */ +static int exec_str(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; +} + +/** * Implementation of guest_t.sigchild. */ static void sigchild(private_guest_t *this) @@ -373,38 +493,38 @@ static bool mount_unionfs(private_guest_t *this) } /** - * load memory configuration from file + * load args configuration from file */ -int loadmem(private_guest_t *this) +char *loadargs(private_guest_t *this) { FILE *file; - int mem = 0; + char buf[512], *args = NULL; - file = fdopen(openat(this->dir, MEMORY_FILE, O_RDONLY, PERM), "r"); + file = fdopen(openat(this->dir, ARGS_FILE, O_RDONLY, PERM), "r"); if (file) { - if (fscanf(file, "%d", &mem) <= 0) + if (fgets(buf, sizeof(buf), file)) { - mem = 0; + args = strdup(buf); } fclose(file); } - return mem; + return args; } /** - * save memory configuration to file + * save args configuration to file */ -bool savemem(private_guest_t *this, int mem) +bool saveargs(private_guest_t *this, char *args) { FILE *file; bool retval = FALSE; - file = fdopen(openat(this->dir, MEMORY_FILE, O_RDWR | O_CREAT | O_TRUNC, + file = fdopen(openat(this->dir, ARGS_FILE, O_RDWR | O_CREAT | O_TRUNC, PERM), "w"); if (file) { - if (fprintf(file, "%d", mem) > 0) + if (fprintf(file, "%s", args) > 0) { retval = TRUE; } @@ -424,7 +544,9 @@ static void destroy(private_guest_t *this) { close(this->dir); } + this->ifaces->destroy(this->ifaces); free(this->dirname); + free(this->args); free(this->name); free(this); } @@ -447,6 +569,8 @@ static private_guest_t *guest_create_generic(char *parent, char *name, this->public.start = (void*)start; this->public.stop = (void*)stop; this->public.load_template = (bool(*)(guest_t*, char *path))load_template; + this->public.exec = (int(*)(guest_t*, void(*cb)(void*,char*,size_t),void*,char*,...))exec; + this->public.exec_str = (int(*)(guest_t*, void(*cb)(void*,char*),bool,void*,char*,...))exec_str; this->public.sigchild = (void(*)(guest_t*))sigchild; this->public.destroy = (void*)destroy; @@ -474,7 +598,7 @@ static private_guest_t *guest_create_generic(char *parent, char *name, this->state = GUEST_STOPPED; this->mconsole = NULL; this->ifaces = linked_list_create(); - this->mem = 0; + this->args = NULL; this->name = strdup(name); this->cowfs = NULL; @@ -505,7 +629,7 @@ static bool make_symlink(private_guest_t *this, char *old, char *new) * create the guest instance, including required dirs and mounts */ guest_t *guest_create(char *parent, char *name, char *kernel, - char *master, int mem) + char *master, char *args) { private_guest_t *this = guest_create_generic(parent, name, TRUE); @@ -530,8 +654,8 @@ guest_t *guest_create(char *parent, char *name, char *kernel, return NULL; } - this->mem = mem; - if (!savemem(this, mem)) + this->args = args; + if (args && !saveargs(this, args)) { destroy(this); return NULL; @@ -558,13 +682,7 @@ guest_t *guest_load(char *parent, char *name) return NULL; } - this->mem = loadmem(this); - if (this->mem == 0) - { - DBG1("unable to open memory configuration file: %m", name); - destroy(this); - return NULL; - } + this->args = loadargs(this); if (!mount_unionfs(this)) { diff --git a/src/dumm/guest.h b/src/dumm/guest.h index 79a47fa62..0e48b1d06 100644 --- a/src/dumm/guest.h +++ b/src/dumm/guest.h @@ -1,4 +1,5 @@ /* + * Copyright (C) 2008 Tobias Brunner * Copyright (C) 2007 Martin Willi * Hochschule fuer Technik Rapperswil * @@ -19,11 +20,11 @@ #include <library.h> #include <utils/enumerator.h> -#include "iface.h" - typedef enum guest_state_t guest_state_t; typedef struct guest_t guest_t; +#include "iface.h" + /** * @brief State of a guest (started, stopped, ...) */ @@ -107,9 +108,8 @@ struct guest_t { * @brief Kill the guest. * * @param idle idle function to call while waiting to termination - * @return TRUE if guest was running and killed */ - bool (*stop) (guest_t *this, idle_function_t idle); + void (*stop) (guest_t *this, idle_function_t idle); /** * @brief Create a new interface in the current scenario. @@ -140,7 +140,35 @@ struct guest_t { * @return FALSE if failed */ bool (*load_template)(guest_t *this, char *parent); - + + /** + * Execute a command in the guest. + * + * @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 in the guest and return the output by lines or as combined + * string. + * + * @note This function does not work with binary output (i.e. containing 0 bytes). + * + * @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 (instead of for the combined output) + * @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, ...); + /** * @brief Called whenever a SIGCHILD for the guests PID is received. */ @@ -159,10 +187,11 @@ struct guest_t { * @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, int mem); + char *master, char *args); /** * @brief Load a guest created with guest_create(). diff --git a/src/dumm/iface.c b/src/dumm/iface.c index b78c10bec..78c6c7c92 100644 --- a/src/dumm/iface.c +++ b/src/dumm/iface.c @@ -1,4 +1,5 @@ /* + * Copyright (C) 2008 Tobias Brunner * Copyright (C) 2007 Martin Willi * Hochschule fuer Technik Rapperswil * Copyright (C) 2002 Jeff Dike @@ -25,6 +26,7 @@ #include <linux/if_tun.h> #include <debug.h> +#include <utils/linked_list.h> #include "iface.h" @@ -39,12 +41,14 @@ struct private_iface_t { 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 + * bring an interface up or down (host side) */ bool iface_control(char *name, bool up) { @@ -96,14 +100,93 @@ static char* get_hostif(private_iface_t *this) } /** + * Implementation of iface_t.add_address + */ +static bool add_address(private_iface_t *this, host_t *addr) +{ + return (this->guest->exec(this->guest, NULL, NULL, "ip addr add %H dev %s", + addr, 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)); +} + +/** + * Implementation of iface_t.create_address_enumerator + */ +static enumerator_t* create_address_enumerator(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, + "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); +} + +/** + * Implementation of iface_t.delete_address + */ +static bool delete_address(private_iface_t *this, host_t *addr) +{ + return (this->guest->exec(this->guest, NULL, NULL, + "ip addr del %H dev %s", addr, this->guestif) == 0); +} + +/** * Implementation of iface_t.set_bridge. */ static void set_bridge(private_iface_t *this, bridge_t *bridge) { + if (this->bridge == NULL && bridge) + { + this->guest->exec(this->guest, NULL, NULL, + "ip link set %s up", this->guestif); + } + else if (this->bridge && bridge == NULL) + { + this->guest->exec(this->guest, NULL, NULL, + "ip link set %s down", this->guestif); + } this->bridge = bridge; } /** + * Implementation of iface_t.get_bridge + */ +static bridge_t *get_bridge(private_iface_t *this) +{ + return this->bridge; +} + +/** + * Implementation of iface_t.get_guest + */ +static guest_t* get_guest(private_iface_t *this) +{ + return this->guest; +} + +/** * destroy the tap device */ static bool destroy_tap(private_iface_t *this) @@ -139,14 +222,15 @@ static bool destroy_tap(private_iface_t *this) /** * create the tap device */ -static char* create_tap(private_iface_t *this, char *guest) +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", guest, this->guestif); + 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) @@ -175,6 +259,8 @@ static void destroy(private_iface_t *this) { 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); @@ -185,18 +271,24 @@ static void destroy(private_iface_t *this) /** * create the iface instance */ -iface_t *iface_create(char *guest, char *guestif, mconsole_t *mconsole) +iface_t *iface_create(char *name, guest_t *guest, mconsole_t *mconsole) { private_iface_t *this = malloc_thing(private_iface_t); this->public.get_hostif = (char*(*)(iface_t*))get_hostif; this->public.get_guestif = (char*(*)(iface_t*))get_guestif; + this->public.add_address = (bool(*)(iface_t*, host_t *addr))add_address; + this->public.create_address_enumerator = (enumerator_t*(*)(iface_t*))create_address_enumerator; + this->public.delete_address = (bool(*)(iface_t*, host_t *addr))delete_address; this->public.set_bridge = (void(*)(iface_t*, bridge_t*))set_bridge; + this->public.get_bridge = (bridge_t*(*)(iface_t*))get_bridge; + this->public.get_guest = (guest_t*(*)(iface_t*))get_guest; this->public.destroy = (void*)destroy; this->mconsole = mconsole; - this->guestif = strdup(guestif); - this->hostif = create_tap(this, guest); + this->guestif = strdup(name); + this->guest = guest; + this->hostif = create_tap(this); this->bridge = NULL; if (this->hostif == NULL) { @@ -205,10 +297,6 @@ iface_t *iface_create(char *guest, char *guestif, mconsole_t *mconsole) free(this); return NULL; } - if (!iface_control(this->hostif, TRUE)) - { - DBG1("bringing iface '%s' up failed: %m", this->hostif); - } if (!this->mconsole->add_iface(this->mconsole, this->guestif, this->hostif)) { DBG1("creating interface '%s' in guest failed", this->guestif); @@ -218,6 +306,10 @@ iface_t *iface_create(char *guest, char *guestif, mconsole_t *mconsole) free(this); return NULL; } + if (!iface_control(this->hostif, TRUE)) + { + DBG1("bringing iface '%s' up failed: %m", this->hostif); + } return &this->public; } diff --git a/src/dumm/iface.h b/src/dumm/iface.h index e04fe4ed1..54a0554c0 100644 --- a/src/dumm/iface.h +++ b/src/dumm/iface.h @@ -18,6 +18,7 @@ #include <library.h> #include <utils/enumerator.h> +#include <utils/host.h> #define TAP_DEVICE "/dev/net/tun" @@ -25,6 +26,7 @@ typedef struct iface_t iface_t; #include "mconsole.h" #include "bridge.h" +#include "guest.h" /** * @brief Interface in a guest, connected to a tap device on the host. @@ -46,16 +48,48 @@ struct iface_t { char* (*get_hostif)(iface_t *this); /** + * Add an address to the interface. + * + * @param addr address to add to interface + * @return TRUE if address added + */ + bool (*add_address)(iface_t *this, host_t *addr); + + /** + * 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. + * + * @param addr address to remove + * @return TRUE if address removed + */ + bool (*delete_address)(iface_t *this, host_t *addr); + + /** * @brief 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); - /* - bool (*add_addr) (iface_t *this, host_t *addr); - enumerator_t* (*create_addr_enumerator) (iface_t *this); - */ + /** + * @brief Get the bridge this iface is connected, or NULL. + * + * @return connected bridge, or NULL + */ + bridge_t* (*get_bridge)(iface_t *this); + + /** + * @brief Get the guest this iface belongs to. + * + * @return guest of this iface + */ + guest_t* (*get_guest)(iface_t *this); /** * @brief Destroy an interface @@ -66,12 +100,12 @@ struct iface_t { /** * @brief Create a new interface for a guest * - * @param guest name of the guest for this interface - * @param guestif name of the interface in the 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 *guest, char *guestif, mconsole_t *mconsole); +iface_t *iface_create(char *name, guest_t *guest, mconsole_t *mconsole); #endif /* IFACE_H */ diff --git a/src/dumm/irdumm.c b/src/dumm/irdumm.c new file mode 100644 index 000000000..bca8ce1db --- /dev/null +++ b/src/dumm/irdumm.c @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2008 Martin Willi + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#undef PACKAGE_NAME +#undef PACKAGE_TARNAME +#undef PACKAGE_VERSION +#undef PACKAGE_STRING +#include <ruby.h> + +/** + * 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; +} + diff --git a/src/dumm/main.c b/src/dumm/main.c index d4f2c5176..e2f2fc255 100644 --- a/src/dumm/main.c +++ b/src/dumm/main.c @@ -100,7 +100,6 @@ static page_t* get_page(int num) static pid_t invoke(void *vte, guest_t *guest, char *args[], int argc) { - args[argc] = "con0=fd:0,fd:1"; return vte_terminal_fork_command(VTE_TERMINAL(vte), args[0], args, NULL, NULL, FALSE, FALSE, FALSE); } @@ -374,7 +373,7 @@ static page_t* create_page(guest_t *guest) static void create_guest() { guest_t *guest; - GtkWidget *dialog, *table, *label, *name, *kernel, *master, *memory; + 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, @@ -396,7 +395,7 @@ static void create_guest() gtk_table_attach(GTK_TABLE(table), label, 0, 1, 2, 3, 0, 0, 0, 0); gtk_widget_show(label); - label = gtk_label_new("Memory (MB)"); + 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); @@ -417,11 +416,10 @@ static void create_guest() GTK_FILL | GTK_EXPAND | GTK_SHRINK, 0, 0, 0); gtk_widget_show(master); - memory = gtk_spin_button_new_with_range(1, 4096, 1); - gtk_spin_button_set_digits(GTK_SPIN_BUTTON(memory), 0); - gtk_table_attach(GTK_TABLE(table), memory, 1, 2, 3, 4, + 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(memory); + gtk_widget_show(args); gtk_widget_show(table); @@ -431,19 +429,19 @@ static void create_guest() { case GTK_RESPONSE_ACCEPT: { - char *sname, *skernel, *smaster; + 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, - gtk_spin_button_get_value(GTK_SPIN_BUTTON(memory))); + guest = dumm->create_guest(dumm, sname, skernel, smaster, sargs); if (!guest) { error_dialog("creating guest failed!"); diff --git a/src/dumm/mconsole.c b/src/dumm/mconsole.c index d9864f676..c6e0c2f08 100644 --- a/src/dumm/mconsole.c +++ b/src/dumm/mconsole.c @@ -88,21 +88,21 @@ struct mconsole_notify { /** * send a request to UML using mconsole */ -static int request(private_mconsole_t *this, char *command, - char buf[], size_t *size) +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, total = 0, flags = 0; + int len, flags = 0; + va_list args; memset(&request, 0, sizeof(request)); request.magic = MCONSOLE_MAGIC; request.version = MCONSOLE_VERSION; - request.len = min(strlen(command), sizeof(reply.data) - 1); - strncpy(request.data, command, request.len); - *buf = '\0'; - (*size)--; - + va_start(args, command); + request.len = vsnprintf(request.data, sizeof(request.data), command, args); + va_end(args); + if (this->idle) { flags = MSG_DONTWAIT; @@ -120,7 +120,7 @@ static int request(private_mconsole_t *this, char *command, if (len < 0) { - snprintf(buf, *size, "sending mconsole command to UML failed: %m"); + DBG1("sending mconsole command to UML failed: %m"); return -1; } do @@ -136,96 +136,87 @@ static int request(private_mconsole_t *this, char *command, } if (len < 0) { - snprintf(buf, *size, "receiving from mconsole failed: %m"); + DBG1("receiving from mconsole failed: %m"); return -1; } if (len > 0) { - strncat(buf, reply.data, min(reply.len, *size - total)); - total += reply.len; + if (cb) + { + cb(data, reply.data, reply.len); + } + else if (reply.err) + { + DBG1("received mconsole error %d: %*.s", + reply.err, reply.len, reply.data); + break; + } } } while (reply.more); - *size = total; return reply.err; } /** + * ignore error message + */ +static void ignore(void *data, char *buf, size_t len) +{ +} + +/** * Implementation of mconsole_t.add_iface. */ static bool add_iface(private_mconsole_t *this, char *guest, char *host) { - char buf[128]; - int len; + int tries = 0; - len = snprintf(buf, sizeof(buf), "config %s=tuntap,%s", guest, host); - if (len < 0 || len >= sizeof(buf)) - { - return FALSE; - } - len = sizeof(buf); - if (request(this, buf, buf, &len) != 0) + while (tries++ < 5) { - DBG1("adding interface failed: %.*s", len, buf); - return FALSE; + if (request(this, ignore, NULL, "config %s=tuntap,%s", guest, host) == 0) + { + return TRUE; + } + usleep(10000 * tries * tries); } - return TRUE; + return FALSE; } /** * Implementation of mconsole_t.del_iface. */ static bool del_iface(private_mconsole_t *this, char *guest) -{ - char buf[128]; - int len; - - len = snprintf(buf, sizeof(buf), "remove %s", guest); - if (len < 0 || len >= sizeof(buf)) - { - return FALSE; - } - if (request(this, buf, buf, &len) != 0) +{ + if (request(this, NULL, NULL, "remove %s", guest) != 0) { - DBG1("removing interface failed: %.*s", len, buf); return FALSE; } return TRUE; } /** + * Implementation of mconsole_t.exec + */ +static int exec(private_mconsole_t *this, void(*cb)(void*,char*,size_t), + void *data, char *cmd) +{ + return request(this, cb, data, "exec %s", cmd); +} + +/** * Poll until guest is ready */ static bool wait_bootup(private_mconsole_t *this) { - char buf[128]; - int len, res; - - while (TRUE) + /* wait for init process to appear */ + while (request(this, ignore, NULL, "exec ps -p 1 > /dev/null")) { - len = sizeof(buf); - res = request(this, "config eth9=mcast", buf, &len); - if (res < 0) - { - return FALSE; - } - if (res == 0) - { - while (request(this, "remove eth9", buf, &len) != 0) - { - usleep(50000); - } - return TRUE; - } if (this->idle) { this->idle(); } - else - { - usleep(50000); - } + usleep(100000); } } @@ -240,7 +231,7 @@ static void destroy(private_mconsole_t *this) } /** - * setup the mconsole notify connection and wait for its readyness + * setup the mconsole notify connection and wait for its readiness */ static bool wait_for_notify(private_mconsole_t *this, char *nsock) { @@ -335,6 +326,7 @@ mconsole_t *mconsole_create(char *notify, void(*idle)(void)) this->public.add_iface = (bool(*)(mconsole_t*, char *guest, char *host))add_iface; this->public.del_iface = (bool(*)(mconsole_t*, char *guest))del_iface; + this->public.exec = (int(*)(mconsole_t*, void(*cb)(void*,char*,size_t), void *data, char *cmd))exec; this->public.destroy = (void*)destroy; this->idle = idle; diff --git a/src/dumm/mconsole.h b/src/dumm/mconsole.h index 55ce15dda..329c40c06 100644 --- a/src/dumm/mconsole.h +++ b/src/dumm/mconsole.h @@ -43,6 +43,17 @@ struct mconsole_t { bool (*del_iface)(mconsole_t *this, char *guest); /** + * Execute a command in the UML host. + * + * @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); + + /** * @brief Destroy the mconsole instance */ void (*destroy) (mconsole_t *this); diff --git a/src/dumm/testing.c b/src/dumm/testing.c deleted file mode 100644 index c0d23296b..000000000 --- a/src/dumm/testing.c +++ /dev/null @@ -1,171 +0,0 @@ -/* - * Copyright (C) 2008 Martin Willi - * Hochschule fuer Technik Rapperswil - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * for more details. - */ - -#include <stdio.h> -#include <signal.h> -#include <unistd.h> -#include <fcntl.h> - -#include <library.h> -#include <dumm.h> - -/** - * number of running guests - */ -static int running = 0; - -/** - * Guest invocation callback - */ -static pid_t invoke(void *vte, guest_t *guest, - char *args[], int argc) -{ - pid_t pid; - - args[argc] = "con0=xterm"; - - pid = fork(); - switch (pid) - { - case 0: /* child */ - dup2(open("/dev/null", 0), 1); - dup2(open("/dev/null", 0), 2); - execvp(args[0], args); - exit(-1); - case -1: - fprintf(stderr, "starting guest '%s' failed\n", guest->get_name(guest)); - return 0; - default: - printf("started guest '%s', pid: %d\n", guest->get_name(guest), pid); - running++; - return pid; - } -} - -/** - * main routine, parses args and reads from console - */ -int main(int argc, char *argv[]) -{ - dumm_t *dumm; - enumerator_t *enumerator; - guest_t *guest; - bridge_t *switch0, *switch1, *switch2; - iface_t *iface; - sigset_t set; - siginfo_t info; - - library_init(NULL); - - dumm = dumm_create(NULL); - - switch0 = dumm->create_bridge(dumm, "switch0"); - switch1 = dumm->create_bridge(dumm, "switch1"); - switch2 = dumm->create_bridge(dumm, "switch2"); - - if (switch0 && switch1 && switch2) - { - enumerator = dumm->create_guest_enumerator(dumm); - while (enumerator->enumerate(enumerator, &guest)) - { - if (!guest->start(guest, invoke, NULL, NULL)) - { - continue; - } - if (streq(guest->get_name(guest), "alice")) - { - iface = guest->create_iface(guest, "eth0"); - if (iface) - { - switch1->connect_iface(switch1, iface); - } - iface = guest->create_iface(guest, "eth1"); - if (iface) - { - switch0->connect_iface(switch0, iface); - } - } - else if (streq(guest->get_name(guest), "moon") || - streq(guest->get_name(guest), "sun")) - { - iface = guest->create_iface(guest, "eth0"); - if (iface) - { - switch0->connect_iface(switch0, iface); - } - iface = guest->create_iface(guest, "eth1"); - if (iface) - { - switch1->connect_iface(switch1, iface); - } - } - else if (streq(guest->get_name(guest), "bob")) - { - iface = guest->create_iface(guest, "eth0"); - if (iface) - { - switch2->connect_iface(switch2, iface); - } - } - else if (streq(guest->get_name(guest), "venus")) - { - iface = guest->create_iface(guest, "eth0"); - if (iface) - { - switch1->connect_iface(switch1, iface); - } - } - else if (streq(guest->get_name(guest), "carol") || - streq(guest->get_name(guest), "winnetou") || - streq(guest->get_name(guest), "dave")) - { - iface = guest->create_iface(guest, "eth0"); - if (iface) - { - switch0->connect_iface(switch0, iface); - } - } - } - enumerator->destroy(enumerator); - - sigemptyset(&set); - sigaddset(&set, SIGINT); - sigaddset(&set, SIGHUP); - sigaddset(&set, SIGTERM); - sigaddset(&set, SIGCHLD); - sigprocmask(SIG_SETMASK, &set, NULL); - while (running) - { - if (sigwaitinfo(&set, &info) == SIGCHLD) - { - enumerator = dumm->create_guest_enumerator(dumm); - while (enumerator->enumerate(enumerator, &guest)) - { - if (guest->get_pid(guest) == info.si_pid) - { - running--; - guest->sigchild(guest); - break; - } - } - enumerator->destroy(enumerator); - } - } - } - dumm->destroy(dumm); - - library_deinit(); - return 0; -} |