From 5c7da9d402a71992a5dc47c5ab25fd6f1479f5a7 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Tue, 24 Jul 2007 14:22:56 +0000 Subject: [PATCH] checked in first draft of "Dynamic Uml Mesh Modeler" --- src/dumm/Makefile | 3 + src/dumm/dumm.c | 74 +++++++++++++++++ src/dumm/dumm.h | 50 ++++++++++++ src/dumm/guest.c | 240 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/dumm/guest.h | 23 ++++++ src/dumm/main.c | 228 +++++++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 618 insertions(+) create mode 100644 src/dumm/Makefile create mode 100644 src/dumm/dumm.c create mode 100644 src/dumm/dumm.h create mode 100644 src/dumm/guest.c create mode 100644 src/dumm/guest.h create mode 100644 src/dumm/main.c diff --git a/src/dumm/Makefile b/src/dumm/Makefile new file mode 100644 index 0000000..39bdb0b --- /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 0000000..7536bce --- /dev/null +++ b/src/dumm/dumm.c @@ -0,0 +1,74 @@ +#include + +#include + +#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 0000000..f18817a --- /dev/null +++ b/src/dumm/dumm.h @@ -0,0 +1,50 @@ +#ifndef DUMM_H +#define DUMM_H + +#include +#include + +#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 0000000..2028823 --- /dev/null +++ b/src/dumm/guest.c @@ -0,0 +1,240 @@ +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#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 0000000..39a72c0 --- /dev/null +++ b/src/dumm/guest.h @@ -0,0 +1,23 @@ +#ifndef GUEST_H +#define GUEST_H + +#include +#include + +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 0000000..26896c2 --- /dev/null +++ b/src/dumm/main.c @@ -0,0 +1,228 @@ +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include + +#include "dumm.h" + +/** + * show usage information (program arguments) + */ +static void usage() +{ + printf("Usage:\n"); + printf(" --dir|-d set working dir to \n"); + printf(" --help|-h show this help\n"); +} + +/** + * show usage information (commandline arguments) + */ +static void help() +{ + printf("start name= [options] start a guest named \n"); + printf(" additional options:\n"); + printf(" kernel=\n"); + printf(" master=\n"); + printf(" memory=\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; +} + -- 2.7.4