aboutsummaryrefslogtreecommitdiff
path: root/libplanfahr/lpf-manager.c
diff options
context:
space:
mode:
Diffstat (limited to 'libplanfahr/lpf-manager.c')
-rw-r--r--libplanfahr/lpf-manager.c274
1 files changed, 274 insertions, 0 deletions
diff --git a/libplanfahr/lpf-manager.c b/libplanfahr/lpf-manager.c
new file mode 100644
index 0000000..e67ddf5
--- /dev/null
+++ b/libplanfahr/lpf-manager.c
@@ -0,0 +1,274 @@
+/*
+ * lpf-manager.h: manage planfahr providers
+ *
+ * Copyright (C) 2014 Guido Günther
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Author: Guido Günther <agx@sigxcpu.org>
+ */
+
+#include "lpf-provider.h"
+#include "lpf-manager.h"
+#include "lpf-priv.h"
+
+#include <glob.h>
+#include <string.h>
+
+#include <glib.h>
+#include <glib/gprintf.h>
+#include <gmodule.h>
+
+#define PROVIDER_LIBBASE "libplanfahr-provider-"
+
+
+/**
+ * SECTION:lpf-manager
+ * @short_description: Provider Manager
+ * @see_also: #LpfProvider
+ *
+ * A #LpfManager handles the different public transport information providers.
+ */
+
+
+G_DEFINE_TYPE (LpfManager, lpf_manager, G_TYPE_OBJECT)
+#define GET_PRIVATE(o) \
+ (G_TYPE_INSTANCE_GET_PRIVATE ((o), LPF_TYPE_MANAGER, LpfManagerPrivate))
+
+typedef struct _LpfManagerPrivate LpfManagerPrivate;
+struct _LpfManagerPrivate {
+ GSList *available; /* available providers */
+ GSList *active; /* active providers */
+};
+
+
+GQuark
+lpf_manager_error_quark (void)
+{
+ return g_quark_from_static_string ("lpf-manager-error-quark");
+}
+
+
+static gchar *plugin_path(const char *name)
+{
+ gchar *dir = NULL;
+ gchar *prefix = NULL;
+ gchar *ret;
+
+ dir = g_strdup(g_getenv(LPF_PROVIDERS_ENV));
+ if (!dir)
+ dir = g_strdup(LPF_PROVIDERS_DIR);
+ prefix = g_strjoin("/", dir, PROVIDER_LIBBASE, NULL);
+ ret = g_strjoin(NULL, prefix, name, ".", G_MODULE_SUFFIX, NULL);
+
+ g_free(dir);
+ g_free(prefix);
+ return ret;
+}
+
+/**
+ * lpf_manager_get_available_providers:
+ *
+ * Returns: (transfer full): List of available providers
+ */
+GStrv lpf_manager_get_available_providers(void)
+{
+ gint i, prefix_len;
+ glob_t globbuf;
+ gchar *pattern = NULL;
+ GStrv providers = NULL;
+ gchar **tmp;
+
+ if (!g_module_supported ()) {
+ g_warning ("GModules are not supported on your platform!");
+ return NULL;
+ }
+
+ pattern = plugin_path("*");
+ prefix_len = 1 + g_strrstr(pattern, "/") + strlen(PROVIDER_LIBBASE) - pattern ;
+ LPF_DEBUG("Looking for providers in %s", pattern);
+ if (glob(pattern, GLOB_NOSORT, NULL, &globbuf))
+ goto out;
+
+ if (!(providers = g_try_malloc (sizeof(gchar) * globbuf.gl_pathc + 1))) {
+ goto out;
+ }
+
+ for (i = 0; i < globbuf.gl_pathc; i++) {
+ tmp = g_strsplit(globbuf.gl_pathv[i] + prefix_len, ".", 2);
+ if (!tmp) {
+ providers[i] = NULL;
+ goto out;
+ }
+ providers[i] = g_strdup(tmp[0]);
+ g_strfreev(tmp);
+ }
+ providers[globbuf.gl_pathc] = NULL;
+
+ out:
+ globfree(&globbuf);
+ g_free (pattern);
+ return providers;
+}
+
+static LpfProvider*
+load_provider (const char *path, GError **error)
+{
+ LpfProvider *provider = NULL;
+ GModule *module;
+ LpfProviderCreateFunc create_func;
+ int *major_provider_version, *minor_provider_version;
+
+ module = g_module_open (path, G_MODULE_BIND_LAZY);
+ if (!module) {
+ g_set_error (error,
+ LPF_MANAGER_ERROR,
+ LPF_MANAGER_ERROR_ACTIVATION_FAILED,
+ "Could not load provider %s: %s", path, g_module_error ());
+ return NULL;
+ }
+
+ if (!g_module_symbol (module, "lpf_provider_major_version",(gpointer *) &major_provider_version)) {
+ g_set_error (error,
+ LPF_MANAGER_ERROR,
+ LPF_MANAGER_ERROR_ACTIVATION_FAILED,
+ "Could not load provider %s: Missing major version info", path);
+ goto out;
+ }
+
+ if (*major_provider_version != LPF_PROVIDER_MAJOR_VERSION) {
+ g_set_error (error,
+ LPF_MANAGER_ERROR,
+ LPF_MANAGER_ERROR_ACTIVATION_FAILED,
+ "Could not load provider %s: Provider major version %d, %d is required",
+ path, *major_provider_version, LPF_PROVIDER_MAJOR_VERSION);
+ goto out;
+ }
+
+ if (!g_module_symbol (module, "lpf_provider_minor_version", (gpointer *) &minor_provider_version)) {
+ g_set_error (error,
+ LPF_MANAGER_ERROR,
+ LPF_MANAGER_ERROR_ACTIVATION_FAILED,
+ "Could not load provider %s: Missing minor version info", path);
+ goto out;
+ }
+
+ if (*minor_provider_version != LPF_PROVIDER_MINOR_VERSION) {
+ g_set_error (error,
+ LPF_MANAGER_ERROR,
+ LPF_MANAGER_ERROR_ACTIVATION_FAILED,
+ "Could not load provider %s: Provider minor version %d, %d is required",
+ path, *minor_provider_version, LPF_PROVIDER_MINOR_VERSION);
+ goto out;
+ }
+
+ if (!g_module_symbol (module, "lpf_provider_create", (gpointer *) &create_func)) {
+ g_set_error (error,
+ LPF_MANAGER_ERROR,
+ LPF_MANAGER_ERROR_ACTIVATION_FAILED,
+ "Could not load provider %s: %s", path, g_module_error ());
+ goto out;
+ }
+
+ provider = (*create_func) ();
+ if (provider) {
+ g_object_weak_ref (G_OBJECT (provider), (GWeakNotify) g_module_close, module);
+ g_message ("Loaded provider %s", lpf_provider_get_name (provider));
+ } else {
+ g_set_error (error,
+ LPF_MANAGER_ERROR,
+ LPF_MANAGER_ERROR_ACTIVATION_FAILED,
+ "Could not load provider %s: initialization failed", path);
+ }
+ out:
+ if (!provider)
+ g_module_close (module);
+
+ return provider;
+}
+
+/**
+ * lpf_manager_activate_provider:
+ * @self: a #LpfManager
+ * @name: the name of the provider to activate
+ * @error: a #GError for errorreporting or %NULL
+ *
+ * Activate the given provider
+ *
+ * Returns: (transfer none): The activated #LpfProvider
+ */
+LpfProvider *lpf_manager_activate_provider(LpfManager *self,
+ const gchar *name,
+ GError **error)
+{
+ gchar *path;
+ LpfProvider *provider;
+ LpfManagerPrivate *priv = GET_PRIVATE (self);
+
+ path = plugin_path(name);
+ provider = load_provider (path, error);
+ if (provider) {
+ lpf_provider_activate(provider, G_OBJECT(self));
+ priv->active = g_slist_prepend (priv->active, provider);
+ }
+ g_free(path);
+ return provider;
+}
+
+static void
+deactivate_provider(gpointer provider, gpointer user_data)
+{
+ LpfManager *manager = LPF_MANAGER (user_data);
+ lpf_manager_deactivate_provider (manager, provider);
+}
+
+void
+lpf_manager_deactivate_provider(LpfManager *self, LpfProvider *provider)
+{
+ LPF_DEBUG ("Deactivating provider %s", lpf_provider_get_name (provider));
+ lpf_provider_deactivate (provider, G_OBJECT(self));
+}
+
+static void
+lpf_manager_dispose(GObject *object)
+{
+ LpfManager *self = LPF_MANAGER(object);
+ LpfManagerPrivate *priv = GET_PRIVATE (self);
+ GObjectClass *parent_class = G_OBJECT_CLASS (lpf_manager_parent_class);
+
+ /* We need to do this before dropping the ref on applet */
+ g_slist_foreach (priv->active, deactivate_provider, self);
+ g_slist_free (priv->active);
+
+ if (parent_class->dispose != NULL)
+ parent_class->dispose (object);
+}
+
+static void
+lpf_manager_class_init (LpfManagerClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->dispose = lpf_manager_dispose;
+ g_type_class_add_private (klass, sizeof (LpfManagerPrivate));
+}
+
+static void
+lpf_manager_init (LpfManager *self)
+{
+ LpfManagerPrivate *priv = GET_PRIVATE (self);
+ priv->active = NULL;
+ priv->available = NULL;
+}