Update to upstream 1.0.1
[profile/ivi/gsignond.git] / src / daemon / plugins / gsignond-plugin-proxy-factory.c
1 /* vi: set et sw=4 ts=4 cino=t0,(0: */
2 /* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 /*
4  * This file is part of gsignond
5  *
6  * Copyright (C) 2012-2014 Intel Corporation.
7  *
8  * Contact: Alexander Kanavin <alex.kanavin@gmail.com>
9  *
10  * This library is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU Lesser General Public
12  * License as published by the Free Software Foundation; either
13  * version 2.1 of the License, or (at your option) any later version.
14  *
15  * This library is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18  * Lesser General Public License for more details.
19  *
20  * You should have received a copy of the GNU Lesser General Public
21  * License along with this library; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
23  * 02110-1301 USA
24  */
25
26 #include <string.h>
27 #include <stdio.h>
28
29 #include "config.h"
30
31 #include "gsignond/gsignond-log.h"
32 #include "gsignond-plugin-proxy-factory.h"
33 #include "gsignond-plugin-remote.h"
34
35 G_DEFINE_TYPE (GSignondPluginProxyFactory, gsignond_plugin_proxy_factory, G_TYPE_OBJECT);
36
37
38 enum
39 {
40     PROP_0,
41     
42     PROP_CONFIG,
43     
44     N_PROPERTIES
45 };
46
47 static GParamSpec *obj_properties[N_PROPERTIES] = { NULL, };
48
49
50 static gchar** _get_plugin_names_from_loader(const gchar* loader_path)
51 {
52     gchar* command_line = g_strdup_printf("%s --list-plugins", loader_path);
53     gchar* standard_output = NULL;
54     gchar* standard_error = NULL;
55     gint exit_status;
56     GError* error = NULL;
57
58     if (g_spawn_command_line_sync(command_line, &standard_output, &standard_error,
59         &exit_status, &error)) {
60         DBG("Loader %s returned plugin list %s", loader_path, standard_output);
61         gchar** plugin_list = g_strsplit(standard_output, "\n", 0);
62         g_free(command_line);
63         g_free(standard_output);
64         g_free(standard_error);
65         return plugin_list;
66     } else {
67         DBG("Loader %s returned exit status %d, error %s", loader_path,
68             exit_status, error->message);
69         g_error_free(error);
70         g_free(command_line);
71         return NULL;
72     }
73 }
74
75 static void _add_plugins(GSignondPluginProxyFactory* self, const gchar* loader_path, gchar** plugins)
76 {
77     DBG ("Checking mechanisms of plugins provided by %s", loader_path);
78     gchar **plugin_iter = plugins;
79     while (*plugin_iter) {
80         GSignondPlugin* plugin = GSIGNOND_PLUGIN (
81                 gsignond_plugin_remote_new (loader_path, *plugin_iter));
82         if (plugin != NULL) {
83             gchar* plugin_type;
84             gchar** mechanisms;
85             g_object_get(plugin,
86                         "type", &plugin_type,
87                         "mechanisms", &mechanisms,
88                          NULL);
89             if (g_strcmp0 (plugin_type, *plugin_iter) == 0) {
90                 const gchar* loader = g_hash_table_lookup(self->methods_to_loader_paths,
91                                                           plugin_type);
92                 // Do not replace plugins provided by gsignond-plugind with
93                 // 3rd party plugins
94                 if (loader && g_str_has_suffix(loader, "/gsignond-plugind")) {
95                     DBG("Do not replace plugin %s with plugin provided by loader %s",
96                         plugin_type, loader_path);
97                     g_strfreev(mechanisms);
98                 } else {
99                     DBG("Adding plugin %s to plugin enumeration", plugin_type);
100                     g_hash_table_insert(self->methods_to_mechanisms,
101                         g_strdup(plugin_type), mechanisms);
102                     g_hash_table_insert(self->methods_to_loader_paths,
103                         g_strdup(plugin_type), g_strdup(loader_path));
104                 }
105             } else {
106                 DBG("Plugin returned type property %s, which does not match requested type %s",
107                     plugin_type, *plugin_iter);
108                 g_strfreev(mechanisms);
109             }
110             g_free(plugin_type);
111             g_object_unref(plugin);
112         }
113         plugin_iter++;
114     }
115 }
116
117 static void _insert_method(gchar* method, gchar*** method_iter_p)
118 {
119     *(*method_iter_p) = method;
120     (*method_iter_p)++;
121 }
122
123
124 static void _enumerate_plugins(GSignondPluginProxyFactory* self)
125 {
126     const gchar *loaders_path = GSIGNOND_PLUGINLOADERS_DIR;
127 #   ifdef ENABLE_DEBUG
128     const gchar* env_val = g_getenv("SSO_BIN_DIR");
129     if (env_val)
130         loaders_path = env_val;
131 #   endif
132
133     GDir* loaders_dir = g_dir_open(loaders_path, 0, NULL);
134     if (loaders_dir == NULL) {
135         WARN ("plugin directory empty");
136         return;
137     }
138
139     DBG ("Getting lists of plugins from loaders in %s (factory=%p)", loaders_path, self);
140     while (1) {
141         const gchar* loader_name = g_dir_read_name(loaders_dir);
142         if (loader_name == NULL)
143             break;
144         gchar* loader_path = g_build_filename(loaders_path, loader_name, NULL);
145         gchar** plugins = _get_plugin_names_from_loader(loader_path);
146         if (plugins != NULL) {
147             _add_plugins(self, loader_path, plugins);
148             g_strfreev(plugins);
149         }
150         g_free(loader_path);
151     }
152     g_dir_close(loaders_dir);
153
154     // make a flat list of available plugin types
155     int n_plugins = g_hash_table_size(self->methods_to_mechanisms);
156     self->methods = g_new0(gchar*, n_plugins + 1);
157     gchar **method_iter = self->methods;
158
159     GList* keys = g_hash_table_get_keys(self->methods_to_mechanisms);
160     g_list_foreach(keys, (GFunc)_insert_method, &method_iter);
161
162     g_list_free(keys);
163 }
164
165 static GObject *
166 gsignond_plugin_proxy_factory_constructor (GType                  gtype,
167                                    guint                  n_properties,
168                                    GObjectConstructParam *properties)
169 {
170   GObject *obj;
171
172   {
173     /* Always chain up to the parent constructor */
174     obj = G_OBJECT_CLASS (gsignond_plugin_proxy_factory_parent_class)->constructor (
175         gtype, n_properties, properties);
176   }
177   
178   return obj;
179 }
180
181 static void
182 gsignond_plugin_proxy_factory_set_property (GObject      *object,
183                                        guint         property_id,
184                                        const GValue *value,
185                                        GParamSpec   *pspec)
186 {
187     GSignondPluginProxyFactory *self = GSIGNOND_PLUGIN_PROXY_FACTORY (object);
188     switch (property_id)
189     {
190         case PROP_CONFIG:
191             g_assert (self->config == NULL);
192             self->config = g_value_dup_object (value);
193             break;
194         default:
195             G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
196             break;
197     }
198 }
199
200 static void
201 gsignond_plugin_proxy_factory_get_property (GObject    *object,
202                                        guint       prop_id,
203                                        GValue     *value,
204                                        GParamSpec *pspec)
205 {
206     GSignondPluginProxyFactory *self = GSIGNOND_PLUGIN_PROXY_FACTORY (object);
207     
208     switch (prop_id)
209     {
210         case PROP_CONFIG:
211             g_value_set_object (value, self->config);
212             break;
213         default:
214             G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
215             break;
216     }
217 }
218
219 static void
220 gsignond_plugin_proxy_factory_dispose (GObject *gobject)
221 {
222     GSignondPluginProxyFactory *self = GSIGNOND_PLUGIN_PROXY_FACTORY (gobject);
223
224     if (self->config) {
225         g_object_unref (self->config);
226         self->config = NULL;
227     }
228
229   /* Chain up to the parent class */
230   G_OBJECT_CLASS (gsignond_plugin_proxy_factory_parent_class)->dispose (gobject);
231 }
232
233 static void
234 gsignond_plugin_proxy_factory_finalize (GObject *gobject)
235 {
236     GSignondPluginProxyFactory *self = GSIGNOND_PLUGIN_PROXY_FACTORY (gobject);
237
238     if (self->plugins) {
239         g_hash_table_destroy (self->plugins);
240         self->plugins = NULL;
241     }
242     if (self->methods_to_mechanisms) {
243         g_hash_table_destroy (self->methods_to_mechanisms);
244         self->methods_to_mechanisms = NULL;
245     }
246     if (self->methods_to_loader_paths) {
247         g_hash_table_destroy (self->methods_to_loader_paths);
248         self->methods_to_loader_paths = NULL;
249     }
250     if (self->methods) {
251         g_free (self->methods);
252         self->methods = NULL;
253     }
254
255     /* Chain up to the parent class */
256     G_OBJECT_CLASS (gsignond_plugin_proxy_factory_parent_class)->finalize (gobject);
257 }
258
259
260 static void
261 gsignond_plugin_proxy_factory_class_init (GSignondPluginProxyFactoryClass *klass)
262 {
263     GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
264     
265     gobject_class->constructor = gsignond_plugin_proxy_factory_constructor;
266     gobject_class->set_property = gsignond_plugin_proxy_factory_set_property;
267     gobject_class->get_property = gsignond_plugin_proxy_factory_get_property;
268     gobject_class->dispose = gsignond_plugin_proxy_factory_dispose;
269     gobject_class->finalize = gsignond_plugin_proxy_factory_finalize;
270
271     obj_properties[PROP_CONFIG] = g_param_spec_object ("config",
272                                                        "config",
273                                                        "Configuration object",
274                                                        GSIGNOND_TYPE_CONFIG,
275                                                        G_PARAM_CONSTRUCT_ONLY |
276                                                        G_PARAM_READWRITE |
277                                                        G_PARAM_STATIC_STRINGS);
278     
279
280     g_object_class_install_properties (gobject_class,
281                                        N_PROPERTIES,
282                                        obj_properties);
283 }
284
285 static void
286 gsignond_plugin_proxy_factory_init (GSignondPluginProxyFactory *self)
287 {
288     self->methods_to_mechanisms = g_hash_table_new_full((GHashFunc)g_str_hash,
289                                              (GEqualFunc)g_str_equal,
290                                              (GDestroyNotify)g_free,
291                                              (GDestroyNotify)g_strfreev);
292
293     self->methods_to_loader_paths = g_hash_table_new_full((GHashFunc)g_str_hash,
294                                              (GEqualFunc)g_str_equal,
295                                              (GDestroyNotify)g_free,
296                                              (GDestroyNotify)g_free);
297
298     self->plugins = g_hash_table_new_full ((GHashFunc)g_str_hash,
299                                            (GEqualFunc)g_str_equal,
300                                            (GDestroyNotify)g_free,
301                                            (GDestroyNotify)g_object_unref);
302
303     self->methods = NULL;
304 }
305
306 GSignondPluginProxyFactory* 
307 gsignond_plugin_proxy_factory_new(GSignondConfig *config)
308 {
309     GSignondPluginProxyFactory* proxy = g_object_new(
310                                               GSIGNOND_TYPE_PLUGIN_PROXY_FACTORY,
311                                               "config", config,
312                                               NULL);
313     return proxy;
314 }
315
316 static gboolean
317 _find_proxy_by_pointer (gpointer key, gpointer value, gpointer userdata)
318 {
319     if (userdata == value) {
320         g_free (key);
321         return TRUE;
322     }
323     return FALSE;
324 }
325
326 static void
327 _remove_dead_proxy (gpointer data, GObject *dead_proxy)
328 {
329     GSignondPluginProxyFactory *factory = GSIGNOND_PLUGIN_PROXY_FACTORY(data);
330     if (factory) {
331         g_hash_table_foreach_steal (factory->plugins, 
332                 _find_proxy_by_pointer, dead_proxy);
333     }
334 }
335
336 static void
337 _proxy_toggle_ref_cb (gpointer userdata, GObject *proxy, gboolean is_last_ref)
338 {
339     /* start/stop timeout timer */
340     gsignond_disposable_set_auto_dispose (GSIGNOND_DISPOSABLE (proxy), is_last_ref);
341
342     if (is_last_ref) g_object_weak_ref (proxy, _remove_dead_proxy, userdata);
343     else g_object_weak_unref (proxy, _remove_dead_proxy, userdata);
344 }
345
346 GSignondPluginProxy*
347 gsignond_plugin_proxy_factory_get_plugin(GSignondPluginProxyFactory* factory,
348                                          const gchar* plugin_type)
349 {
350     g_return_val_if_fail (factory && GSIGNOND_IS_PLUGIN_PROXY_FACTORY(factory), NULL);
351     g_return_val_if_fail (plugin_type, NULL);
352
353     GSignondPluginProxy* proxy = NULL;
354
355     if (factory->methods == NULL) {
356         _enumerate_plugins (factory);
357     }
358
359     if (g_hash_table_lookup(factory->methods_to_mechanisms, plugin_type) == NULL) {
360         DBG("Plugin not known %s", plugin_type);
361         return NULL;
362     }
363
364     proxy = g_hash_table_lookup(factory->plugins, plugin_type);
365     if (proxy != NULL) {
366         DBG("get existing plugin %s -> %p", plugin_type, proxy);
367         g_object_ref(proxy);
368         return proxy;
369     }
370
371     proxy = gsignond_plugin_proxy_new(g_hash_table_lookup(factory->methods_to_loader_paths, plugin_type), plugin_type,
372                                       gsignond_config_get_integer (factory->config, GSIGNOND_CONFIG_PLUGIN_TIMEOUT));
373     if (proxy == NULL) {
374         return NULL;
375     }
376     g_hash_table_insert(factory->plugins, g_strdup (plugin_type), proxy);
377     DBG("get new plugin %s -> %p", plugin_type, proxy);
378     g_object_add_toggle_ref(G_OBJECT(proxy), _proxy_toggle_ref_cb, factory);
379
380     return proxy;
381 }
382
383 const gchar** 
384 gsignond_plugin_proxy_factory_get_plugin_types(
385    GSignondPluginProxyFactory* factory)
386 {
387         if (factory->methods == NULL) {
388                 _enumerate_plugins (factory);
389         }
390     return (const gchar**)factory->methods;
391 }
392    
393 const gchar**
394 gsignond_plugin_proxy_factory_get_plugin_mechanisms(
395    GSignondPluginProxyFactory* factory, const gchar* plugin_type)
396 {
397     g_return_val_if_fail(factory->methods_to_mechanisms, NULL);
398
399     if (factory->methods == NULL) {
400         _enumerate_plugins (factory);
401     }
402
403     return g_hash_table_lookup(factory->methods_to_mechanisms, plugin_type);
404 }