/* * Copyright (C) 2008-2009 Tobias Brunner * Copyright (C) 2007 Martin Willi * HSR 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 #include "dumm.h" #define PERME (S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) #define GUEST_DIR "guests" #define TEMPLATE_DIR "templates" 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 loaded template */ char *template; /** list of managed guests */ linked_list_t *guests; /** list of managed bridges */ linked_list_t *bridges; }; METHOD(dumm_t, create_guest, guest_t*, private_dumm_t *this, char *name, char *kernel, char *master, char *args) { guest_t *guest; guest = guest_create(this->guest_dir, name, kernel, master, args); if (guest) { this->guests->insert_last(this->guests, guest); } return guest; } METHOD(dumm_t, create_guest_enumerator, enumerator_t*, private_dumm_t *this) { return this->guests->create_enumerator(this->guests); } METHOD(dumm_t, delete_guest, void, private_dumm_t *this, guest_t *guest) { if (this->guests->remove(this->guests, guest, NULL)) { char buf[512]; int len; len = snprintf(buf, sizeof(buf), "rm -Rf %s/%s", this->guest_dir, guest->get_name(guest)); guest->destroy(guest); if (len > 8 && len < 512) { ignore_result(system(buf)); } } } METHOD(dumm_t, create_bridge, bridge_t*, private_dumm_t *this, char *name) { bridge_t *bridge; bridge = bridge_create(name); if (bridge) { this->bridges->insert_last(this->bridges, bridge); } return bridge; } METHOD(dumm_t, create_bridge_enumerator, enumerator_t*, private_dumm_t *this) { return this->bridges->create_enumerator(this->bridges); } METHOD(dumm_t, delete_bridge, void, private_dumm_t *this, bridge_t *bridge) { if (this->bridges->remove(this->bridges, bridge, NULL)) { bridge->destroy(bridge); } } METHOD(dumm_t, add_overlay, bool, private_dumm_t *this, char *dir) { enumerator_t *enumerator; guest_t *guest; if (dir == NULL) { return TRUE; } if (strlen(dir) > PATH_MAX) { DBG1(DBG_LIB, "overlay directory string '%s' is too long", dir); return FALSE; } if (access(dir, F_OK) != 0) { if (!mkdir_p(dir, PERME)) { DBG1(DBG_LIB, "creating overlay directory '%s' failed: %m", dir); return FALSE; } } enumerator = this->guests->create_enumerator(this->guests); while (enumerator->enumerate(enumerator, (void**)&guest)) { char guest_dir[PATH_MAX]; int len = snprintf(guest_dir, sizeof(guest_dir), "%s/%s", dir, guest->get_name(guest)); if (len < 0 || len >= sizeof(guest_dir)) { goto error; } if (access(guest_dir, F_OK) != 0) { if (!mkdir_p(guest_dir, PERME)) { DBG1(DBG_LIB, "creating overlay directory for guest '%s' failed: %m", guest->get_name(guest)); goto error; } } if (!guest->add_overlay(guest, guest_dir)) { goto error; } } enumerator->destroy(enumerator); return TRUE; error: enumerator->destroy(enumerator); this->public.del_overlay(&this->public, dir); return FALSE; } METHOD(dumm_t, del_overlay, bool, private_dumm_t *this, char *dir) { bool ret = FALSE; enumerator_t *enumerator; guest_t *guest; enumerator = this->guests->create_enumerator(this->guests); while (enumerator->enumerate(enumerator, (void**)&guest)) { char guest_dir[PATH_MAX]; int len = snprintf(guest_dir, sizeof(guest_dir), "%s/%s", dir, guest->get_name(guest)); if (len < 0 || len >= sizeof(guest_dir)) { continue; } ret = guest->del_overlay(guest, guest_dir) || ret; } enumerator->destroy(enumerator); return ret; } METHOD(dumm_t, pop_overlay, bool, private_dumm_t *this) { bool ret = FALSE; enumerator_t *enumerator; guest_t *guest; enumerator = this->guests->create_enumerator(this->guests); while (enumerator->enumerate(enumerator, (void**)&guest)) { ret = guest->pop_overlay(guest) || ret; } enumerator->destroy(enumerator); return ret; } /** * disable the currently enabled template */ static void clear_template(private_dumm_t *this) { if (this->template) { del_overlay(this, this->template); free(this->template); this->template = NULL; } } METHOD(dumm_t, load_template, bool, private_dumm_t *this, char *name) { clear_template(this); if (name == NULL) { return TRUE; } if (strlen(name) > PATH_MAX) { DBG1(DBG_LIB, "template name '%s' is too long", name); return FALSE; } if (strchr(name, '/') != NULL) { DBG1(DBG_LIB, "template name '%s' must not contain '/' characters", name); return FALSE; } if (asprintf(&this->template, "%s/%s", TEMPLATE_DIR, name) < 0) { this->template = NULL; return FALSE; } if (access(this->template, F_OK) != 0) { if (!mkdir_p(this->template, PERME)) { DBG1(DBG_LIB, "creating template directory '%s' failed: %m", this->template); return FALSE; } } return add_overlay(this, this->template); } /** * Template directory enumerator */ typedef struct { /** implements enumerator_t */ enumerator_t public; /** directory enumerator */ enumerator_t *inner; } template_enumerator_t; METHOD(enumerator_t, template_enumerate, bool, template_enumerator_t *this, va_list args) { struct stat st; char *rel, **template; VA_ARGS_VGET(args, template); while (this->inner->enumerate(this->inner, &rel, NULL, &st)) { if (S_ISDIR(st.st_mode) && *rel != '.') { *template = rel; return TRUE; } } return FALSE; } METHOD(enumerator_t, template_enumerator_destroy, void, template_enumerator_t *this) { this->inner->destroy(this->inner); free(this); } METHOD(dumm_t, create_template_enumerator, enumerator_t*, private_dumm_t *this) { template_enumerator_t *enumerator; INIT(enumerator, .public = { .enumerate = enumerator_enumerate_default, .venumerate = _template_enumerate, .destroy = (void*)_template_enumerator_destroy, }, .inner = enumerator_create_directory(TEMPLATE_DIR), ); if (!enumerator->inner) { free(enumerator); return enumerator_create_empty(); } return &enumerator->public; } METHOD(dumm_t, destroy, void, private_dumm_t *this) { enumerator_t *enumerator; guest_t *guest; this->bridges->destroy_offset(this->bridges, offsetof(bridge_t, destroy)); enumerator = this->guests->create_enumerator(this->guests); while (enumerator->enumerate(enumerator, (void**)&guest)) { guest->stop(guest, NULL); } enumerator->destroy(enumerator); while (this->guests->remove_last(this->guests, (void**)&guest) == SUCCESS) { guest->destroy(guest); } this->guests->destroy(this->guests); free(this->guest_dir); free(this->template); free(this->dir); 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 (*ent->d_name == '.') { /* skip ".", ".." and hidden files (such as ".svn") */ continue; } guest = guest_load(this->guest_dir, ent->d_name); if (guest) { this->guests->insert_last(this->guests, guest); } else { DBG1(DBG_LIB, "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; INIT(this, .public = { .create_guest = _create_guest, .create_guest_enumerator = _create_guest_enumerator, .delete_guest = _delete_guest, .create_bridge = _create_bridge, .create_bridge_enumerator = _create_bridge_enumerator, .delete_bridge = _delete_bridge, .add_overlay = _add_overlay, .del_overlay = _del_overlay, .pop_overlay = _pop_overlay, .load_template = _load_template, .create_template_enumerator = _create_template_enumerator, .destroy = _destroy, }, ); if (dir && *dir == '/') { this->dir = strdup(dir); } else { if (getcwd(cwd, sizeof(cwd)) == NULL) { free(this); return NULL; } if (dir) { if (asprintf(&this->dir, "%s/%s", cwd, dir) < 0) { this->dir = NULL; } } else { this->dir = strdup(cwd); } } if (asprintf(&this->guest_dir, "%s/%s", this->dir, GUEST_DIR) < 0) { this->guest_dir = NULL; } this->guests = linked_list_create(); this->bridges = linked_list_create(); if (this->dir == NULL || this->guest_dir == NULL || (mkdir(this->guest_dir, PERME) < 0 && errno != EEXIST)) { DBG1(DBG_LIB, "creating guest directory '%s' failed: %m", this->guest_dir); destroy(this); return NULL; } load_guests(this); return &this->public; }