aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/dumm/Makefile3
-rw-r--r--src/dumm/dumm.c74
-rw-r--r--src/dumm/dumm.h50
-rw-r--r--src/dumm/guest.c240
-rw-r--r--src/dumm/guest.h23
-rw-r--r--src/dumm/main.c228
6 files changed, 618 insertions, 0 deletions
diff --git a/src/dumm/Makefile b/src/dumm/Makefile
new file mode 100644
index 000000000..39bdb0b4a
--- /dev/null
+++ b/src/dumm/Makefile
@@ -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
index 000000000..7536bce57
--- /dev/null
+++ b/src/dumm/dumm.c
@@ -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
index 000000000..f18817abc
--- /dev/null
+++ b/src/dumm/dumm.h
@@ -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
index 000000000..202882367
--- /dev/null
+++ b/src/dumm/guest.c
@@ -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
index 000000000..39a72c081
--- /dev/null
+++ b/src/dumm/guest.h
@@ -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
index 000000000..26896c2df
--- /dev/null
+++ b/src/dumm/main.c
@@ -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;
+}
+