loading of subjectPublicKeyInfo wrapped keys using KEY_ANY (openssl format)
[strongswan.git] / src / dumm / dumm.c
index 7536bce..b9a2814 100644 (file)
+/*
+ * 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;
 };
 
-static guest_t* start_guest(private_dumm_t *this, char *name, char *kernel,
-                                                       char *master, int mem)
+/**
+ * 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(name, kernel, master, mem);
+       guest = guest_create(this->guest_dir, name, kernel, master, mem);
        if (guest)
        {
-               if (guest->start(guest))
-               {
-                       this->guests->insert_last(this->guests, guest);         
-                       return guest;
-               }
-               guest->destroy(guest);
+               this->guests->insert_last(this->guests, guest);
        }
-       return NULL;
+       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**)&current))
+       {
+               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);
 }
 
 /**
- * check for a directory, create if it does not exist
+ * load all guests in our working dir
  */
-static bool makedir(char *dir)
+static void load_guests(private_dumm_t *this)
 {
-       struct stat st;
+       DIR *dir;
+       struct dirent *ent;
+       guest_t *guest;
        
-       if (stat(dir, &st) != 0)
+       dir = opendir(this->guest_dir);
+       if (dir == NULL)
        {
-               return mkdir(dir, S_IRWXU) == 0;
+               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);
+               }
        }
-       return S_ISDIR(st.st_mode);
+       closedir(dir);
 }
 
-dumm_t *dumm_create()
+/**
+ * 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.start_guest = (void*)start_guest;
-       this->public.create_guest_iterator = (void*)create_guest_iterator;
-       this->public.destroy = (void*)destroy;
+       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;
        
-       if (!makedir(HOST_DIR) || !makedir(MOUNT_DIR) || !makedir(RUN_DIR))
+       this->destroying = FALSE;
+       if (*dir == '/' || getcwd(cwd, sizeof(cwd)) == 0)
        {
-               free(this);
+               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;
        }
        
-       this->guests = linked_list_create();
+       load_guests(this);
        return &this->public;
 }