/* * 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 . * * 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 #include #include #include #include #include #include #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; }