checked in first draft of "Dynamic Uml Mesh Modeler"
authorMartin Willi <martin@strongswan.org>
Tue, 24 Jul 2007 14:22:56 +0000 (14:22 -0000)
committerMartin Willi <martin@strongswan.org>
Tue, 24 Jul 2007 14:22:56 +0000 (14:22 -0000)
src/dumm/Makefile [new file with mode: 0644]
src/dumm/dumm.c [new file with mode: 0644]
src/dumm/dumm.h [new file with mode: 0644]
src/dumm/guest.c [new file with mode: 0644]
src/dumm/guest.h [new file with mode: 0644]
src/dumm/main.c [new file with mode: 0644]

diff --git a/src/dumm/Makefile b/src/dumm/Makefile
new file mode 100644 (file)
index 0000000..39bdb0b
--- /dev/null
@@ -0,0 +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
diff --git a/src/dumm/dumm.c b/src/dumm/dumm.c
new file mode 100644 (file)
index 0000000..7536bce
--- /dev/null
@@ -0,0 +1,74 @@
+#include <sys/stat.h>
+
+#include <debug.h>
+
+#include "dumm.h"
+
+typedef struct private_dumm_t private_dumm_t;
+
+struct private_dumm_t {
+       dumm_t public;
+       linked_list_t *guests;
+};
+
+static guest_t* start_guest(private_dumm_t *this, char *name, char *kernel,
+                                                       char *master, int mem)
+{
+       guest_t *guest;
+       
+       guest = guest_create(name, kernel, master, mem);
+       if (guest)
+       {
+               if (guest->start(guest))
+               {
+                       this->guests->insert_last(this->guests, guest);         
+                       return guest;
+               }
+               guest->destroy(guest);
+       }
+       return NULL;
+}
+
+static iterator_t* create_guest_iterator(private_dumm_t *this)
+{
+       return this->guests->create_iterator(this->guests, TRUE);
+}
+
+static void destroy(private_dumm_t *this)
+{
+       this->guests->destroy_offset(this->guests, offsetof(guest_t, destroy));
+       free(this);
+}
+
+/**
+ * check for a directory, create if it does not exist
+ */
+static bool makedir(char *dir)
+{
+       struct stat st;
+       
+       if (stat(dir, &st) != 0)
+       {
+               return mkdir(dir, S_IRWXU) == 0;
+       }
+       return S_ISDIR(st.st_mode);
+}
+
+dumm_t *dumm_create()
+{
+       private_dumm_t *this = malloc_thing(private_dumm_t);
+       
+       this->public.start_guest = (void*)start_guest;
+       this->public.create_guest_iterator = (void*)create_guest_iterator;
+       this->public.destroy = (void*)destroy;
+       
+       if (!makedir(HOST_DIR) || !makedir(MOUNT_DIR) || !makedir(RUN_DIR))
+       {
+               free(this);
+               return NULL;
+       }
+       
+       this->guests = linked_list_create();
+       return &this->public;
+}
+
diff --git a/src/dumm/dumm.h b/src/dumm/dumm.h
new file mode 100644 (file)
index 0000000..f18817a
--- /dev/null
@@ -0,0 +1,50 @@
+#ifndef DUMM_H
+#define DUMM_H
+
+#include <library.h>
+#include <utils/linked_list.h>
+
+#include "guest.h"
+
+#define HOST_DIR "host"
+#define MOUNT_DIR "mount"
+#define RUN_DIR "run"
+
+
+typedef struct dumm_t dumm_t;
+
+/**
+ * @brief dumm - Dynamic Uml Mesh Modeler
+ *
+ * Controls a group of UML guests and their networks.
+ */
+struct dumm_t {
+
+       /**
+        * @brief Starts a new UML guest
+        *
+        * @param name          name of the guest
+        * @param kernel        kernel to boot
+        * @param master        mounted read only master filesystem
+        * @param mem           amount of memory for guest, in MB
+        * @return                      guest if started, NULL if failed
+        */
+       guest_t* (*start_guest) (dumm_t *this, char *name, char *kernel,
+                                                        char *master, int mem);
+       
+       /**
+        * @brief Create an iterator over all guests.
+        *
+        * @return                      iteraotor over guest_t's
+        */
+       iterator_t* (*create_guest_iterator) (dumm_t *this);
+       
+       /**
+        * @brief stop all guests and destroy the modeler
+        */
+       void (*destroy) (dumm_t *this);
+};
+
+dumm_t *dumm_create();
+
+#endif /* DUMM_H */
diff --git a/src/dumm/guest.c b/src/dumm/guest.c
new file mode 100644 (file)
index 0000000..2028823
--- /dev/null
@@ -0,0 +1,240 @@
+#define _GNU_SOURCE
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <sys/uio.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <signal.h>
+
+#include <debug.h>
+
+#include "dumm.h"
+#include "guest.h"
+
+typedef struct private_guest_t private_guest_t;
+
+struct private_guest_t {
+       guest_t public;
+       char *name;
+       char *kernel;
+       char *master;
+       int mem;
+       int pid;
+       int bootlog;
+};
+
+static char* get_name(private_guest_t *this)
+{
+       return this->name;
+}
+
+static char* write_arg(char **pos, size_t *left, char *format, ...)
+{
+       size_t len;
+       char *res = NULL;
+       va_list args;
+
+       va_start(args, format);
+       len = vsnprintf(*pos, *left, format, args);
+       va_end(args);
+       if (len < *left)
+       {
+               res = *pos;
+               len++;
+               *pos += len + 1;
+               *left -= len + 1;
+       }
+       return res;
+}
+
+static bool start(private_guest_t *this)
+{
+       char buf[1024];
+       char cwd[512];
+       char *pos = buf;
+       char *args[16];
+       int i = 0;
+       size_t left = sizeof(buf);
+
+       args[i++] = this->kernel;
+       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/%s",
+                                                 getcwd(cwd, sizeof(cwd)), MOUNT_DIR, this->name);
+       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, "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");
+       args[i++] = write_arg(&pos, &left, "con4=null,null");
+       args[i++] = write_arg(&pos, &left, "con5=null,null");
+       args[i++] = write_arg(&pos, &left, "con6=null,null");
+       args[i++] = NULL;
+         
+       this->pid = fork();
+       switch (this->pid)
+       {
+               case 0: /* child,  */
+                       dup2(open("/dev/null", 0), 0);
+                       dup2(open("/dev/null", 0), 1);
+                       dup2(open("/dev/null", 0), 2);
+                       execvp(args[0], args);
+                       exit(1);
+               case -1:
+                       this->pid = 0;
+                       return FALSE;
+               default:
+                       return TRUE;
+       }
+}
+
+static void stop(private_guest_t *this)
+{
+       if (this->pid)
+       {
+               kill(this->pid, SIGINT);
+               this->pid = 0;
+       }
+}
+
+/**
+ * create a directory
+ */
+static bool makedir(char *dir, char *name)
+{
+       struct stat st;
+       char buf[256];
+       size_t len;
+       
+       len = snprintf(buf, sizeof(buf), "%s/%s", dir, name);
+       if (len < 0 || len >= sizeof(buf))
+       {
+               return FALSE;
+       }
+       if (stat(buf, &st) != 0)
+       {
+               return mkdir(buf, S_IRWXU) == 0;
+       }
+       return S_ISDIR(st.st_mode);
+}
+
+/**
+ * umount the union filesystem
+ */
+static bool umount_unionfs(char *name)
+{
+       char cmd[128];
+       size_t len;
+       
+       len = snprintf(cmd, sizeof(cmd), "fusermount -u %s/%s", MOUNT_DIR, name);
+       if (len < 0 || len >= sizeof(cmd))
+       {
+               return FALSE;
+       }
+       if (system(cmd) != 0)
+       {
+               return FALSE;
+       }
+       return TRUE;
+}
+
+/**
+ * mount the union filesystem
+ */
+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))
+       {
+               return FALSE;
+       }
+       if (system(cmd) != 0)
+       {
+               DBG1("mounting unionfs using '%s' failed.", cmd);
+               return FALSE;
+       }
+       return TRUE;
+}
+
+/**
+ * open logfile for boot messages
+ */
+static int open_bootlog(char *name)
+{
+       char blg[256];
+       size_t len;
+       int fd;
+       
+       len = snprintf(blg, sizeof(blg), "%s/%s/boot.log", RUN_DIR, name);
+       if (len < 0 || len >= sizeof(blg))
+       {
+               return 1;
+       }
+       fd = open(blg, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR);
+       if (fd == -1)
+       {
+               return 1;
+       }
+       return fd;
+}
+
+/**
+ * stop guest, unmount mounts
+ */
+static void destroy(private_guest_t *this)
+{
+       stop(this);
+       umount_unionfs(this->name);
+       free(this->name);
+       free(this->kernel);
+       free(this->master);
+       free(this);
+}
+
+/**
+ * create the guest instance, including required dirs and mounts 
+ */
+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.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))
+       {
+               DBG1("mounting guest unionfs for %s failed.", name);
+               free(this);
+               return NULL;
+       }
+       
+       this->name = strdup(name);
+       this->kernel = strdup(kernel);
+       this->master = strdup(master);
+       this->mem = mem;
+       this->pid = 0;
+       this->bootlog = open_bootlog(name);
+
+       return &this->public;
+}
+
diff --git a/src/dumm/guest.h b/src/dumm/guest.h
new file mode 100644 (file)
index 0000000..39a72c0
--- /dev/null
@@ -0,0 +1,23 @@
+#ifndef GUEST_H
+#define GUEST_H
+
+#include <library.h>
+#include <utils/linked_list.h>
+
+typedef struct guest_t guest_t;
+
+struct guest_t {
+       
+       char* (*get_name) (guest_t *this);
+       
+       bool (*start) (guest_t *this);
+       
+       bool (*stop) (guest_t *this);
+       
+       void (*destroy) (guest_t *this);
+};
+
+guest_t *guest_create(char *name, char *kernel, char *master, int mem);
+
+
+#endif /* GUEST_H */
diff --git a/src/dumm/main.c b/src/dumm/main.c
new file mode 100644 (file)
index 0000000..26896c2
--- /dev/null
@@ -0,0 +1,228 @@
+#define _GNU_SOURCE
+
+#include <stdio.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <library.h>
+#include <readline/readline.h>
+#include <readline/history.h>
+
+#include "dumm.h"
+
+/**
+ * show usage information (program arguments)
+ */
+static void usage()
+{
+       printf("Usage:\n");
+       printf("  --dir|-d <path>            set working dir to <path>\n");
+       printf("  --help|-h                  show this help\n");
+}
+
+/**
+ * show usage information (commandline arguments)
+ */
+static void help()
+{
+       printf("start name=<name> [options]   start a guest named <name>\n");
+       printf("                              additional options:\n");
+       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("help                          show this help\n");
+       printf("quit                          kill quests and exit\n");
+}
+
+/**
+ * start an UML guest
+ */
+static void start(umli_t *umli, char *line)
+{
+       enum {
+               NAME = 0,
+               MASTER,
+               KERNEL,
+               MEMORY,
+       };
+       char *const opts[] = {
+               [NAME] = "name",
+               [MASTER] = "master",
+               [KERNEL] = "kernel",
+               [MEMORY] = "memory",
+               NULL
+       };
+       char *value;
+       char *name = NULL;
+       char *kernel = NULL;
+       char *master = NULL;
+       int mem = 0;
+       
+       while (TRUE)
+       {
+               switch (getsubopt(&line, opts, &value))
+               {
+                       case NAME:
+                               name = value;
+                               continue;
+                       case MASTER:
+                               master = value;
+                               continue;
+                       case KERNEL:
+                               kernel = value;
+                               continue;
+                       case MEMORY:
+                               if (value)
+                               {
+                                       mem = atoi(value);
+                               }
+                               continue;
+                       default:
+                               break;
+               }
+               break;
+       }
+       if (name == NULL)
+       {
+               printf("option 'name' is required.\n");
+               help();
+               return;
+       }
+       if (kernel == NULL)
+       {
+               kernel = "./linux";
+       }
+       if (master == NULL)
+       {
+               master = "master";
+       }
+       if (mem == 0)
+       {
+               mem = 128;
+       }
+       
+       if (umli->start_guest(umli, name, kernel, master, mem))
+       {
+               printf("starting guest '%s'\n", name);
+       }
+       else
+       {
+               printf("starting guest '%s' failed\n", name);
+       }
+}
+
+/**
+ * list running UML guests
+ */
+static void guests(umli_t *umli)
+{
+       iterator_t *iterator;
+       guest_t *guest;
+       
+       iterator = umli->create_guest_iterator(umli);
+       while (iterator->iterate(iterator, (void**)&guest))
+       {
+               printf("%s\n", guest->get_name(guest));
+       }
+       iterator->destroy(iterator);
+}
+
+/**
+ * main routine, parses args and reads from console
+ */
+int main(int argc, char *argv[])
+{
+       umli_t *umli;
+       char *line = NULL;
+
+       while (TRUE)
+       {
+               struct option options[] = {
+                       {"dir", 1, 0, 0},
+                       {"help", 0, 0, 0},
+                       {0, 0, 0, 0}
+               };
+               
+               switch (getopt_long(argc, argv, "d:h", options, NULL)) 
+               {
+                       case -1:
+                               break;
+                       case 'd':
+                               if (chdir(optarg))
+                               {
+                                       printf("changing to directory '%s' failed.\n", optarg);
+                                       return 1;
+                               }
+                               continue;
+                       case 'h':
+                               usage();
+                               return 0;
+                       default:
+                               usage();
+                               return 1;
+               }
+               break;
+       }
+       
+       umli = umli_create();
+
+       while (TRUE)
+       {
+               enum {
+                       QUIT = 0,
+                       HELP,
+                       START,
+                       GUESTS,
+               };
+               char *const opts[] = {
+                       [QUIT] = "quit",
+                       [HELP] = "help",
+                       [START] = "start",
+                       [GUESTS] = "guests",
+                       NULL
+               };
+               char *pos, *value;
+               
+               free(line);
+               line = readline("dumm# ");
+               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();
+                               continue;
+                       case START:
+                               start(umli, pos);
+                               continue;
+                       case GUESTS:
+                               guests(umli);
+                               continue;
+                       default:
+                               printf("command unknown: '%s'\n", line);
+                               continue;
+               }
+               break;
+       }
+       umli->destroy(umli);
+       clear_history();
+       return 0;
+}
+