extended UCI plugin by a simple control interface using a fifo
authorMartin Willi <martin@strongswan.org>
Fri, 18 Jul 2008 10:34:44 +0000 (10:34 -0000)
committerMartin Willi <martin@strongswan.org>
Fri, 18 Jul 2008 10:34:44 +0000 (10:34 -0000)
src/charon/plugins/uci/Makefile.am
src/charon/plugins/uci/uci_config.c
src/charon/plugins/uci/uci_control.c [new file with mode: 0644]
src/charon/plugins/uci/uci_control.h [new file with mode: 0644]
src/charon/plugins/uci/uci_parser.c
src/charon/plugins/uci/uci_plugin.c

index 47a55ae..0136bf5 100644 (file)
@@ -6,7 +6,8 @@ AM_CFLAGS = -rdynamic
 plugin_LTLIBRARIES = libstrongswan-uci.la
 libstrongswan_uci_la_SOURCES = \
   uci_plugin.h uci_plugin.c uci_parser.h uci_parser.c \
-  uci_config.h uci_config.c uci_creds.h uci_creds.c
+  uci_config.h uci_config.c uci_creds.h uci_creds.c \
+  uci_control.h uci_control.c
 libstrongswan_uci_la_LDFLAGS = -module
 libstrongswan_uci_la_LIBADD = -luci
 
index cc44eaa..b66957e 100644 (file)
@@ -352,9 +352,8 @@ uci_config_t *uci_config_create(uci_parser_t *parser)
        this->public.backend.create_ike_cfg_enumerator = (enumerator_t*(*)(backend_t*, host_t *me, host_t *other))create_ike_cfg_enumerator;
        this->public.backend.get_peer_cfg_by_name = (peer_cfg_t* (*)(backend_t*,char*))get_peer_cfg_by_name;
        this->public.destroy = (void(*)(uci_config_t*))destroy;
-       
        this->parser = parser;
-       
+
        return &this->public;
 }
 
diff --git a/src/charon/plugins/uci/uci_control.c b/src/charon/plugins/uci/uci_control.c
new file mode 100644 (file)
index 0000000..ebc1b20
--- /dev/null
@@ -0,0 +1,304 @@
+/*
+ * Copyright (C) 2008 Thomas Kallenberg
+ * Copyright (C) 2008 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.
+ *
+ * $Id$
+ */
+
+#define _GNU_SOURCE
+#include <string.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <pthread.h>
+
+#include "uci_control.h"
+
+#include <daemon.h>
+#include <processing/jobs/callback_job.h>
+
+#define FIFO_FILE "/var/run/charon.fifo"
+
+
+typedef struct private_uci_control_t private_uci_control_t;
+
+/**
+ * private data of uci_control_t
+ */
+struct private_uci_control_t {
+
+       /**
+        * Public part
+        */
+       uci_control_t public;
+
+       /**
+        * Job 
+        */
+       callback_job_t *job;
+};
+
+/**
+ * write answer to fifo
+ */
+static void write_fifo(private_uci_control_t *this, char *format, ...)
+{
+       va_list args;
+       FILE *out;
+       
+       out = fopen(FIFO_FILE, "w");
+       if (out)
+       {
+               va_start(args, format);
+               vfprintf(out, format, args);
+               va_end(args);
+               fclose(out);
+       }
+       else
+       {
+               DBG1(DBG_CFG, "writing to UCI fifo failed: %s", strerror(errno));
+       }
+}
+
+/**
+ * print IKE_SA status information
+ */
+static void status(private_uci_control_t *this, char *name)
+{
+       enumerator_t *configs, *sas;
+       iterator_t *children;
+       ike_sa_t *ike_sa;
+       child_sa_t *child_sa;
+       peer_cfg_t *peer_cfg;
+       char buf[2048];
+       FILE *out = NULL;
+       
+    configs = charon->backends->create_peer_cfg_enumerator(charon->backends);
+    while (configs->enumerate(configs, &peer_cfg))
+    {
+       if (name && !streq(name, peer_cfg->get_name(peer_cfg)))
+       {
+               continue;
+       }
+               sas = charon->controller->create_ike_sa_enumerator(charon->controller);
+               while (sas->enumerate(sas, &ike_sa))
+               {
+                       if (!streq(ike_sa->get_name(ike_sa), peer_cfg->get_name(peer_cfg)))
+                       {
+                               continue;
+                       }
+                       if (!out)
+                       {
+                               out = fmemopen(buf, sizeof(buf), "w");
+                               if (!out)
+                               {
+                                       continue;
+                               }
+                       }
+               fprintf(out, "%-8s %-20D %-16H ", ike_sa->get_name(ike_sa),
+                       ike_sa->get_other_id(ike_sa), ike_sa->get_other_host(ike_sa));
+               
+                       children = ike_sa->create_child_sa_iterator(ike_sa);
+                       while (children->iterate(children, (void**)&child_sa))
+                       {
+                               fprintf(out, "%#R",
+                                               child_sa->get_traffic_selectors(child_sa, FALSE));
+                       }
+                       children->destroy(children);
+               fprintf(out, "\n");
+               }
+               sas->destroy(sas);
+       }
+       configs->destroy(configs);
+       if (out)
+       {
+               fclose(out);
+               write_fifo(this, "%s", buf);
+       }
+       else
+       {
+               write_fifo(this, "");
+       }
+}
+
+/**
+ * Initiate an IKE_SA
+ */
+static void initiate(private_uci_control_t *this, char *name)
+{
+       peer_cfg_t *peer_cfg;
+       child_cfg_t *child_cfg;
+       enumerator_t *enumerator;
+
+       peer_cfg = charon->backends->get_peer_cfg_by_name(charon->backends, name);
+       if (peer_cfg)
+       {
+               enumerator = peer_cfg->create_child_cfg_enumerator(peer_cfg);
+               if (enumerator->enumerate(enumerator, &child_cfg) &&
+                       charon->controller->initiate(charon->controller, peer_cfg,
+                                                                                child_cfg->get_ref(child_cfg),
+                                                                                controller_cb_empty, NULL) == SUCCESS)
+               {
+                       write_fifo(this, "connection '%s' established\n", name);
+               }
+               else
+               {
+                       write_fifo(this, "establishing connection '%s' failed\n", name);
+               }
+               enumerator->destroy(enumerator);
+       }
+       else
+       {
+               write_fifo(this, "no connection named '%s' found\n", name);
+       }
+}
+
+/**
+ * terminate an IKE_SA
+ */
+static void terminate(private_uci_control_t *this, char *name)
+{
+       enumerator_t *enumerator;
+       ike_sa_t *ike_sa;
+       u_int id;
+
+       enumerator = charon->controller->create_ike_sa_enumerator(charon->controller);
+       while (enumerator->enumerate(enumerator, &ike_sa))
+       {
+               if (streq(name, ike_sa->get_name(ike_sa)))
+               {
+                       id = ike_sa->get_unique_id(ike_sa);
+                       enumerator->destroy(enumerator);
+                       charon->controller->terminate_ike(charon->controller, id,
+                                                                                         controller_cb_empty, NULL);
+                       write_fifo(this, "connection '%s' terminated\n", name);
+                       return;
+               }
+       }
+       enumerator->destroy(enumerator);
+       write_fifo(this, "no active connection named '%s'\n", name);
+}
+
+/**
+ * dispatch control request
+ */
+static void process(private_uci_control_t *this, char *message)
+{
+       enumerator_t* enumerator;
+       
+       enumerator = enumerator_create_token(message, " \n", "");
+       if (enumerator->enumerate(enumerator, &message))
+       {
+               if (streq(message, "status"))
+               {
+                       if (enumerator->enumerate(enumerator, &message))
+                       {
+                               status(this, message);
+                       }
+                       else
+                       {
+                               status(this, NULL);
+                       }
+               }
+               else if (streq(message, "up") &&
+                                enumerator->enumerate(enumerator, &message))
+               {
+                       initiate(this, message);
+               }
+               else if (streq(message, "down") && 
+                                enumerator->enumerate(enumerator, &message))
+               {
+                       terminate(this, message);
+               }
+               else
+               {
+                       write_fifo(this, "usage: status [<name<] | up <name> | down <name>\n"
+                                          "  status format: name peer-id peer-addr tunnel(s)\n");
+               }
+       }
+       enumerator->destroy(enumerator);
+}
+
+/**
+ * read from fifo
+ */
+static job_requeue_t receive(private_uci_control_t *this)
+{
+       while (TRUE)
+       {
+               char message[128];
+               int oldstate, len;
+               FILE *in;
+
+               memset(message, 0, sizeof(message));
+               pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &oldstate);
+               in = fopen(FIFO_FILE, "r");
+               pthread_setcancelstate(oldstate, NULL);
+               if (in)
+               {
+                       len = fread(message, 1, sizeof(message) - 1, in);
+                       fclose(in);
+                       if (len > 0)
+                       {
+                               process(this, message);
+                       }
+                       else
+                       {
+                               DBG1(DBG_DMN, "reading from UCI fifo failed: %s", strerror(errno));
+                       }
+               }
+               else
+               {
+                       DBG1(DBG_DMN, "opening UCI fifo failed: %s", strerror(errno));
+               }
+       }
+       return JOB_REQUEUE_NONE;
+}
+
+/**
+ * Implementation of uci_control_t.destroy
+ */
+static void destroy(private_uci_control_t *this)
+{
+       this->job->cancel(this->job);
+       unlink(FIFO_FILE);
+       free(this);
+}
+
+/**
+ * Described in header.
+ */
+uci_control_t *uci_control_create()
+{
+       private_uci_control_t *this = malloc_thing(private_uci_control_t);
+
+       this->public.destroy = (void(*)(uci_control_t*))destroy;
+
+       if (access(FIFO_FILE, F_OK) != 0 &&
+               mkfifo(FIFO_FILE, S_IRUSR|S_IWUSR) != 0)
+       {
+               DBG1(DBG_CFG, "creating UCI control fifo '%s' failed: %s",
+                        FIFO_FILE, strerror(errno));
+       }
+       else
+       {
+               this->job = callback_job_create((callback_job_cb_t)receive,
+                                                                               this, NULL, NULL);
+               charon->processor->queue_job(charon->processor, (job_t*)this->job);
+       }
+       return &this->public;
+}
+
diff --git a/src/charon/plugins/uci/uci_control.h b/src/charon/plugins/uci/uci_control.h
new file mode 100644 (file)
index 0000000..774c426
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2008 Thomas Kallenberg
+ * 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.
+ *
+ * $Id$
+ */
+
+/**
+ * @defgroup uci_control_t uci_control
+ * @{ @ingroup uci
+ */
+
+#ifndef UCI_CONTROL_H_
+#define UCI_CONTROL_H_
+
+typedef struct uci_control_t uci_control_t;
+
+struct uci_control_t {
+
+       /**
+        * Destroy the controller
+        */
+
+       void (*destroy)(uci_control_t *this);
+};
+
+/**
+ * Create a UCI based configuration backend.
+ */
+
+uci_control_t *uci_control_create();
+
+#endif /*UCI_CONTROL_H_@}*/
index 796d299..8f4acb9 100644 (file)
@@ -76,11 +76,18 @@ static bool section_enumerator_enumerate(section_enumerator_t *this, ...)
        
        va_start(args, this);
 
-       /* name is first parameter */   
        value = va_arg(args, char**);
        if (value)
        {
-               *value = uci_to_section(this->current)->type;
+               if (uci_lookup(this->ctx, &element, this->package,
+                                          this->current->name, "name") == UCI_OK)
+               {       /* use "name" attribute as config name if available ... */
+                       *value = uci_to_option(element)->value;
+               }
+               else
+               {       /* ... or the section name becomes config name */
+                       *value = uci_to_section(this->current)->type;
+               }
        }
        
        /* followed by keyword parameters */
index 9185238..fd84b22 100644 (file)
@@ -18,6 +18,7 @@
 #include "uci_plugin.h"
 #include "uci_config.h"
 #include "uci_creds.h"
+#include "uci_control.h"
 
 #include <daemon.h>
 
@@ -52,6 +53,11 @@ struct private_uci_plugin_t {
         * UCI parser wrapper
         */
        uci_parser_t *parser;
+
+       /**
+        * UCI control interface
+        */
+       uci_control_t *control;
 };
 
 /**
@@ -64,6 +70,7 @@ static void destroy(private_uci_plugin_t *this)
        this->config->destroy(this->config);
        this->creds->destroy(this->creds);
        this->parser->destroy(this->parser);
+       this->control->destroy(this->control);
        free(this);
 }
 
@@ -75,10 +82,11 @@ plugin_t *plugin_create()
        private_uci_plugin_t *this = malloc_thing(private_uci_plugin_t);
        
        this->public.plugin.destroy = (void(*)(plugin_t*))destroy;
-
+       
        this->parser = uci_parser_create(UCI_PACKAGE);
        this->config = uci_config_create(this->parser);
        this->creds = uci_creds_create(this->parser);
+       this->control = uci_control_create();
        charon->backends->add_backend(charon->backends, &this->config->backend);
        charon->credentials->add_set(charon->credentials, &this->creds->credential_set);