added dynamic interface manipulation for guests
authorMartin Willi <martin@strongswan.org>
Wed, 25 Jul 2007 13:23:45 +0000 (13:23 -0000)
committerMartin Willi <martin@strongswan.org>
Wed, 25 Jul 2007 13:23:45 +0000 (13:23 -0000)
management of tap devices on the host

src/dumm/Makefile
src/dumm/dumm.c
src/dumm/dumm.h
src/dumm/guest.c
src/dumm/guest.h
src/dumm/iface.c [new file with mode: 0644]
src/dumm/iface.h [new file with mode: 0644]
src/dumm/main.c
src/dumm/mconsole.c [new file with mode: 0644]
src/dumm/mconsole.h [new file with mode: 0644]

index 39bdb0b..c60e57d 100644 (file)
@@ -1,3 +1,3 @@
 
-dumm:  dumm.c dumm.h guest.c guest.h main.c
-       gcc -o dumm dumm.c guest.c main.c -lreadline -lstrongswan -I../libstrongswan/ -g -Wall
+dumm:  dumm.c dumm.h guest.c guest.h iface.c iface.h mconsole.c mconsole.h main.c
+       gcc -o dumm dumm.c guest.c iface.c mconsole.c main.c -lreadline -lstrongswan -I../libstrongswan/ -g -Wall
index 7536bce..f5ca7d3 100644 (file)
@@ -1,3 +1,18 @@
+/*
+ * Copyright (C) 2007 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 <sys/stat.h>
 
 #include <debug.h>
index f18817a..fd2ba46 100644 (file)
@@ -1,3 +1,18 @@
+/*
+ * Copyright (C) 2007 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.
+ */
+
 #ifndef DUMM_H
 #define DUMM_H
 
@@ -45,6 +60,14 @@ struct dumm_t {
        void (*destroy) (dumm_t *this);
 };
 
+/**
+ * @brief Create a new group of UML hosts and networks.
+ *
+ * Dumm uses its working dir to create folders and files it works with.
+ *
+ * @return                             created UML group, or NULL if failed.
+ */
 dumm_t *dumm_create();
 
 #endif /* DUMM_H */
+
index 2028823..cdb97c7 100644 (file)
@@ -1,3 +1,18 @@
+/*
+ * Copyright (C) 2007 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.
+ */
+
 #define _GNU_SOURCE
 
 #include <sys/types.h>
 #include <signal.h>
 
 #include <debug.h>
+#include <utils/linked_list.h>
 
 #include "dumm.h"
 #include "guest.h"
+#include "mconsole.h"
 
 typedef struct private_guest_t private_guest_t;
 
 struct private_guest_t {
+       /** implemented public interface */
        guest_t public;
+       /** name of the guest */
        char *name;
+       /** kernel to boot for guest */
        char *kernel;
+       /** read only master filesystem guest uses */
        char *master;
+       /** amount of memory for guest, in MB */
        int mem;
+       /** pid of guest child process */
        int pid;
+       /** log file for console 0 */
        int bootlog;
+       /** mconsole to control running UML */
+       mconsole_t *mconsole;
+       /** list of interfaces attached to the guest */
+       linked_list_t *ifaces;
 };
 
+/**
+ * Implementation of guest_t.get_name.
+ */
 static char* get_name(private_guest_t *this)
 {
        return this->name;
 }
 
+/**
+ * Implementation of guest_t.create_iface.
+ */
+static iface_t* create_iface(private_guest_t *this, char *name)
+{
+       iterator_t *iterator;
+       iface_t *iface;
+       
+       if (this->pid == 0)
+       {
+               DBG1("guest '%s' not running, unable to add interface", this->name);
+               return NULL;
+       }
+       
+       iterator = this->ifaces->create_iterator(this->ifaces, TRUE);
+       while (iterator->iterate(iterator, (void**)&iface))
+       {
+               if (streq(name, iface->get_guest(iface)))
+               {
+                       DBG1("guest '%s' already has an interface '%s'", this->name, name);
+                       iterator->destroy(iterator);
+                       return NULL;
+               }
+       }
+       iterator->destroy(iterator);
+
+       iface = iface_create(name, this->mconsole);
+       if (iface)
+       {
+               this->ifaces->insert_last(this->ifaces, iface);
+       }
+       return iface;
+}
+
+/**
+ * Implementation of guest_t.create_iface_iterator.
+ */
+static iterator_t* create_iface_iterator(private_guest_t *this)
+{
+       return this->ifaces->create_iterator(this->ifaces, TRUE);
+}
+
+/**
+ * write format string to a buffer, and advance buffer position
+ */
 static char* write_arg(char **pos, size_t *left, char *format, ...)
 {
        size_t len;
@@ -50,6 +126,9 @@ static char* write_arg(char **pos, size_t *left, char *format, ...)
        return res;
 }
 
+/**
+ * Implementation of guest_t.start.
+ */
 static bool start(private_guest_t *this)
 {
        char buf[1024];
@@ -67,7 +146,7 @@ static bool start(private_guest_t *this)
        args[i++] = write_arg(&pos, &left, "uml_dir=%s/%s", RUN_DIR, this->name);
        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, "con=pts");
+       /*args[i++] = write_arg(&pos, &left, "con=pts");*/
        args[i++] = write_arg(&pos, &left, "con0=null,fd:%d", this->bootlog);
        args[i++] = write_arg(&pos, &left, "con1=fd:0,fd:1");
        args[i++] = write_arg(&pos, &left, "con3=null,null");
@@ -84,15 +163,30 @@ static bool start(private_guest_t *this)
                        dup2(open("/dev/null", 0), 1);
                        dup2(open("/dev/null", 0), 2);
                        execvp(args[0], args);
+                       DBG1("starting UML kernel '%s' failed", args[0]);
                        exit(1);
                case -1:
                        this->pid = 0;
                        return FALSE;
                default:
-                       return TRUE;
+                       break;
+       }
+       /* open mconsole */
+       snprintf(buf, sizeof(buf), "%s/%s/%s/mconsole", RUN_DIR, this->name, this->name);
+       this->mconsole = mconsole_create(buf);
+       if (this->mconsole == NULL)
+       {
+               DBG1("opening mconsole at '%s' failed, stopping guest", buf);
+               kill(this->pid, SIGINT);
+               this->pid = 0;
+               return FALSE;
        }
+       return TRUE;
 }
 
+/**
+ * Implementation of guest_t.stop.
+ */
 static void stop(private_guest_t *this)
 {
        if (this->pid)
@@ -103,7 +197,7 @@ static void stop(private_guest_t *this)
 }
 
 /**
- * create a directory
+ * Check if directory exists, create otherwise
  */
 static bool makedir(char *dir, char *name)
 {
@@ -138,6 +232,7 @@ static bool umount_unionfs(char *name)
        }
        if (system(cmd) != 0)
        {
+               DBG1("unmounting guest unionfs for %s failed", name);
                return FALSE;
        }
        return TRUE;
@@ -151,7 +246,6 @@ static bool mount_unionfs(char *name, char *master)
        char cmd[256];
        size_t len;
        
-       /* mount unionfs */
        len = snprintf(cmd, sizeof(cmd), "unionfs %s/%s:%s %s/%s",
                                   HOST_DIR, name, master, MOUNT_DIR, name);
        if (len < 0 || len >= sizeof(cmd))
@@ -160,7 +254,7 @@ static bool mount_unionfs(char *name, char *master)
        }
        if (system(cmd) != 0)
        {
-               DBG1("mounting unionfs using '%s' failed.", cmd);
+               DBG1("mounting guest unionfs for %s using '%s' failed", name, cmd);
                return FALSE;
        }
        return TRUE;
@@ -183,18 +277,21 @@ static int open_bootlog(char *name)
        fd = open(blg, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR);
        if (fd == -1)
        {
+               DBG1("opening bootlog '%s' for %s failed, using stdout", blg, name);
                return 1;
        }
        return fd;
 }
 
 /**
- * stop guest, unmount mounts
+ * Implementation of guest_t.destroy.
  */
 static void destroy(private_guest_t *this)
 {
        stop(this);
        umount_unionfs(this->name);
+       this->ifaces->destroy_offset(this->ifaces, offsetof(iface_t, destroy));
+       DESTROY_IF(this->mconsole);
        free(this->name);
        free(this->kernel);
        free(this->master);
@@ -209,21 +306,15 @@ guest_t *guest_create(char *name, char *kernel, char *master, int mem)
        private_guest_t *this = malloc_thing(private_guest_t);
        
        this->public.get_name = (void*)get_name;
+       this->public.create_iface = (iface_t*(*)(guest_t*,char*))create_iface;
+       this->public.create_iface_iterator = (iterator_t*(*)(guest_t*))create_iface_iterator;
        this->public.start = (void*)start;
        this->public.stop = (void*)stop;
        this->public.destroy = (void*)destroy;
        
        if (!makedir(HOST_DIR, name) || !makedir(MOUNT_DIR, name) ||
-               !makedir(RUN_DIR, name))
-       {
-               DBG1("creating guest directories for %s failed failed.", name);
-               free(this);
-               return NULL;
-       }
-       
-       if (!mount_unionfs(name, master))
+               !makedir(RUN_DIR, name) || !mount_unionfs(name, master))
        {
-               DBG1("mounting guest unionfs for %s failed.", name);
                free(this);
                return NULL;
        }
@@ -234,6 +325,8 @@ guest_t *guest_create(char *name, char *kernel, char *master, int mem)
        this->mem = mem;
        this->pid = 0;
        this->bootlog = open_bootlog(name);
+       this->mconsole = NULL;
+       this->ifaces = linked_list_create();
 
        return &this->public;
 }
index 39a72c0..f4e9f50 100644 (file)
@@ -1,23 +1,84 @@
+/*
+ * Copyright (C) 2007 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.
+ */
+
 #ifndef GUEST_H
 #define GUEST_H
 
 #include <library.h>
-#include <utils/linked_list.h>
+#include <utils/iterator.h>
+
+#include "iface.h"
 
 typedef struct guest_t guest_t;
 
+/**
+ * @brief A guest is a UML instance running on the host.
+ **/
 struct guest_t {
        
+       /**
+        * @brief Get the name of this guest.
+        *
+        * @return              name of the guest
+        */
        char* (*get_name) (guest_t *this);
        
+       /**
+        * @brief Start the guest.
+        *
+        * @return              TRUE if guest successfully started
+        */
        bool (*start) (guest_t *this);
        
+       /**
+        * @brief Kill the guest.
+        *
+        * @return              TRUE if guest was running and killed
+        */
        bool (*stop) (guest_t *this);
        
+       /**
+        * @brief Create a new interface for that host.
+        *
+        * @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);
+       
+       /**
+        * @brief Create an iterator over all guest interfaces.
+        *
+        * @return              iterator over iface_t's
+        */
+       iterator_t* (*create_iface_iterator)(guest_t *this);
+
+       /**
+        * @brief Close and destroy a guest with all interfaces
+        */     
        void (*destroy) (guest_t *this);
 };
 
+/**
+ * @brief Create a new, unstarted guest.
+ *
+ * @param name         name of the guest
+ * @param kernel       kernel to boot for this guest
+ * @param master       read-only master filesystem for guest
+ * @param mem          amount of memory to give the guest
+ */
 guest_t *guest_create(char *name, char *kernel, char *master, int mem);
 
-
 #endif /* GUEST_H */
+
diff --git a/src/dumm/iface.c b/src/dumm/iface.c
new file mode 100644 (file)
index 0000000..9e690d4
--- /dev/null
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2007 Martin Willi
+ * Hochschule fuer Technik Rapperswil
+ * Copyright (C) 2002 Jeff Dike
+ *
+ * Based on the "tunctl" utlity 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 <net/if.h>
+#include <sys/ioctl.h>
+#include <linux/if_tun.h>
+
+#include <debug.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 *guest;
+       /** device name at host (tap0) */
+       char *host;
+       /** tap device handle to manage taps */
+       int tap;
+       /** mconsole for guest */
+       mconsole_t *mconsole;
+};
+
+/**
+ * Implementation of iface_t.get_guest.
+ */
+static char* get_guest(private_iface_t *this)
+{
+       return this->guest;
+}
+
+/**
+ * Implementation of iface_t.get_host.
+ */
+static char* get_host(private_iface_t *this)
+{
+       return this->host;
+}
+
+/**
+ * destroy the tap device
+ */
+static bool destroy_tap(private_iface_t *this)
+{
+       struct ifreq ifr;
+
+       memset(&ifr, 0, sizeof(ifr));
+       ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
+       strncpy(ifr.ifr_name, this->host, sizeof(ifr.ifr_name) - 1);
+       
+       if (ioctl(this->tap, TUNSETIFF, &ifr) < 0 ||
+               ioctl(this->tap, TUNSETPERSIST, 0) < 0)
+       {
+               DBG1("removing %s failed: %m", this->host);
+               return FALSE;
+       }
+       return TRUE;
+}
+
+/**
+ * create the tap device
+ */
+static char* create_tap(private_iface_t *this)
+{
+       struct ifreq ifr;
+
+       memset(&ifr, 0, sizeof(ifr));
+       ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
+       
+       if (ioctl(this->tap, TUNSETIFF, &ifr) < 0 ||
+               ioctl(this->tap, TUNSETPERSIST, 1) < 0)
+    {
+               DBG1("creating new tap device failed: %m");
+               return NULL;
+    } 
+       return strdup(ifr.ifr_name);
+}
+
+/**
+ * Implementation of iface_t.destroy.
+ */
+static void destroy(private_iface_t *this)
+{
+       this->mconsole->del_iface(this->mconsole, this->guest);
+       destroy_tap(this);
+       close(this->tap);
+       free(this->guest);
+       free(this->host);
+       free(this);
+}
+
+/**
+ * create the iface instance
+ */
+iface_t *iface_create(char *guest, mconsole_t *mconsole)
+{
+       private_iface_t *this = malloc_thing(private_iface_t);
+       
+       this->public.get_host = (char*(*)(iface_t*))get_host;
+       this->public.get_guest = (char*(*)(iface_t*))get_guest;
+       this->public.destroy = (void*)destroy;
+
+       this->mconsole = mconsole;
+       this->tap = open(TAP_DEVICE, O_RDWR);
+       if (this->tap < 0)
+       {
+               DBG1("unable to open tap device %s: %m", TAP_DEVICE);
+               free(this);
+               return NULL;
+       }
+       this->guest = strdup(guest);
+       this->host = create_tap(this);
+       if (this->host == NULL)
+       {
+               destroy_tap(this);
+               close(this->tap);
+               free(this->guest);
+               free(this);
+               return NULL;
+       }
+       if (!this->mconsole->add_iface(this->mconsole, this->guest, this->host))
+       {
+               DBG1("creating interface '%s' in guest failed", this->guest);
+               destroy_tap(this);
+               close(this->tap);
+               free(this->guest);
+               free(this->host);
+               free(this);
+               return NULL;
+       }
+       return &this->public;
+}
+
diff --git a/src/dumm/iface.h b/src/dumm/iface.h
new file mode 100644 (file)
index 0000000..de69fcb
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2007 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.
+ */
+
+#ifndef IFACE_H
+#define IFACE_H
+
+#include <library.h>
+#include <utils/iterator.h>
+
+#include "mconsole.h"
+
+#define TAP_DEVICE "/dev/net/tun"
+
+typedef struct iface_t iface_t;
+
+/**
+ * @brief Interface in a guest, connected to a tap device on the host.
+ */
+struct iface_t {
+       
+       /**
+        * @brief Get the interface name in the guest (e.g. eth0).
+        *
+        * @return              guest interface name
+        */
+       char* (*get_guest)(iface_t *this);
+       
+       /**
+        * @brief Get the interface name at the host (e.g. tap0).
+        *
+        * @return              host interface (tap device) name
+        */
+       char* (*get_host)(iface_t *this);
+       
+       /*
+       bool (*up) (iface_t *this);
+       bool (*down) (iface_t *this);
+       bool (*add_addr) (iface_t *this, host_t *addr);
+       bool (*del_addr) (iface_t *this, host_t *addr);
+       iterator_t* (*create_addr_iterator) (iface_t *this);
+       */
+       
+       /**
+        * @brief Destroy an interface
+        */
+       void (*destroy) (iface_t *this);
+};
+
+/**
+ * @brief Create a new interface for a guest
+ *
+ * @param guest                name of the interface in the guest
+ * @param mconsole     mconsole of guest
+ * @return                     interface descriptor, or NULL if failed
+ */
+iface_t *iface_create(char *guest, mconsole_t *mconsole);
+
+#endif /* IFACE_H */
index 26896c2..7e22ddf 100644 (file)
@@ -1,3 +1,18 @@
+/*
+ * Copyright (C) 2007 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.
+ */
+
 #define _GNU_SOURCE
 
 #include <stdio.h>
@@ -20,7 +35,7 @@ static void usage()
 }
 
 /**
- * show usage information (commandline arguments)
+ * help for dumm root shell
  */
 static void help()
 {
@@ -29,15 +44,29 @@ static void help()
        printf("                                kernel=<uml-kernel>\n");
        printf("                                master=<read-only root files>\n");
        printf("                                memory=<guest memory in MB>\n");
-       printf("guests                        list running guests\n");
+       printf("list                          list running guests\n");
+       printf("guest <name>                  open guest menu for <name>\n");
        printf("help                          show this help\n");
        printf("quit                          kill quests and exit\n");
 }
 
+
+/**
+ * help for guest shell
+ */
+static void help_guest()
+{
+       printf("addif <name>                  add an interface to the guest\n");
+       printf("delif <name>                  remove the interface\n");
+       printf("listif                        list guests interfaces\n");
+       printf("help                          show this help\n");
+       printf("quit                          quit the guest menu\n");
+}
+
 /**
  * start an UML guest
  */
-static void start(umli_t *umli, char *line)
+static void start(dumm_t *dumm, char *line)
 {
        enum {
                NAME = 0,
@@ -101,7 +130,7 @@ static void start(umli_t *umli, char *line)
                mem = 128;
        }
        
-       if (umli->start_guest(umli, name, kernel, master, mem))
+       if (dumm->start_guest(dumm, name, kernel, master, mem))
        {
                printf("starting guest '%s'\n", name);
        }
@@ -112,19 +141,187 @@ static void start(umli_t *umli, char *line)
 }
 
 /**
- * list running UML guests
+ * add an iface to a guest
+ */
+static void add_if(guest_t *guest, char *name)
+{
+       iface_t *iface;
+       
+       iface = guest->create_iface(guest, name);
+       if (iface)
+       {
+               printf("created guest interface '%s' connected to '%s'\n",
+                          iface->get_guest(iface), iface->get_host(iface));
+       }
+       else
+       {
+               printf("failed to create guest interface\n");
+       }
+}
+
+/**
+ * delete an iface from a guest
  */
-static void guests(umli_t *umli)
+static void del_if(guest_t *guest, char *name)
 {
+       iface_t *iface;
+       iterator_t *iterator;
+       bool found = FALSE;
+       
+       iterator = guest->create_iface_iterator(guest);
+       while (iterator->iterate(iterator, (void**)&iface))
+       {
+               if (streq(name, iface->get_guest(iface)))
+               {
+                       iterator->remove(iterator);
+                       printf("removing interface '%s' ('%s') from %s\n",
+                                  iface->get_guest(iface), iface->get_host(iface),
+                                  guest->get_name(guest));
+                       iface->destroy(iface);
+                       found = TRUE;
+                       break;
+               }
+       }
+       iterator->destroy(iterator);
+       if (!found)
+       {
+               printf("guest '%s' has no interface named '%s'\n",
+                          guest->get_name(guest), name);
+       }
+}
+
+/**
+ * list interfaces on a guest
+ */
+static void list_if(guest_t *guest)
+{
+       iface_t *iface;
+       iterator_t *iterator;
+       
+       iterator = guest->create_iface_iterator(guest);
+       while (iterator->iterate(iterator, (void**)&iface))
+       {
+               printf("'%s' => '%s'\n", iface->get_guest(iface), iface->get_host(iface));
+
+       }
+       iterator->destroy(iterator);
+}
+
+/**
+ * subshell for guests
+ */
+static void guest(dumm_t *dumm, char *name)
+{
+       char *line = NULL;
+       char prompt[32];
+       int len;
        iterator_t *iterator;
        guest_t *guest;
+       bool found = FALSE;
        
-       iterator = umli->create_guest_iterator(umli);
+       iterator = dumm->create_guest_iterator(dumm);
        while (iterator->iterate(iterator, (void**)&guest))
        {
-               printf("%s\n", guest->get_name(guest));
+               if (streq(name, guest->get_name(guest)))
+               {
+                       found = TRUE;
+               }
        }
        iterator->destroy(iterator);
+       if (!found)
+       {
+               printf("guest '%s' not found\n", name);
+               return;
+       }
+       
+       len = snprintf(prompt, sizeof(prompt), "dumm@%s# ", name);
+       if (len < 0 || len >= sizeof(prompt))
+       {
+               return;
+       }
+
+       while (TRUE)
+       {
+               enum {
+                       QUIT = 0,
+                       HELP,
+                       ADDIF,
+                       DELIF,
+                       LISTIF,
+               };
+               char *const opts[] = {
+                       [QUIT] = "quit",
+                       [HELP] = "help",
+                       [ADDIF] = "addif",
+                       [DELIF] = "delif",
+                       [LISTIF] = "listif",
+                       NULL
+               };
+               char *pos, *value;
+               
+               free(line);
+               line = readline(prompt);
+               if (line == NULL || *line == '\0')
+               {
+                       continue;
+               }
+               add_history(line);
+               pos = line;
+               while (*pos != '\0')
+               {
+                       if (*pos == ' ')
+                       {
+                               *pos = ',';
+                       }
+                       pos++;
+               }
+               pos = line;
+               switch (getsubopt(&pos, opts, &value))
+               {
+                       case QUIT:
+                               free(line);
+                               break;
+                       case HELP:
+                               help_guest();
+                               continue;
+                       case ADDIF:
+                               add_if(guest, pos);
+                               continue;
+                       case DELIF:
+                               del_if(guest, pos);
+                               continue;
+                       case LISTIF:
+                               list_if(guest);
+                               continue;
+                       default:
+                               printf("command unknown: '%s'\n", line);
+                               continue;
+               }
+               break;
+       }
+}
+
+/**
+ * list running UML guests
+ */
+static void list(dumm_t *dumm)
+{
+       iterator_t *guests, *ifaces;
+       guest_t *guest;
+       iface_t *iface;
+       
+       guests = dumm->create_guest_iterator(dumm);
+       while (guests->iterate(guests, (void**)&guest))
+       {
+               printf("%s\n", guest->get_name(guest));
+               ifaces = guest->create_iface_iterator(guest);
+               while (ifaces->iterate(ifaces, (void**)&iface))
+               {
+                       printf("  '%s' => '%s'\n", iface->get_guest(iface), iface->get_host(iface));
+               }
+               ifaces->destroy(ifaces);
+       }
+       guests->destroy(guests);
 }
 
 /**
@@ -132,7 +329,7 @@ static void guests(umli_t *umli)
  */
 int main(int argc, char *argv[])
 {
-       umli_t *umli;
+       dumm_t *dumm;
        char *line = NULL;
 
        while (TRUE)
@@ -164,7 +361,7 @@ int main(int argc, char *argv[])
                break;
        }
        
-       umli = umli_create();
+       dumm = dumm_create();
 
        while (TRUE)
        {
@@ -172,13 +369,15 @@ int main(int argc, char *argv[])
                        QUIT = 0,
                        HELP,
                        START,
-                       GUESTS,
+                       LIST,
+                       GUEST,
                };
                char *const opts[] = {
                        [QUIT] = "quit",
                        [HELP] = "help",
                        [START] = "start",
-                       [GUESTS] = "guests",
+                       [LIST] = "list",
+                       [GUEST] = "guest",
                        NULL
                };
                char *pos, *value;
@@ -210,10 +409,13 @@ int main(int argc, char *argv[])
                                help();
                                continue;
                        case START:
-                               start(umli, pos);
+                               start(dumm, pos);
+                               continue;
+                       case LIST:
+                               list(dumm);
                                continue;
-                       case GUESTS:
-                               guests(umli);
+                       case GUEST:
+                               guest(dumm, pos);
                                continue;
                        default:
                                printf("command unknown: '%s'\n", line);
@@ -221,7 +423,7 @@ int main(int argc, char *argv[])
                }
                break;
        }
-       umli->destroy(umli);
+       dumm->destroy(dumm);
        clear_history();
        return 0;
 }
diff --git a/src/dumm/mconsole.c b/src/dumm/mconsole.c
new file mode 100644 (file)
index 0000000..b782ebe
--- /dev/null
@@ -0,0 +1,170 @@
+/*
+ * Copyright (C) 2007 Martin Willi
+ * Hochschule fuer Technik Rapperswil
+ * Copyright (C) 2001-2004 Jeff Dike
+ *
+ * Based on the "uml_mconsole" utilty 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 <unistd.h>
+#include <stdio.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include <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 socket;
+       /** address of uml socket */
+       struct sockaddr_un uml;
+};
+
+/**
+ * send a request to UML using mconsole
+ */
+static bool request(private_mconsole_t *this, char *command)
+{
+       struct {
+               u_int32_t magic;
+               u_int32_t version;
+               u_int32_t len;
+               char data[MCONSOLE_MAX_DATA];
+       } request;
+       struct {
+               u_int32_t err;
+               u_int32_t more;
+               u_int32_t len;
+               char data[MCONSOLE_MAX_DATA];
+       } reply;
+       bool first = TRUE, good = TRUE;
+       int len;
+       
+       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);
+
+       if (sendto(this->socket, &request, sizeof(request), 0,
+               (struct sockaddr*)&this->uml, sizeof(this->uml)) < 0)
+       {
+               DBG1("sending mconsole command to UML failed: %m");
+               return FALSE;
+       }
+       do 
+       {
+               len = recvfrom(this->socket, &reply, sizeof(reply), 0, NULL, 0);
+               if (len < 0)
+               {
+                       DBG1("receiving from mconsole failed: %m");
+               return FALSE;
+               }
+               if (first && reply.err)
+               {
+                       good = FALSE;
+                       DBG1("received error from UML mconsole: %s", reply.data);
+               }
+               first = FALSE;
+       }
+       while (reply.more);
+       return good;
+}
+
+/**
+ * Implementation of mconsole_t.add_iface.
+ */
+static bool add_iface(private_mconsole_t *this, char *guest, char *host)
+{
+       char buf[128];
+       int len;
+       
+       len = snprintf(buf, sizeof(buf), "config %s=tuntap,%s", guest, host);
+       if (len < 0 || len >= sizeof(buf))
+       {
+               return FALSE;
+       }
+       return request(this, buf);
+}
+
+/**
+ * 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;
+       }
+       return request(this, buf);
+}
+
+/**
+ * Implementation of mconsole_t.destroy.
+ */
+static void destroy(private_mconsole_t *this)
+{
+       close(this->socket);
+       free(this);
+}
+
+/**
+ * create the mconsole instance
+ */
+mconsole_t *mconsole_create(char *sock)
+{
+       struct sockaddr_un addr;
+       private_mconsole_t *this = malloc_thing(private_mconsole_t);
+       
+       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.destroy = (void*)destroy;
+       
+       this->socket = socket(AF_UNIX, SOCK_DGRAM, 0);
+       if (this->socket < 0)
+       {
+               DBG1("opening mconsole socket failed: %m");
+               free(this);
+               return NULL;
+       }
+       memset(&addr, 0, sizeof(addr));
+       addr.sun_family = AF_UNIX;
+       sprintf(&addr.sun_path[1], "%5d", getpid());
+       if (bind(this->socket, (struct sockaddr*)&addr, sizeof(addr)) < 0)
+       {
+               DBG1("binding mconsole socket failed: %m");
+               destroy(this);
+               return NULL;
+       }
+       memset(&this->uml, 0, sizeof(this->uml));
+       this->uml.sun_family = AF_UNIX;
+       strncpy(this->uml.sun_path, sock, sizeof(this->uml.sun_path));
+       
+       return &this->public;
+}
+
diff --git a/src/dumm/mconsole.h b/src/dumm/mconsole.h
new file mode 100644 (file)
index 0000000..8d71053
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2007 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.
+ */
+
+#ifndef MCONSOLE_H
+#define MCONSOLE_H
+
+#include <library.h>
+
+typedef struct mconsole_t mconsole_t;
+
+/**
+ * @brief UML mconsole, change running UML configuration using mconsole.
+ */
+struct mconsole_t {
+       
+       /**
+        * @brief 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);
+       
+       /**
+        * @brief 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);
+       
+       /**
+        * @brief Destroy the mconsole instance
+        */
+       void (*destroy) (mconsole_t *this);
+};
+
+/**
+ * @brief Create a new mconsole connection to a guest.
+ *
+ * @param socket                       guests mconsole socket
+ * @return                                     mconsole instance, or NULL if failed
+ */
+mconsole_t *mconsole_create(char *socket);
+
+#endif /* MCONSOLE_H */
+