~cyphermox/ethos/debian

« back to all changes in this revision

Viewing changes to ethos/ethos-manager.c

  • Committer: Mathieu Trudel-Lapierre
  • Date: 2011-07-27 00:48:52 UTC
  • Revision ID: mathieu-tl@ubuntu.com-20110727004852-t7bz3ye3ugttb9aq
Clean tree to get to a merge-mode package tree.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/* ethos-manager.c
2
 
 *
3
 
 * Copyright (C) 2009 Christian Hergert <chris@dronelabs.com>
4
 
 *
5
 
 * This library is free software; you can redistribute it and/or
6
 
 * modify it under the terms of the GNU Library General Public
7
 
 * License as published by the Free Software Foundation; either
8
 
 * version 2 of the License, or (at your option) any later version.
9
 
 *
10
 
 * This library is distributed in the hope that it will be useful,
11
 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13
 
 * Library General Public License for more details.
14
 
 *
15
 
 * You should have received a copy of the GNU Library General Public
16
 
 * License along with this library; if not, write to the Free Software
17
 
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 
18
 
 * 02110-1301 USA
19
 
 */
20
 
 
21
 
#if HAVE_CONFIG_H
22
 
#include "config.h"
23
 
#endif
24
 
 
25
 
#include <string.h>
26
 
#include <stdlib.h>
27
 
#include <gmodule.h>
28
 
 
29
 
#include "ethos-error.h"
30
 
#include "ethos-manager.h"
31
 
#include "ethos-plugin-loader.h"
32
 
#include "ethos-plugin-info-private.h"
33
 
 
34
 
/**
35
 
 * SECTION:ethos-manager
36
 
 * @title: EthosManager
37
 
 * @short_description: plugin management during runtime
38
 
 *
39
 
 * The #EthosManager is responsible for managing plugins during runtime.  It can be used to
40
 
 * load and unload plugins as well as configure where and how plugins should be loaded.
41
 
 *
42
 
 * During runtime, the app_name and plugin_dirs should both be set.  The app_name is used
43
 
 * to derive the plugin filenames and how to parse them.  The plugin_dirs are used to locate
44
 
 * plugins.
45
 
 */
46
 
 
47
 
struct _EthosManagerPrivate
48
 
{
49
 
        gboolean     initialized;
50
 
        gchar       *app_name;
51
 
        gchar      **plugin_dirs;
52
 
        GList       *plugin_loaders;
53
 
        GList       *plugin_info;
54
 
        GHashTable  *plugin_info_hash;
55
 
        GHashTable  *plugins;
56
 
        GHashTable  *deps_cache;
57
 
};
58
 
 
59
 
enum
60
 
{
61
 
        INITIALIZED,
62
 
        PLUGIN_LOADED,
63
 
        PLUGIN_UNLOADED,
64
 
        LAST_SIGNAL
65
 
};
66
 
 
67
 
G_DEFINE_TYPE (EthosManager, ethos_manager, G_TYPE_OBJECT);
68
 
 
69
 
static guint signals[LAST_SIGNAL] = {0,};
70
 
 
71
 
/**
72
 
 * ethos_manager_new:
73
 
 *
74
 
 * Creates a new instance of #EthosManager.  There should only be one of these
75
 
 * created per process.
76
 
 *
77
 
 * Return value: the newly created #EthosManager instance.
78
 
 */
79
 
EthosManager*
80
 
ethos_manager_new (void)
81
 
{
82
 
        return g_object_new (ETHOS_TYPE_MANAGER, NULL);
83
 
}
84
 
 
85
 
/**
86
 
 * ethos_manager_new_full:
87
 
 * @app_name: The name of the application (Capitalized)
88
 
 * @plugin_dirs: An array of strings containing directories to locate plugins
89
 
 *
90
 
 * Creates a new #EthosManager instance and sets the application name and
91
 
 * plugin directories to traverse to locate plugins.
92
 
 *
93
 
 * Return value: The newly created #EthosManager instance.
94
 
 */
95
 
EthosManager*
96
 
ethos_manager_new_full (const gchar  *app_name,
97
 
                        gchar       **plugin_dirs)
98
 
{
99
 
        EthosManager *manager;
100
 
 
101
 
        manager = ethos_manager_new ();
102
 
        ethos_manager_set_app_name (manager, app_name);
103
 
        ethos_manager_set_plugin_dirs (manager, plugin_dirs);
104
 
 
105
 
        return manager;
106
 
}
107
 
 
108
 
static EthosPluginLoader*
109
 
ethos_manager_create_plugin_loader (EthosManager *manager,
110
 
                                    const gchar  *filename)
111
 
{
112
 
        EthosPluginLoader* (*create_plugin_loader) (void) = NULL;
113
 
        EthosPluginLoader  *plugin_loader;
114
 
        GModule            *module;
115
 
 
116
 
        module = g_module_open (filename, G_MODULE_BIND_LAZY);
117
 
 
118
 
        if (!module) {
119
 
                g_warning ("%s: %s", filename, g_module_error ());
120
 
                return NULL;
121
 
        }
122
 
 
123
 
        if (!g_module_symbol (module, "ethos_plugin_loader_register",
124
 
                              (gpointer*)&create_plugin_loader)) {
125
 
                g_warning ("%s: %s", filename, g_module_error ());
126
 
                if (!g_module_close (module))
127
 
                        g_warning ("%s: %s", filename, g_module_error ());
128
 
                return NULL;
129
 
        }
130
 
 
131
 
        if (create_plugin_loader == NULL) {
132
 
                g_warning ("%s: ethos_plugin_loader_register is NULL", filename);
133
 
                if (!g_module_close (module))
134
 
                        g_warning ("%s: %s", filename, g_module_error ());
135
 
                return NULL;
136
 
        }
137
 
 
138
 
        plugin_loader = create_plugin_loader ();
139
 
 
140
 
        if (plugin_loader && !ETHOS_IS_PLUGIN_LOADER (plugin_loader)) {
141
 
                g_warning ("%s: ethos_plugin_loader_register returned "
142
 
                           "something other than an EthosPluginLoader",
143
 
                           filename);
144
 
                if (!g_module_close (module))
145
 
                        g_warning ("%s: %s", filename, g_module_error ());
146
 
                return NULL;
147
 
        }
148
 
 
149
 
        ethos_plugin_loader_initialize (plugin_loader, manager);
150
 
 
151
 
        return plugin_loader;
152
 
}
153
 
 
154
 
/*
155
 
 * ethos_manager_discover_plugin_loaders:
156
 
 * @manager: An #EthosManager
157
 
 *
158
 
 * Discovers the available plugin loaders.
159
 
 *
160
 
 * Return value: A #GList containing #EthosPluginLoader instances.
161
 
 */
162
 
static GList*
163
 
ethos_manager_discover_plugin_loaders (EthosManager *manager)
164
 
{
165
 
        EthosManagerPrivate *priv;
166
 
        GDir                *dir;
167
 
        const gchar         *loaders_dir;
168
 
        const gchar         *filename;
169
 
        gchar               *abspath;
170
 
        GList               *loaders = NULL;
171
 
        EthosPluginLoader   *loader;
172
 
 
173
 
        g_return_val_if_fail (ETHOS_IS_MANAGER (manager), NULL);
174
 
 
175
 
        priv = manager->priv;
176
 
 
177
 
        if (g_getenv ("ETHOS_PLUGIN_LOADERS_DIR") != NULL)
178
 
                loaders_dir = g_getenv ("ETHOS_PLUGIN_LOADERS_DIR");
179
 
        else
180
 
                loaders_dir = ETHOS_PLUGIN_LOADERS_DIR;
181
 
 
182
 
        if (!g_file_test (loaders_dir, G_FILE_TEST_IS_DIR)) {
183
 
                g_warning ("plugin-loaders directory not found: %s",
184
 
                           loaders_dir);
185
 
                return NULL;
186
 
        }
187
 
 
188
 
        if (!(dir = g_dir_open (loaders_dir, 0, NULL))) {
189
 
                g_warning ("plugin-loaders directory not accessable: %s",
190
 
                           loaders_dir);
191
 
                return NULL;
192
 
        }
193
 
 
194
 
        while (NULL != (filename = g_dir_read_name (dir))) {
195
 
                if (g_str_has_suffix (filename, "." G_MODULE_SUFFIX)) {
196
 
                        abspath = g_build_filename (loaders_dir, filename, NULL);
197
 
                        loader = ethos_manager_create_plugin_loader (manager, abspath);
198
 
                        if (loader != NULL)
199
 
                                loaders = g_list_prepend (loaders, loader);
200
 
                        g_free (abspath);
201
 
                }
202
 
        }
203
 
 
204
 
        g_dir_close (dir);
205
 
 
206
 
        return loaders;
207
 
}
208
 
 
209
 
/*
210
 
 * ethos_manager_discover_plugins:
211
 
 * @manager: An #EthosManager
212
 
 *
213
 
 * Discovers all of the plugins found within the plugin directories.
214
 
 *
215
 
 * Return value: A #GList containing #EthosPluginInfo instances that were
216
 
 *   created from plugin files in the plugin directories.
217
 
 */
218
 
static GList*
219
 
ethos_manager_discover_plugins (EthosManager *manager)
220
 
{
221
 
        EthosManagerPrivate  *priv;
222
 
        EthosPluginInfo      *plugin_info;
223
 
        GList                *plugins = NULL;
224
 
        GDir                 *dir;
225
 
        gchar               **plugin_dirs;
226
 
        const gchar          *filename;
227
 
        gchar                *lower;
228
 
        gchar                *abspath;
229
 
        gchar                *suffix;
230
 
        gchar                *group;
231
 
        gint                  n_dirs;
232
 
        gint                  i;
233
 
 
234
 
        g_return_val_if_fail (ETHOS_IS_MANAGER (manager), NULL);
235
 
 
236
 
        priv = manager->priv;
237
 
 
238
 
        plugin_dirs = g_strdupv (priv->plugin_dirs);
239
 
        n_dirs = plugin_dirs ? g_strv_length (plugin_dirs) : 0;
240
 
        lower = g_utf8_strdown (priv->app_name, -1);
241
 
        suffix = g_strconcat (".", lower, "-plugin", NULL);
242
 
        group = g_strconcat (priv->app_name, " Plugin", NULL);
243
 
 
244
 
        for (i = 0; i < n_dirs; i++) {
245
 
                if (!(dir = g_dir_open (plugin_dirs [i], 0, NULL))) {
246
 
                        g_warning ("Could not access plugin directory: %s",
247
 
                                   plugin_dirs [i]);
248
 
                        continue;
249
 
                }
250
 
 
251
 
                while (NULL != (filename = g_dir_read_name (dir))) {
252
 
                        if (!g_str_has_suffix (filename, suffix))
253
 
                                continue;
254
 
 
255
 
                        abspath = g_build_filename (plugin_dirs [i], filename, NULL);
256
 
 
257
 
                        plugin_info = ethos_plugin_info_new ();
258
 
                        ethos_plugin_info_set_filename (plugin_info, abspath);
259
 
 
260
 
                        /* overwrite the last '.' in the filename and re-use
261
 
                         * the same string as the id. */
262
 
                        if (g_strrstr (filename, "."))
263
 
                                *g_strrstr (filename, ".") = '\0';
264
 
 
265
 
                        ethos_plugin_info_set_id (plugin_info, filename);
266
 
 
267
 
                        if (ethos_plugin_info_load_from_file (plugin_info, group, abspath, NULL))
268
 
                                plugins = g_list_prepend (plugins, g_object_ref (plugin_info));
269
 
 
270
 
                        g_object_unref (plugin_info);
271
 
                        g_free (abspath);
272
 
                }
273
 
 
274
 
                g_dir_close (dir);
275
 
        }
276
 
 
277
 
        g_strfreev (plugin_dirs);
278
 
        g_free (suffix);
279
 
        g_free (lower);
280
 
        g_free (group);
281
 
 
282
 
        return plugins;
283
 
}
284
 
 
285
 
/**
286
 
 * ethos_manager_initialize:
287
 
 * @manager: An #EthosManager
288
 
 *
289
 
 * Initialize the plugin manager.  This will result in the manager looking for
290
 
 * all of the available plugins that it can find in the registered plugin
291
 
 * directories.
292
 
 */
293
 
void
294
 
ethos_manager_initialize (EthosManager *manager)
295
 
{
296
 
        EthosManagerPrivate *priv;
297
 
        GList               *iter;
298
 
 
299
 
        g_return_if_fail (ETHOS_IS_MANAGER (manager));
300
 
 
301
 
        priv = manager->priv;
302
 
 
303
 
        if (priv->initialized)
304
 
                return;
305
 
 
306
 
        priv->initialized = TRUE;
307
 
        priv->plugin_loaders = ethos_manager_discover_plugin_loaders (manager);
308
 
        priv->plugin_info = ethos_manager_discover_plugins (manager);
309
 
 
310
 
        for (iter = priv->plugin_info; iter; iter = iter->next) {
311
 
                g_hash_table_insert (priv->plugin_info_hash,
312
 
                                     g_strdup (ethos_plugin_info_get_id (iter->data)),
313
 
                                     g_object_ref (iter->data));
314
 
        }
315
 
 
316
 
        g_signal_emit (manager, signals [INITIALIZED], 0);
317
 
}
318
 
 
319
 
/**
320
 
 * ethos_manager_unload:
321
 
 * @manager: An #EthosManager
322
 
 *
323
 
 * Unloads all of the plugins.
324
 
 */
325
 
void
326
 
ethos_manager_unload (EthosManager *manager)
327
 
{
328
 
        g_warning ("%s is not yet implemented", __func__);
329
 
}
330
 
 
331
 
/**
332
 
 * ethos_manager_lookup_plugin_loader:
333
 
 * @manager: An #EthosManager
334
 
 * @name: The name of the #EthosPluginLoader
335
 
 *
336
 
 * Attempts to find the #EthosPluginLoader matching @name.
337
 
 *
338
 
 * Return value: An #EthosPluginLoader or %NULL.
339
 
 */
340
 
EthosPluginLoader*
341
 
ethos_manager_lookup_plugin_loader (EthosManager *manager,
342
 
                                    const gchar  *name)
343
 
{
344
 
        EthosManagerPrivate *priv;
345
 
        GList               *iter;
346
 
        const gchar         *loader_name;
347
 
        gchar               *loader_down = NULL,
348
 
                            *name_down   = NULL;
349
 
        EthosPluginLoader   *loader      = NULL;
350
 
 
351
 
        g_return_val_if_fail (ETHOS_IS_MANAGER (manager), NULL);
352
 
 
353
 
        priv = manager->priv;
354
 
 
355
 
        for (iter = priv->plugin_loaders; iter; iter = iter->next) {
356
 
                /* To make things a bit more resilient, we will check to see if the developer
357
 
                 * accidentally used Loader=C or Loader=Vala and replace that with NULL to
358
 
                 * indicate we should use the default shared library loader.
359
 
                 */
360
 
 
361
 
                loader_name = ethos_plugin_loader_get_name (iter->data);
362
 
 
363
 
                if (loader_name)
364
 
                        loader_down = g_ascii_strdown (loader_name, -1);
365
 
                if (name)
366
 
                        name_down = g_ascii_strdown (name, -1);
367
 
 
368
 
                if (g_strcmp0 (name_down, "c") == 0 || g_strcmp0 (name_down, "vala") == 0) {
369
 
                        g_free (name_down);
370
 
                        name_down = NULL;
371
 
                }
372
 
 
373
 
                if (g_strcmp0 (name_down, loader_down) == 0)
374
 
                        loader = iter->data;
375
 
 
376
 
                g_free (loader_down);
377
 
                g_free (name_down);
378
 
                loader_down = NULL;
379
 
                name_down = NULL;
380
 
 
381
 
                if (loader)
382
 
                        return loader;
383
 
        }
384
 
 
385
 
        return NULL;
386
 
}
387
 
 
388
 
typedef enum
389
 
{
390
 
        OP_NONE = 0,
391
 
        OP_LT   = 1 << 0,
392
 
        OP_GT   = 1 << 1,
393
 
        OP_EQ   = 1 << 2,
394
 
        OP_LTE  = OP_LT | OP_EQ,
395
 
        OP_GTE  = OP_GT | OP_EQ,
396
 
} Operator;
397
 
 
398
 
typedef struct
399
 
{
400
 
        gchar    *name;
401
 
        gchar    *version;
402
 
        Operator  oper;
403
 
} Dependency;
404
 
 
405
 
typedef struct
406
 
{
407
 
        gint major;
408
 
        gint minor;
409
 
        gint rev;
410
 
} Version;
411
 
 
412
 
static GList*
413
 
parse_deps (EthosPluginInfo *plugin_info)
414
 
{
415
 
        const gchar *depstr;
416
 
        const gchar *name;
417
 
        GScanner    *scanner;
418
 
        GList       *deps = NULL;
419
 
        Dependency  *dep;
420
 
        gboolean     has_oper;
421
 
 
422
 
        depstr = ethos_plugin_info_get_dependencies (plugin_info);
423
 
        if (!depstr || g_strcmp0 (depstr, "") == 0)
424
 
                return NULL;
425
 
 
426
 
        name = ethos_plugin_info_get_id (plugin_info);
427
 
        scanner = g_scanner_new (NULL);
428
 
        g_scanner_input_text (scanner, depstr, strlen (depstr));
429
 
 
430
 
        scanner->config->scan_float = 0;
431
 
 
432
 
        if (g_scanner_get_next_token (scanner) != G_TOKEN_IDENTIFIER) {
433
 
                g_warning ("%s: Invalid Dependencies", name);
434
 
                goto cleanup;
435
 
        }
436
 
 
437
 
        do {
438
 
                has_oper = TRUE;
439
 
                dep = g_slice_new0 (Dependency);
440
 
                dep->name = g_strdup (g_scanner_cur_value (scanner).v_string);
441
 
 
442
 
                /* check for first of two possible operator tokens */
443
 
                switch (g_scanner_peek_next_token (scanner)) {
444
 
                case '=':
445
 
                        dep->oper = OP_EQ;
446
 
                        break;
447
 
                case '<':
448
 
                        dep->oper = OP_LT;
449
 
                        break;
450
 
                case '>':
451
 
                        dep->oper = OP_GT;
452
 
                        break;
453
 
                default:
454
 
                        has_oper = FALSE;
455
 
                        break;
456
 
                }
457
 
 
458
 
                if (has_oper) {
459
 
                        g_scanner_get_next_token (scanner);
460
 
 
461
 
                        /* check for the second possible operator token */
462
 
                        switch (g_scanner_peek_next_token (scanner)) {
463
 
                        case '=':
464
 
                                dep->oper |= OP_EQ;
465
 
                                break;
466
 
                        case '<':
467
 
                                dep->oper |= OP_LT;
468
 
                                break;
469
 
                        case '>':
470
 
                                dep->oper |= OP_GT;
471
 
                                break;
472
 
                        default:
473
 
                                has_oper = FALSE;
474
 
                                break;
475
 
                        }
476
 
 
477
 
                        if (has_oper)
478
 
                                g_scanner_get_next_token (scanner);
479
 
 
480
 
                        GTokenType t;
481
 
                        int a = 0, b = 0, c = 0;
482
 
                        gboolean has_a = FALSE, has_b = FALSE, has_c = FALSE;
483
 
 
484
 
                        t = g_scanner_peek_next_token (scanner);
485
 
 
486
 
                        if (t != G_TOKEN_INT) {
487
 
                                g_warning ("Invalid dependency version string for %s", dep->name);
488
 
                        }
489
 
                        else
490
 
                        {
491
 
                                do {
492
 
                                        t = g_scanner_get_next_token (scanner);
493
 
                                        if (t == G_TOKEN_INT) {
494
 
                                                if (!has_a) {
495
 
                                                        a = g_scanner_cur_value (scanner).v_int;
496
 
                                                        has_a = TRUE;
497
 
                                                }
498
 
                                                else if (!has_b) {
499
 
                                                        b = g_scanner_cur_value (scanner).v_int;
500
 
                                                        has_b = TRUE;
501
 
                                                }
502
 
                                                else if (!has_c) {
503
 
                                                        c = g_scanner_cur_value (scanner).v_int;
504
 
                                                        has_c = TRUE;
505
 
                                                }
506
 
                                                else {
507
 
                                                        g_warning ("Invalid version string for %s",
508
 
                                                                   ethos_plugin_info_get_id (plugin_info));
509
 
                                                }
510
 
                                        }
511
 
                                        t = g_scanner_peek_next_token (scanner);
512
 
                                } while (t == G_TOKEN_INT || t == '.');
513
 
                        }
514
 
 
515
 
                        dep->version = g_strdup_printf ("%d.%d.%d", a, b, c);
516
 
                }
517
 
 
518
 
                deps = g_list_append (deps, dep);
519
 
        }
520
 
        while (g_scanner_get_next_token (scanner) == G_TOKEN_IDENTIFIER);
521
 
 
522
 
cleanup:
523
 
        g_scanner_destroy (scanner);
524
 
        return deps;
525
 
 
526
 
}
527
 
 
528
 
static void
529
 
free_deps (GList *deps)
530
 
{
531
 
        GList *iter;
532
 
        if (deps == NULL)
533
 
                return;
534
 
        for (iter = deps; iter; iter = iter->next) {
535
 
                g_free (((Dependency*)iter->data)->name);
536
 
                g_slice_free (Dependency, iter->data);
537
 
        }
538
 
        g_list_free (deps);
539
 
}
540
 
 
541
 
/**
542
 
 * ethos_manager_lookup_plugin_info:
543
 
 * @manager: An #EthosManager
544
 
 * @name: the plugin name
545
 
 *
546
 
 * Retrieves the #EthosPluginInfo found for the plugin named @name.
547
 
 *
548
 
 * Return value: The #EthosPluginInfo or %NULL.
549
 
 */
550
 
EthosPluginInfo*
551
 
ethos_manager_lookup_plugin_info (EthosManager *manager,
552
 
                                  const gchar  *name)
553
 
{
554
 
        EthosManagerPrivate *priv;
555
 
        GList               *iter;
556
 
 
557
 
        g_return_val_if_fail (ETHOS_IS_MANAGER (manager), NULL);
558
 
 
559
 
        priv = manager->priv;
560
 
 
561
 
        for (iter = priv->plugin_info; iter; iter = iter->next) {
562
 
                if (g_strcmp0 (name, ethos_plugin_info_get_id (iter->data)) == 0)
563
 
                        return iter->data;
564
 
        }
565
 
 
566
 
        return NULL;
567
 
}
568
 
 
569
 
static gboolean
570
 
parse_version (const gchar *str,
571
 
               Version     *version)
572
 
{
573
 
        gchar **parts;
574
 
 
575
 
        g_return_val_if_fail (str != NULL, FALSE);
576
 
        g_return_val_if_fail (version != NULL, FALSE);
577
 
 
578
 
        parts = g_strsplit (str, ".", -1);
579
 
 
580
 
        version->major = 0;
581
 
        version->minor = 0;
582
 
        version->rev = 0;
583
 
 
584
 
        if (parts [0]) {
585
 
                version->major = atoi (parts [0]);
586
 
                if (parts [1]) {
587
 
                        version->minor = atoi (parts [1]);
588
 
                        if (parts [2])
589
 
                                version->rev = atoi (parts [2]);
590
 
                }
591
 
        }
592
 
 
593
 
        g_strfreev (parts);
594
 
 
595
 
        return TRUE;
596
 
}
597
 
 
598
 
static gint
599
 
version_cmp (Version *a,
600
 
             Version *b)
601
 
{
602
 
        if (a == b)
603
 
                return 0;
604
 
        else if (a->major > b->major
605
 
                 || (a->major == b->major && a->minor > b->minor)
606
 
                 || (a->major == b->major && a->minor == b->minor && a->rev > b->rev))
607
 
                return 1;
608
 
        else if (a->major < b->major
609
 
                 || (a->major == b->major && a->minor < b->minor)
610
 
                 || (a->major == b->major && a->minor == b->minor && a->rev < b->rev))
611
 
                return -1;
612
 
        else if (a->major == b->major && a->minor == b->minor && a->rev == b->rev)
613
 
                return 0;
614
 
        else
615
 
                g_assert_not_reached ();
616
 
}
617
 
 
618
 
/**
619
 
 * ethos_manager_load_plugin:
620
 
 * @manager: An #EthosManager
621
 
 * @plugin_info: An #EthosPluginInfo
622
 
 * @error: A location for a #GError
623
 
 *
624
 
 * Attempts to load a plugin using the #EthosPluginInfo.
625
 
 *
626
 
 * Return value: %TRUE if the plugin was loaded. @error is set in case of
627
 
 *   failure.
628
 
 */
629
 
gboolean
630
 
ethos_manager_load_plugin (EthosManager     *manager,
631
 
                           EthosPluginInfo  *plugin_info,
632
 
                           GError          **error)
633
 
{
634
 
        EthosManagerPrivate *priv;
635
 
        EthosPluginLoader   *plugin_loader;
636
 
        EthosPlugin         *plugin;
637
 
        EthosPluginInfo     *dep_info;
638
 
        const gchar         *loader;
639
 
        GList               *deps,
640
 
                            *iter,
641
 
                            *dep_names = NULL;
642
 
        Dependency          *dep;
643
 
        Version              ver, depver;
644
 
        GError              *local_error = NULL;
645
 
 
646
 
        g_return_val_if_fail (ETHOS_IS_MANAGER (manager), FALSE);
647
 
        g_return_val_if_fail (ETHOS_IS_PLUGIN_INFO (plugin_info), FALSE);
648
 
 
649
 
        if (ethos_plugin_info_get_active (plugin_info))
650
 
                return TRUE;
651
 
 
652
 
        priv = manager->priv;
653
 
 
654
 
        loader = ethos_plugin_info_get_loader (plugin_info);
655
 
        plugin_loader = ethos_manager_lookup_plugin_loader (manager, loader);
656
 
 
657
 
        if (!plugin_loader) {
658
 
                g_set_error (error, ETHOS_ERROR, ETHOS_ERROR_PLUGIN,
659
 
                             "The plugin loader \"%s\" could not be found",
660
 
                             loader);
661
 
                return FALSE;
662
 
        }
663
 
 
664
 
        deps = parse_deps (plugin_info);
665
 
 
666
 
        for (iter = deps; iter; iter = iter->next) {
667
 
                dep = iter->data;
668
 
                if (!parse_version (dep->version, &depver)) {
669
 
                        local_error = g_error_new (ETHOS_ERROR,
670
 
                                                   ETHOS_ERROR_PLUGIN,
671
 
                                                   "Invalid request for plugin version %s (%s",
672
 
                                                   dep->name, dep->version);
673
 
                        ethos_plugin_info_add_error (plugin_info, local_error);
674
 
                        if (error && *error == NULL)
675
 
                                *error = local_error;
676
 
                        else
677
 
                                g_error_free (local_error);
678
 
                        free_deps (deps);
679
 
                        return FALSE;
680
 
                }
681
 
                dep_info = ethos_manager_lookup_plugin_info (manager, dep->name);
682
 
                if (!dep_info) {
683
 
                        g_warning ("%s: Could not locate plugin dependency %s",
684
 
                                   ethos_plugin_info_get_id (plugin_info),
685
 
                                   dep->name);
686
 
                        local_error = g_error_new (ETHOS_ERROR,
687
 
                                                   ETHOS_ERROR_PLUGIN,
688
 
                                                   "Missing dependency %s",
689
 
                                                   dep->name);
690
 
                        ethos_plugin_info_add_error (plugin_info, local_error);
691
 
                        if (error && *error == NULL)
692
 
                                *error = local_error;
693
 
                        else
694
 
                                g_error_free (local_error);
695
 
                        free_deps (deps);
696
 
                        return FALSE;
697
 
                }
698
 
                if (!parse_version (ethos_plugin_info_get_version (dep_info), &ver)) {
699
 
                        local_error = g_error_new (ETHOS_ERROR,
700
 
                                                   ETHOS_ERROR_PLUGIN,
701
 
                                                   "Invalid dependency plugin version %s (%s)",
702
 
                                                   ethos_plugin_info_get_id (dep_info),
703
 
                                                   ethos_plugin_info_get_version (dep_info));
704
 
                        ethos_plugin_info_add_error (plugin_info, local_error);
705
 
                        if (error && *error == NULL)
706
 
                                *error = local_error;
707
 
                        else
708
 
                                g_error_free (local_error);
709
 
                        free_deps (deps);
710
 
                        return FALSE;
711
 
                }
712
 
 
713
 
                /* make sure that the dependency is met */
714
 
                switch (version_cmp (&ver, &depver)) {
715
 
                case -1:
716
 
                        if ((dep->oper & OP_LT) == 0)
717
 
                                goto unsatisfied;
718
 
                        break;
719
 
                case 0:
720
 
                        if ((dep->oper & OP_EQ) == 0)
721
 
                                goto unsatisfied;
722
 
                        break;
723
 
                case 1:
724
 
                        if ((dep->oper & OP_GT) == 0)
725
 
                                goto unsatisfied;
726
 
                        break;
727
 
                default:
728
 
                        g_assert_not_reached ();
729
 
                }
730
 
 
731
 
                if (!ethos_manager_load_plugin (manager, dep_info, &local_error))
732
 
                        goto unsatisfied;
733
 
 
734
 
                continue;
735
 
 
736
 
unsatisfied:
737
 
                if (local_error) {
738
 
                        ethos_plugin_info_add_error (dep_info, local_error);
739
 
                        ethos_plugin_info_add_error (plugin_info, local_error);
740
 
                        if (error && *error == NULL)
741
 
                                *error = local_error;
742
 
                        else
743
 
                                g_error_free (local_error);
744
 
                }
745
 
 
746
 
                free_deps (deps);
747
 
                return FALSE;
748
 
        }
749
 
 
750
 
        /* cache the dep names for this plugin */
751
 
        for (iter = deps; iter; iter = iter->next) {
752
 
                dep = iter->data;
753
 
                dep_names = g_list_prepend (dep_names, g_strdup (dep->name));
754
 
        }
755
 
        g_hash_table_insert (priv->deps_cache,
756
 
                             g_strdup (ethos_plugin_info_get_id (plugin_info)),
757
 
                             dep_names);
758
 
 
759
 
        free_deps (deps);
760
 
 
761
 
        if (!(plugin = ethos_plugin_loader_load (plugin_loader, plugin_info, error)))
762
 
                return FALSE;
763
 
 
764
 
        if (ETHOS_MANAGER_GET_CLASS (manager)->load_plugin (manager, plugin, error)) {
765
 
                g_hash_table_insert (priv->plugins,
766
 
                                     g_strdup (ethos_plugin_info_get_id (plugin_info)),
767
 
                                     g_object_ref (plugin));
768
 
                ethos_plugin_info_set_active (plugin_info, TRUE);
769
 
                g_signal_emit (manager, signals [PLUGIN_LOADED], 0, plugin_info);
770
 
                g_object_unref (plugin);
771
 
                return TRUE;
772
 
        }
773
 
 
774
 
        g_object_unref (plugin);
775
 
 
776
 
        return FALSE;
777
 
}
778
 
 
779
 
/**
780
 
 * ethos_manager_unload_plugin:
781
 
 * @manager: An #EthosManager
782
 
 * @plugin_info: An #EthosPluginInfo
783
 
 * @error: A location for a #GError or %NULL
784
 
 *
785
 
 * Attempts to unload the plugin that was created using @plugin_info.
786
 
 *
787
 
 * Return value: %TRUE if the plugin was unloaded. @error is set in case of
788
 
 *   failure.
789
 
 */
790
 
gboolean
791
 
ethos_manager_unload_plugin (EthosManager     *manager,
792
 
                             EthosPluginInfo  *plugin_info,
793
 
                             GError          **error)
794
 
{
795
 
        EthosManagerPrivate *priv;
796
 
        EthosPlugin         *plugin;
797
 
        const gchar         *plugin_id;
798
 
        GList               *list;
799
 
        GHashTableIter       iter;
800
 
        gpointer             key, value;
801
 
 
802
 
        g_return_val_if_fail (ETHOS_IS_MANAGER (manager), FALSE);
803
 
        g_return_val_if_fail (ETHOS_IS_PLUGIN_INFO (plugin_info), FALSE);
804
 
 
805
 
        if (!ethos_plugin_info_get_active (plugin_info))
806
 
                return TRUE;
807
 
 
808
 
        priv = manager->priv;
809
 
        plugin_id = ethos_plugin_info_get_id (plugin_info);
810
 
 
811
 
        if (!(plugin = g_hash_table_lookup (priv->plugins, plugin_id)))
812
 
                return TRUE;
813
 
 
814
 
        /* if any other plugin is depending on this plugin, we cannot
815
 
         * unload it right now. deps_cache is a hashtable containing
816
 
         * a linked list of the names of dependencies */
817
 
        g_hash_table_iter_init (&iter, priv->deps_cache);
818
 
        while (g_hash_table_iter_next (&iter, &key, &value)) {
819
 
                for (list = value; list; list = list->next) {
820
 
                        if (g_strcmp0 (plugin_id, list->data) == 0) {
821
 
                                g_set_error (error,
822
 
                                             ETHOS_ERROR,
823
 
                                             ETHOS_ERROR_PLUGIN,
824
 
                                             "Cannot unload the plugin"
825
 
                                             " because the %s plugin is "
826
 
                                             "depending on it.",
827
 
                                             (gchar*)key);
828
 
                                return FALSE;
829
 
                        }
830
 
                }
831
 
        }
832
 
 
833
 
        if (ETHOS_MANAGER_GET_CLASS (manager)->unload_plugin (manager, plugin, error)) {
834
 
                ethos_plugin_info_set_active (plugin_info, FALSE);
835
 
                g_hash_table_remove (priv->deps_cache, plugin_id);
836
 
                g_hash_table_remove (priv->plugins, plugin_id);
837
 
                g_signal_emit (manager, signals [PLUGIN_UNLOADED], 0, plugin_info);
838
 
                return TRUE;
839
 
        }
840
 
 
841
 
        return FALSE;
842
 
}
843
 
 
844
 
/**
845
 
 * ethos_manager_get_plugin_loaders:
846
 
 * @manager: An #EthosManager
847
 
 *
848
 
 * Retreives a list of #EthosPluginLoader<!-- -->s that were discovered
849
 
 * when the application was initialized.
850
 
 *
851
 
 * Return value: A #GList of #EthosPluginLoader<!-- -->s.  The list should
852
 
 *   be freed using g_list_free().
853
 
 */
854
 
GList*
855
 
ethos_manager_get_plugin_loaders (EthosManager *manager)
856
 
{
857
 
        EthosManagerPrivate *priv;
858
 
 
859
 
        g_return_val_if_fail (ETHOS_IS_MANAGER (manager), NULL);
860
 
 
861
 
        priv = manager->priv;
862
 
 
863
 
        return g_list_copy (priv->plugin_loaders);
864
 
}
865
 
 
866
 
/**
867
 
 * ethos_manager_get_plugin_info:
868
 
 * @manager: An #EthosManager
869
 
 *
870
 
 * Retreives the list of #EthosPluginInfo that were discovered.
871
 
 *
872
 
 * Return value: A #GList of #EthosPluginInfo which should be freed using
873
 
 *   g_list_free().
874
 
 */
875
 
GList*
876
 
ethos_manager_get_plugin_info (EthosManager *manager)
877
 
{
878
 
        g_return_val_if_fail (ETHOS_IS_MANAGER (manager), NULL);
879
 
        return g_list_copy (manager->priv->plugin_info);
880
 
}
881
 
 
882
 
/**
883
 
 * ethos_manager_get_plugin_dirs:
884
 
 * @manager: An #EthosManager
885
 
 *
886
 
 * Retrieves the list of plugin directories that are traversed when looking to load plugins.
887
 
 *
888
 
 * Return value: a %NULL terminated list of strings containing directories that will be
889
 
 *   traversed to locate plugins.  This reslut should never be modified or freed.
890
 
 */
891
 
G_CONST_RETURN gchar**
892
 
ethos_manager_get_plugin_dirs (EthosManager *manager)
893
 
{
894
 
        g_return_val_if_fail (ETHOS_IS_MANAGER (manager), NULL);
895
 
        return (G_CONST_RETURN gchar**)manager->priv->plugin_dirs;
896
 
}
897
 
 
898
 
/**
899
 
 * ethos_manager_set_plugin_dirs:
900
 
 * @manager: An #EthosManager
901
 
 * @plugin_dirs: A %NULL terminated list of strings of directories
902
 
 *
903
 
 * Sets the list of directories to use for locating plugins.
904
 
 */
905
 
void
906
 
ethos_manager_set_plugin_dirs (EthosManager  *manager,
907
 
                               gchar        **plugin_dirs)
908
 
{
909
 
        EthosManagerPrivate *priv;
910
 
 
911
 
        g_return_if_fail (ETHOS_IS_MANAGER (manager));
912
 
 
913
 
        priv = manager->priv;
914
 
 
915
 
        g_strfreev (priv->plugin_dirs);
916
 
        priv->plugin_dirs = g_strdupv (plugin_dirs);
917
 
}
918
 
 
919
 
/**
920
 
 * ethos_manager_get_app_name:
921
 
 * @manager: An #EthosManager
922
 
 *
923
 
 * Retrieves the app-name that the #EthosManager should use.
924
 
 *
925
 
 * Return value: the configured app name used for plugins or %NULL
926
 
 */
927
 
G_CONST_RETURN gchar*
928
 
ethos_manager_get_app_name (EthosManager *manager)
929
 
{
930
 
        g_return_val_if_fail (ETHOS_IS_MANAGER (manager), NULL);
931
 
        return manager->priv->app_name;
932
 
}
933
 
 
934
 
/**
935
 
 * ethos_manager_set_app_name:
936
 
 * @manager: An #EthosManager
937
 
 * @app_name: A single-worded name for the application
938
 
 *
939
 
 * Sets the app name used for the application.  This should be a single word
940
 
 * to simplify the plugin development process.  The app name is used inside
941
 
 * the plugin description files for the group prefix as well as in the name
942
 
 * of the file.
943
 
 *
944
 
 * For example, an application named "Ethos" would have the group
945
 
 * [Ethos Plugin] in the plugin description file which would be named
946
 
 * myplugin.ethos-plugin.
947
 
 */
948
 
void
949
 
ethos_manager_set_app_name (EthosManager *manager,
950
 
                            const gchar  *app_name)
951
 
{
952
 
        EthosManagerPrivate *priv;
953
 
 
954
 
        g_return_if_fail (ETHOS_IS_MANAGER (manager));
955
 
        g_return_if_fail (app_name != NULL);
956
 
 
957
 
        priv = manager->priv;
958
 
 
959
 
        g_free (priv->app_name);
960
 
        priv->app_name = g_strdup (app_name);
961
 
}
962
 
 
963
 
/**
964
 
 * ethos_manager_get_plugin:
965
 
 * @manager: An #EthosManager
966
 
 * @plugin_info: An #EthosPluginInfo
967
 
 *
968
 
 * Retrieves the created instance of the plugin defined by @plugin_info.  If
969
 
 * no instance of the plugin has been created, then %NULL is returned.
970
 
 *
971
 
 * Return value: The #EthosPlugin instance or %NULL
972
 
 */
973
 
EthosPlugin*
974
 
ethos_manager_get_plugin (EthosManager    *manager,
975
 
                          EthosPluginInfo *plugin_info)
976
 
{
977
 
        EthosManagerPrivate *priv;
978
 
 
979
 
        g_return_val_if_fail (ETHOS_IS_MANAGER (manager), NULL);
980
 
        g_return_val_if_fail (ETHOS_IS_PLUGIN_INFO (plugin_info), NULL);
981
 
 
982
 
        priv = manager->priv;
983
 
 
984
 
        return g_hash_table_lookup (priv->plugins,
985
 
                                    ethos_plugin_info_get_id (plugin_info));
986
 
}
987
 
 
988
 
static gboolean
989
 
ethos_manager_real_load_plugin (EthosManager  *manager,
990
 
                                EthosPlugin   *plugin,
991
 
                                GError       **error)
992
 
{
993
 
        ethos_plugin_activate (plugin);
994
 
        return TRUE;
995
 
}
996
 
 
997
 
static gboolean
998
 
ethos_manager_real_unload_plugin (EthosManager  *manager,
999
 
                                  EthosPlugin   *plugin,
1000
 
                                  GError       **error)
1001
 
{
1002
 
        ethos_plugin_deactivate (plugin);
1003
 
        return TRUE;
1004
 
}
1005
 
 
1006
 
static void
1007
 
ethos_manager_real_initialized (EthosManager *manager)
1008
 
{
1009
 
        GList  *list,
1010
 
               *iter;
1011
 
        GError *error = NULL;
1012
 
 
1013
 
        g_return_if_fail (ETHOS_IS_MANAGER (manager));
1014
 
 
1015
 
        list = ethos_manager_get_plugin_info (manager);
1016
 
        for (iter = list; iter; iter = iter->next) {
1017
 
                if (ethos_plugin_info_get_active (iter->data)) {
1018
 
                        continue;
1019
 
                }
1020
 
                else if (!ethos_manager_load_plugin (manager, iter->data, &error)) {
1021
 
                        g_warning ("%s: %s",
1022
 
                                   ethos_plugin_info_get_id (iter->data),
1023
 
                                   error ? error->message : "Error loading");
1024
 
 
1025
 
                        if (error) {
1026
 
                                ethos_plugin_info_add_error (iter->data, error);
1027
 
                                g_error_free (error);
1028
 
                                error = NULL;
1029
 
                        }
1030
 
                }
1031
 
        }
1032
 
 
1033
 
        g_list_free (list);
1034
 
}
1035
 
 
1036
 
static void
1037
 
ethos_manager_finalize (GObject *object)
1038
 
{
1039
 
        EthosManagerPrivate *priv;
1040
 
 
1041
 
        priv = ETHOS_MANAGER (object)->priv;
1042
 
 
1043
 
        g_free (priv->app_name);
1044
 
        g_strfreev (priv->plugin_dirs);
1045
 
        g_hash_table_destroy (priv->plugin_info_hash);
1046
 
 
1047
 
        g_list_foreach (priv->plugin_loaders, (GFunc)g_object_unref, NULL);
1048
 
        g_list_free (priv->plugin_loaders);
1049
 
 
1050
 
        g_list_foreach (priv->plugin_info, (GFunc)g_object_unref, NULL);
1051
 
        g_list_free (priv->plugin_info);
1052
 
 
1053
 
        G_OBJECT_CLASS (ethos_manager_parent_class)->finalize (object);
1054
 
}
1055
 
 
1056
 
static void
1057
 
ethos_manager_real_plugin_loaded (EthosManager    *manager,
1058
 
                                  EthosPluginInfo *plugin_info)
1059
 
{
1060
 
}
1061
 
 
1062
 
static void
1063
 
ethos_manager_real_plugin_unloaded (EthosManager    *manager,
1064
 
                                    EthosPluginInfo *plugin_info)
1065
 
{
1066
 
}
1067
 
 
1068
 
static void
1069
 
ethos_manager_class_init (EthosManagerClass *klass)
1070
 
{
1071
 
        GObjectClass *object_class;
1072
 
 
1073
 
        object_class = G_OBJECT_CLASS (klass);
1074
 
        object_class->finalize = ethos_manager_finalize;
1075
 
        g_type_class_add_private (object_class, sizeof(EthosManagerPrivate));
1076
 
 
1077
 
        klass->load_plugin = ethos_manager_real_load_plugin;
1078
 
        klass->unload_plugin = ethos_manager_real_unload_plugin;
1079
 
        klass->initialized = ethos_manager_real_initialized;
1080
 
        klass->plugin_loaded = ethos_manager_real_plugin_loaded;
1081
 
        klass->plugin_unloaded = ethos_manager_real_plugin_unloaded;
1082
 
 
1083
 
        /**
1084
 
         * EthosManager::initialized:
1085
 
         * @manager: An #EthosManager
1086
 
         *
1087
 
         * The initialized signal is emmitted after the manager has completed
1088
 
         * initializing.  This is a good place to load the plugins you want
1089
 
         * loaded on startup.
1090
 
         */
1091
 
        signals [INITIALIZED] =
1092
 
                g_signal_new (g_intern_static_string ("initialized"),
1093
 
                              G_TYPE_FROM_CLASS (klass),
1094
 
                              G_SIGNAL_RUN_FIRST,
1095
 
                              G_STRUCT_OFFSET (EthosManagerClass, initialized),
1096
 
                              NULL, NULL,
1097
 
                              g_cclosure_marshal_VOID__VOID,
1098
 
                              G_TYPE_NONE,
1099
 
                              0);
1100
 
 
1101
 
        /**
1102
 
         * EthosManager::plugin-loaded:
1103
 
         * @manager: An #EthosManager
1104
 
         * @plugin_info: An #EthosPluginInfo
1105
 
         *
1106
 
         * The plugin-loaded signal is emitted when a plugin has been
1107
 
         * successfully loaded.
1108
 
         */
1109
 
        signals [PLUGIN_LOADED] =
1110
 
                g_signal_new (g_intern_static_string ("plugin-loaded"),
1111
 
                              G_TYPE_FROM_CLASS (klass),
1112
 
                              G_SIGNAL_RUN_FIRST,
1113
 
                              G_STRUCT_OFFSET (EthosManagerClass, plugin_loaded),
1114
 
                              NULL, NULL,
1115
 
                              g_cclosure_marshal_VOID__OBJECT,
1116
 
                              G_TYPE_NONE,
1117
 
                              1, ETHOS_TYPE_PLUGIN_INFO);
1118
 
 
1119
 
        /**
1120
 
         * EthosManager::plugin-unloaded:
1121
 
         * @manager: An #EthosManager
1122
 
         * @plugin_info: An #EthosPluginInfo
1123
 
         *
1124
 
         * The plugin-loaded signal is emitted when a plugin has been
1125
 
         * successfully unloaded.
1126
 
         */
1127
 
        signals [PLUGIN_UNLOADED] =
1128
 
                g_signal_new (g_intern_static_string ("plugin-unloaded"),
1129
 
                              G_TYPE_FROM_CLASS (klass),
1130
 
                              G_SIGNAL_RUN_FIRST,
1131
 
                              G_STRUCT_OFFSET (EthosManagerClass, plugin_unloaded),
1132
 
                              NULL, NULL,
1133
 
                              g_cclosure_marshal_VOID__OBJECT,
1134
 
                              G_TYPE_NONE,
1135
 
                              1, ETHOS_TYPE_PLUGIN_INFO);
1136
 
}
1137
 
 
1138
 
static void
1139
 
free_dep_list (gpointer data)
1140
 
{
1141
 
        GList *list = data,
1142
 
              *iter;
1143
 
        for (iter = list; iter; iter = iter->next)
1144
 
                g_free (iter->data);
1145
 
        g_list_free (list);
1146
 
}
1147
 
 
1148
 
static void
1149
 
ethos_manager_init (EthosManager *manager)
1150
 
{
1151
 
        manager->priv = G_TYPE_INSTANCE_GET_PRIVATE (manager,
1152
 
                                                     ETHOS_TYPE_MANAGER,
1153
 
                                                     EthosManagerPrivate);
1154
 
 
1155
 
        /* set the default app name, which is gotten from the executable
1156
 
         * but with an uppercase first letter.
1157
 
         */
1158
 
        manager->priv->app_name = g_strdup (g_get_prgname ());
1159
 
        if (manager->priv->app_name) {
1160
 
                if (!g_ascii_isupper (manager->priv->app_name [0])) {
1161
 
                        manager->priv->app_name [0] =
1162
 
                                g_ascii_toupper (manager->priv->app_name [0]);
1163
 
                }
1164
 
        }
1165
 
        else
1166
 
                manager->priv->app_name = g_strdup ("Ethos");
1167
 
 
1168
 
        manager->priv->plugin_info_hash =
1169
 
                g_hash_table_new_full (g_str_hash,
1170
 
                                       g_str_equal,
1171
 
                                       g_free,
1172
 
                                       g_object_unref);
1173
 
        manager->priv->plugins =
1174
 
                g_hash_table_new_full (g_str_hash,
1175
 
                                       g_str_equal,
1176
 
                                       g_free,
1177
 
                                       g_object_unref);
1178
 
        manager->priv->deps_cache =
1179
 
                g_hash_table_new_full (g_str_hash,
1180
 
                                       g_str_equal,
1181
 
                                       g_free,
1182
 
                                       free_dep_list);
1183
 
}