/* * 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 "dumm.h" /** * 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; /** 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->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); } /** * 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->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->dir); if (dir == NULL) { return; } while ((ent = readdir(dir))) { if (streq(ent->d_name, ".") || streq(ent->d_name, "..")) { continue; } guest = guest_load(this->dir, ent->d_name); if (guest) { 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.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->guests = linked_list_create(); this->bridges = linked_list_create(); add_instance(this); load_guests(this); return &this->public; }