diff options
Diffstat (limited to 'src/dumm/dumm.c')
-rw-r--r-- | src/dumm/dumm.c | 391 |
1 files changed, 391 insertions, 0 deletions
diff --git a/src/dumm/dumm.c b/src/dumm/dumm.c new file mode 100644 index 000000000..b9a2814e6 --- /dev/null +++ b/src/dumm/dumm.c @@ -0,0 +1,391 @@ +/* + * 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 <sys/stat.h> +#include <unistd.h> +#include <stdio.h> +#include <dirent.h> +#include <errno.h> + +#include <debug.h> + +#include "dumm.h" + +#define PERME (S_IRWXU | S_IRWXG) +#define GUEST_DIR "guests" +#define TEMPLATE_DIR "templates" +#define TEMPLATE_DIR_DIR "diff" + +/** + * instances of dumm, used to deliver signals + */ +static linked_list_t *instances = NULL; + +typedef struct private_dumm_t private_dumm_t; + +struct private_dumm_t { + /** public dumm interface */ + dumm_t public; + /** working dir */ + char *dir; + /** directory of guests */ + char *guest_dir; + /** directory of templates */ + char *template_dir; + /** directory of loaded template */ + char *template; + /** list of managed guests */ + linked_list_t *guests; + /** list of managed bridges */ + linked_list_t *bridges; + /** do not catch signals if we are destroying */ + bool destroying; +}; + +/** + * Implementation of dumm_t.create_guest. + */ +static guest_t* create_guest(private_dumm_t *this, char *name, char *kernel, + char *master, int mem) +{ + guest_t *guest; + + guest = guest_create(this->guest_dir, name, kernel, master, mem); + if (guest) + { + this->guests->insert_last(this->guests, guest); + } + return guest; +} + +/** + * Implementation of dumm_t.create_guest_iterator. + */ +static iterator_t* create_guest_iterator(private_dumm_t *this) +{ + return this->guests->create_iterator(this->guests, TRUE); +} + +/** + * Implementation of dumm_t.create_bridge. + */ +static bridge_t* create_bridge(private_dumm_t *this, char *name) +{ + bridge_t *bridge; + + bridge = bridge_create(name); + if (bridge) + { + this->bridges->insert_last(this->bridges, bridge); + } + return bridge; +} + +/** + * Implementation of dumm_t.create_bridge_iterator. + */ +static iterator_t* create_bridge_iterator(private_dumm_t *this) +{ + return this->bridges->create_iterator(this->bridges, TRUE); +} + +/** + * disable the currently enabled template + */ +static void clear_template(private_dumm_t *this) +{ + iterator_t *iterator, *ifaces; + guest_t *guest; + iface_t *iface; + + free(this->template); + this->template = NULL; + + iterator = this->guests->create_iterator(this->guests, TRUE); + while (iterator->iterate(iterator, (void**)&guest)) + { + guest->load_template(guest, NULL); + ifaces = guest->create_iface_iterator(guest); + while (ifaces->iterate(ifaces, (void**)&iface)) + { + ifaces->remove(ifaces); + iface->destroy(iface); + } + ifaces->destroy(ifaces); + } + iterator->destroy(iterator); +} + +/** + * Implementation of dumm_t.load_template. + */ +static bool load_template(private_dumm_t *this, char *name) +{ + iterator_t *iterator; + guest_t *guest; + char dir[PATH_MAX]; + size_t len; + + clear_template(this); + + if (name == NULL) + { + return TRUE; + } + + free(this->template); + asprintf(&this->template, "%s/%s", this->template_dir, name); + len = snprintf(dir, sizeof(dir), "%s/%s", this->template, TEMPLATE_DIR_DIR); + if (len < 0 || len >= sizeof(dir)) + { + return FALSE; + } + + if (access(this->template, F_OK) != 0) + { /* does not exist, create template */ + if (mkdir(this->template, PERME) != 0) + { + DBG1("creating template directory '%s' failed: %m", this->template); + return FALSE; + } + if (mkdir(dir, PERME) != 0) + { + DBG1("creating template overlay directory '%s' failed: %m", dir); + return FALSE; + } + } + iterator = this->guests->create_iterator(this->guests, TRUE); + while (iterator->iterate(iterator, (void**)&guest)) + { + if (!guest->load_template(guest, dir)) + { + iterator->destroy(iterator); + clear_template(this); + return FALSE; + } + } + iterator->destroy(iterator); + return TRUE; +} + +/** + * signal handler + */ +void signal_handler(int sig, siginfo_t *info, void *ucontext) +{ + if (sig == SIGCHLD) + { + switch (info->si_code) + { + case CLD_EXITED: + case CLD_KILLED: + case CLD_DUMPED: + { + private_dumm_t *this; + guest_t *guest; + iterator_t *iterator, *guests; + + iterator = instances->create_iterator(instances, TRUE); + while (iterator->iterate(iterator, (void**)&this)) + { + if (this->destroying) + { + continue; + } + guests = this->guests->create_iterator(this->guests, TRUE); + while (guests->iterate(guests, (void**)&guest)) + { + if (guest->get_pid(guest) == info->si_pid) + { + guest->sigchild(guest); + break; + } + } + guests->destroy(guests); + } + iterator->destroy(iterator); + break; + } + default: + break; + } + + } + /* SIGHUP is currently just ignored */ +} + +/** + * add a dumm instance + */ +static void add_instance(private_dumm_t *this) +{ + if (instances == NULL) + { + struct sigaction action; + + instances = linked_list_create(); + + memset(&action, 0, sizeof(action)); + action.sa_sigaction = signal_handler; + action.sa_flags = SA_SIGINFO; + + if (sigaction(SIGCHLD, &action, NULL) != 0 || + sigaction(SIGHUP, &action, NULL) != 0) + { + DBG1("installing signal handler failed!"); + } + } + instances->insert_last(instances, this); +} + +/** + * remove a dumm instance + */ +static void remove_instance(private_dumm_t *this) +{ + iterator_t *iterator; + private_dumm_t *current; + + iterator = instances->create_iterator(instances, TRUE); + while (iterator->iterate(iterator, (void**)¤t)) + { + if (current == this) + { + iterator->remove(iterator); + break; + } + } + iterator->destroy(iterator); + if (instances->get_count(instances) == 0) + { + instances->destroy(instances); + instances = NULL; + } +} + +/** + * Implementation of dumm_t.destroy + */ +static void destroy(private_dumm_t *this) +{ + iterator_t *iterator; + guest_t *guest; + + this->bridges->destroy_offset(this->bridges, offsetof(bridge_t, destroy)); + + iterator = this->guests->create_iterator(this->guests, TRUE); + while (iterator->iterate(iterator, (void**)&guest)) + { + guest->stop(guest); + } + iterator->destroy(iterator); + + this->destroying = TRUE; + this->guests->destroy_offset(this->guests, offsetof(guest_t, destroy)); + free(this->guest_dir); + free(this->template_dir); + free(this->template); + free(this->dir); + remove_instance(this); + free(this); +} + +/** + * load all guests in our working dir + */ +static void load_guests(private_dumm_t *this) +{ + DIR *dir; + struct dirent *ent; + guest_t *guest; + + dir = opendir(this->guest_dir); + if (dir == NULL) + { + return; + } + + while ((ent = readdir(dir))) + { + if (streq(ent->d_name, ".") || streq(ent->d_name, "..")) + { + continue; + } + guest = guest_load(this->guest_dir, ent->d_name); + if (guest) + { + DBG1("loaded guest '%s'", ent->d_name); + this->guests->insert_last(this->guests, guest); + } + else + { + DBG1("loading guest in directory '%s' failed, skipped", ent->d_name); + } + } + closedir(dir); +} + +/** + * create a dumm instance + */ +dumm_t *dumm_create(char *dir) +{ + char cwd[PATH_MAX]; + private_dumm_t *this = malloc_thing(private_dumm_t); + + this->public.create_guest = (guest_t*(*)(dumm_t*,char*,char*,char*,int))create_guest; + this->public.create_guest_iterator = (iterator_t*(*)(dumm_t*))create_guest_iterator; + this->public.create_bridge = (bridge_t*(*)(dumm_t*, char *name))create_bridge; + this->public.create_bridge_iterator = (iterator_t*(*)(dumm_t*))create_bridge_iterator; + this->public.load_template = (bool(*)(dumm_t*, char *name))load_template; + this->public.destroy = (void(*)(dumm_t*))destroy; + + this->destroying = FALSE; + if (*dir == '/' || getcwd(cwd, sizeof(cwd)) == 0) + { + this->dir = strdup(dir); + } + else + { + asprintf(&this->dir, "%s/%s", cwd, dir); + } + this->template = NULL; + asprintf(&this->guest_dir, "%s/%s", this->dir, GUEST_DIR); + asprintf(&this->template_dir, "%s/%s", this->dir, TEMPLATE_DIR); + this->guests = linked_list_create(); + this->bridges = linked_list_create(); + + add_instance(this); + + if (mkdir(this->guest_dir, PERME) < 0 && errno != EEXIST) + { + DBG1("creating guest directory '%s' failed: %m", this->guest_dir); + destroy(this); + return NULL; + } + if (mkdir(this->template_dir, PERME) < 0 && errno != EEXIST) + { + DBG1("creating template directory '%s' failed: %m", this->template_dir); + destroy(this); + return NULL; + } + + load_guests(this); + return &this->public; +} + |