guest interface/address management using hackish mconsole exec patch, ruby bindings
authorMartin Willi <martin@strongswan.org>
Mon, 7 Jul 2008 14:56:04 +0000 (14:56 -0000)
committerMartin Willi <martin@strongswan.org>
Mon, 7 Jul 2008 14:56:04 +0000 (14:56 -0000)
src/dumm/guest.c
src/dumm/guest.h
src/dumm/iface.c
src/dumm/iface.h
src/dumm/irdumm.c
src/dumm/mconsole.c
src/dumm/mconsole.h
src/dumm/patches/mconsole-exec-2.6.26.patch [new file with mode: 0644]

index 75d0245..6aad150 100644 (file)
@@ -25,6 +25,7 @@
 #include <signal.h>
 #include <dirent.h>
 #include <termios.h>
+#include <stdarg.h>
 
 #include <debug.h>
 #include <utils/linked_list.h>
@@ -325,6 +326,29 @@ static bool load_template(private_guest_t *this, char *path)
 }
 
 /**
+ * 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.
  */
 static void sigchild(private_guest_t *this)
@@ -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;
                
index 919726d..c9406e1 100644 (file)
@@ -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.
         */
index a7d62ff..fdfe50d 100644 (file)
@@ -25,6 +25,7 @@
 #include <linux/if_tun.h>
 
 #include <debug.h>
+#include <utils/linked_list.h>
 
 #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)
 {
@@ -98,10 +101,68 @@ 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)
+{
+       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, &current))
+       {
+               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;
 }
 
index e646724..54a0554 100644 (file)
@@ -18,6 +18,7 @@
 
 #include <library.h>
 #include <utils/enumerator.h>
+#include <utils/host.h>
 
 #define TAP_DEVICE "/dev/net/tun"
 
@@ -47,6 +48,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.
         *
         * @param bridge        assigned bridge, or NULL for none
index a45e17c..e8cc12d 100644 (file)
@@ -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);
 }
 
 /**
index 78c97fb..63e15c7 100644 (file)
@@ -198,6 +198,27 @@ static bool del_iface(private_mconsole_t *this, char *guest)
 }
 
 /**
+ * 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
  */
 static bool wait_bootup(private_mconsole_t *this)
@@ -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;
index 55ce15d..a26e094 100644 (file)
@@ -43,6 +43,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
         */
        void (*destroy) (mconsole_t *this);
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 (file)
index 0000000..0ab47aa
--- /dev/null
@@ -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 <linux/console.h>
+ #include <linux/ctype.h>
+ #include <linux/interrupt.h>
+@@ -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 <string> - make UML enter <string> into the kernel log\n\
++    exec <string> - pass <string> to /bin/sh -c in guest\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-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);