From 02a608363393df08a3153ac779e08ab903ce27c4 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Mon, 7 Jul 2008 14:56:04 +0000 Subject: guest interface/address management using hackish mconsole exec patch, ruby bindings --- src/dumm/guest.c | 25 ++++++++++ src/dumm/guest.h | 11 ++++- src/dumm/iface.c | 70 ++++++++++++++++++++++++++- src/dumm/iface.h | 24 ++++++++++ src/dumm/irdumm.c | 73 +++++++++++++++++++++++++++++ src/dumm/mconsole.c | 22 +++++++++ src/dumm/mconsole.h | 5 ++ src/dumm/patches/mconsole-exec-2.6.26.patch | 63 +++++++++++++++++++++++++ 8 files changed, 291 insertions(+), 2 deletions(-) create mode 100644 src/dumm/patches/mconsole-exec-2.6.26.patch (limited to 'src/dumm') diff --git a/src/dumm/guest.c b/src/dumm/guest.c index 75d0245d8..6aad15058 100644 --- a/src/dumm/guest.c +++ b/src/dumm/guest.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include @@ -324,6 +325,29 @@ static bool load_template(private_guest_t *this, char *path) return TRUE; } +/** + * Implementation of gues_t.exec + */ +static int exec(private_guest_t *this, char *cmd, ...) +{ + char buf[512]; + size_t len; + va_list args; + + if (this->mconsole) + { + va_start(args, cmd); + len = vsnprintf(buf, sizeof(buf), cmd, args); + va_end(args); + + if (len > 0 && len < sizeof(buf)) + { + return this->mconsole->exec(this->mconsole, buf); + } + } + return FALSE; +} + /** * Implementation of guest_t.sigchild. */ @@ -448,6 +472,7 @@ 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 = (bool(*)(guest_t*, char *cmd, ...))exec; this->public.sigchild = (void(*)(guest_t*))sigchild; this->public.destroy = (void*)destroy; diff --git a/src/dumm/guest.h b/src/dumm/guest.h index 919726d0c..c9406e11d 100644 --- a/src/dumm/guest.h +++ b/src/dumm/guest.h @@ -140,7 +140,16 @@ struct guest_t { * @return FALSE if failed */ bool (*load_template)(guest_t *this, char *parent); - + + /** + * Execute a command in the guest. + * + * @param cmd command to execute + * @param ... printf style argument list for cmd + * @return TRUE if command executed + */ + bool (*exec)(guest_t *this, char *cmd, ...); + /** * @brief Called whenever a SIGCHILD for the guests PID is received. */ diff --git a/src/dumm/iface.c b/src/dumm/iface.c index a7d62ffe0..fdfe50d3b 100644 --- a/src/dumm/iface.c +++ b/src/dumm/iface.c @@ -25,6 +25,7 @@ #include #include +#include #include "iface.h" @@ -43,10 +44,12 @@ struct private_iface_t { guest_t *guest; /** mconsole for guest */ mconsole_t *mconsole; + /** list of interface addresses */ + linked_list_t *addresses; }; /** - * bring an interface up or down + * bring an interface up or down (host side) */ bool iface_control(char *name, bool up) { @@ -97,11 +100,69 @@ static char* get_hostif(private_iface_t *this) return this->hostif; } +/** + * Implementation of iface_t.add_address + */ +static bool add_address(private_iface_t *this, host_t *addr) +{ + if (this->guest->exec(this->guest, "ip addr add %H dev %s", + addr, this->guestif)) + { + this->addresses->insert_last(this->addresses, addr); + return TRUE; + } + addr->destroy(addr); + return FALSE; +} + +/** + * Implementation of iface_t.create_address_enumerator + */ +static enumerator_t* create_address_enumerator(private_iface_t *this) +{ + return this->addresses->create_enumerator(this->addresses); +} + +/** + * Implementation of iface_t.delete_address + */ +static bool delete_address(private_iface_t *this, host_t *addr) +{ + enumerator_t *enumerator; + bool success = FALSE; + host_t *current; + + enumerator = create_address_enumerator(this); + while (enumerator->enumerate(enumerator, ¤t)) + { + if (current->ip_equals(current, addr)) + { + if (this->guest->exec(this->guest, "ip addr del %H dev %s", + current, this->guestif)) + { + this->addresses->remove_at(this->addresses, enumerator); + success = TRUE; + } + break; + } + } + enumerator->destroy(enumerator); + return success; +} + /** * 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, "ip link set %s up", this->guestif); + } + else if (this->bridge && bridge == NULL) + { + this->guest->exec(this->guest, "ip link set %s down", this->guestif); + } this->bridge = bridge; } @@ -194,10 +255,13 @@ 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); free(this->hostif); + this->addresses->destroy(this->addresses); free(this); } @@ -210,6 +274,9 @@ iface_t *iface_create(char *name, guest_t *guest, mconsole_t *mconsole) 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; @@ -240,6 +307,7 @@ iface_t *iface_create(char *name, guest_t *guest, mconsole_t *mconsole) { DBG1("bringing iface '%s' up failed: %m", this->hostif); } + this->addresses = linked_list_create(); return &this->public; } diff --git a/src/dumm/iface.h b/src/dumm/iface.h index e646724f0..54a0554c0 100644 --- a/src/dumm/iface.h +++ b/src/dumm/iface.h @@ -18,6 +18,7 @@ #include #include +#include #define TAP_DEVICE "/dev/net/tun" @@ -46,6 +47,29 @@ 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. * diff --git a/src/dumm/irdumm.c b/src/dumm/irdumm.c index a45e17ca3..e8cc12d12 100644 --- a/src/dumm/irdumm.c +++ b/src/dumm/irdumm.c @@ -170,6 +170,18 @@ static VALUE guest_stop(VALUE self) return self; } +static VALUE guest_exec(VALUE self, VALUE cmd) +{ + guest_t *guest; + + Data_Get_Struct(self, guest_t, guest); + if (!guest->exec(guest, StringValuePtr(cmd))) + { + rb_raise(rb_eRuntimeError, "executing command failed"); + } + return self; +} + static VALUE guest_add_iface(VALUE self, VALUE name) { guest_t *guest; @@ -246,6 +258,7 @@ static void guest_init() 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, "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); @@ -399,6 +412,62 @@ static VALUE iface_disconnect(VALUE self) 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_eRuntimeError, "invalid IP address"); + } + Data_Get_Struct(self, iface_t, iface); + if (!iface->add_address(iface, addr)) + { + rb_raise(rb_eRuntimeError, "adding address failed"); + } + 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); + Data_Get_Struct(self, iface_t, iface); + if (!iface->delete_address(iface, addr)) + { + addr->destroy(addr); + rb_raise(rb_eRuntimeError, "address not found"); + } + addr->destroy(addr); + return self; +} + static VALUE iface_delete(VALUE self) { guest_t *guest; @@ -416,7 +485,11 @@ static void iface_init() 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); + rb_include_module(rbc_iface, rb_mEnumerable); } /** diff --git a/src/dumm/mconsole.c b/src/dumm/mconsole.c index 78c97fb6b..63e15c75a 100644 --- a/src/dumm/mconsole.c +++ b/src/dumm/mconsole.c @@ -197,6 +197,27 @@ static bool del_iface(private_mconsole_t *this, char *guest) return TRUE; } +/** + * Implementation of mconsole_t.exec + */ +static bool exec(private_mconsole_t *this, char *cmd) +{ + char buf[512]; + int len; + + len = snprintf(buf, sizeof(buf), "exec %s", cmd); + if (len < 0 || len >= sizeof(buf)) + { + return -1; + } + if (request(this, buf, buf, &len) != 0) + { + DBG1("exec failed: %.*s", len, buf); + return FALSE; + } + return TRUE; +} + /** * Poll until guest is ready */ @@ -338,6 +359,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 = (bool(*)(mconsole_t*, 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..a26e094e8 100644 --- a/src/dumm/mconsole.h +++ b/src/dumm/mconsole.h @@ -42,6 +42,11 @@ struct mconsole_t { */ bool (*del_iface)(mconsole_t *this, char *guest); + /** + * Execute a command in the UML host. + */ + bool (*exec)(mconsole_t *this, char *cmd); + /** * @brief Destroy the mconsole instance */ diff --git a/src/dumm/patches/mconsole-exec-2.6.26.patch b/src/dumm/patches/mconsole-exec-2.6.26.patch new file mode 100644 index 000000000..0ab47aaf1 --- /dev/null +++ b/src/dumm/patches/mconsole-exec-2.6.26.patch @@ -0,0 +1,63 @@ +--- a/arch/um/drivers/mconsole_kern.c 2008-04-17 04:49:44.000000000 +0200 ++++ b/arch/um/drivers/mconsole_kern.c 2008-07-07 13:55:48.000000000 +0200 +@@ -4,6 +4,7 @@ + * Licensed under the GPL + */ + ++#include "linux/kmod.h" + #include + #include + #include +@@ -199,6 +200,24 @@ + } + #endif + ++void mconsole_exec(struct mc_request *req) ++{ ++ int res; ++ ++ char *envp[] = { "HOME=/", "TERM=linux", "PATH=/sbin:/usr/sbin:/bin:/usr/bin", NULL }; ++ char *argv[] = { "/bin/sh", "-c", req->request.data + strlen("exec "), NULL }; ++ res = call_usermodehelper("/bin/sh", argv, envp, 0); ++ ++ if (res < 0) { ++ char buf[60]; ++ snprintf(buf, 60, "call_usermodehelper failed in mconsole_exec with error code: %d", -res); ++ mconsole_reply(req, buf, 1, 0); ++ return; ++ } ++ ++ mconsole_reply(req, "The command has been started successfully.", 0, 0); ++} ++ + void mconsole_proc(struct mc_request *req) + { + char path[64]; +@@ -270,6 +289,7 @@ + stop - pause the UML; it will do nothing until it receives a 'go' \n\ + go - continue the UML after a 'stop' \n\ + log - make UML enter into the kernel log\n\ ++ exec - pass to /bin/sh -c in guest\n\ + proc - returns the contents of the UML's /proc/\n\ + stack - returns the stack of the specified pid\n\ + " +--- a/arch/um/drivers/mconsole_user.c 2008-05-21 18:34:47.000000000 +0200 ++++ b/arch/um/drivers/mconsole_user.c 2008-07-07 13:47:13.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-04-17 04:49:44.000000000 +0200 ++++ b/arch/um/include/mconsole.h 2008-07-07 13:46:56.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); + -- cgit v1.2.3