1 /*
2 * e-module.c
3 *
4 * This library is free software: you can redistribute it and/or modify it
5 * under the terms of the GNU Lesser General Public License as published by
6 * the Free Software Foundation.
7 *
8 * This library is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
10 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
11 * for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this library. If not, see <http://www.gnu.org/licenses/>.
15 *
16 */
17
18 /**
19 * SECTION: e-module
20 * @include: libedataserver/libedataserver.h
21 * @short_description: A module loader
22 **/
23
24 #include "evolution-data-server-config.h"
25
26 #include <glib.h>
27
28 #include "e-data-server-util.h"
29 #include "e-module.h"
30
31 /* This is the symbol we call when loading a module. */
32 #define LOAD_SYMBOL "e_module_load"
33
34 /* This is the symbol we call when unloading a module. */
35 #define UNLOAD_SYMBOL "e_module_unload"
36
37 struct _EModulePrivate {
38 GModule *module;
39 gchar *filename;
40
41 void (*load) (GTypeModule *type_module);
42 void (*unload) (GTypeModule *type_module);
43 };
44
45 enum {
46 PROP_0,
47 PROP_FILENAME
48 };
49
G_DEFINE_TYPE_WITH_PRIVATE(EModule,e_module,G_TYPE_TYPE_MODULE)50 G_DEFINE_TYPE_WITH_PRIVATE (
51 EModule,
52 e_module,
53 G_TYPE_TYPE_MODULE)
54
55 static void
56 module_set_filename (EModule *module,
57 const gchar *filename)
58 {
59 g_return_if_fail (module->priv->filename == NULL);
60
61 module->priv->filename = g_strdup (filename);
62 }
63
64 static void
module_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)65 module_set_property (GObject *object,
66 guint property_id,
67 const GValue *value,
68 GParamSpec *pspec)
69 {
70 switch (property_id) {
71 case PROP_FILENAME:
72 module_set_filename (
73 E_MODULE (object),
74 g_value_get_string (value));
75 return;
76 }
77
78 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
79 }
80
81 static void
module_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)82 module_get_property (GObject *object,
83 guint property_id,
84 GValue *value,
85 GParamSpec *pspec)
86 {
87 switch (property_id) {
88 case PROP_FILENAME:
89 g_value_set_string (
90 value, e_module_get_filename (
91 E_MODULE (object)));
92 return;
93 }
94
95 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
96 }
97
98 static void
module_finalize(GObject * object)99 module_finalize (GObject *object)
100 {
101 EModulePrivate *priv;
102
103 priv = E_MODULE (object)->priv;
104
105 g_free (priv->filename);
106
107 /* Chain up to parent's finalize() method. */
108 G_OBJECT_CLASS (e_module_parent_class)->finalize (object);
109 }
110
111 static gboolean
module_load(GTypeModule * type_module)112 module_load (GTypeModule *type_module)
113 {
114 EModulePrivate *priv;
115 gpointer symbol;
116
117 priv = E_MODULE (type_module)->priv;
118
119 g_return_val_if_fail (priv->filename != NULL, FALSE);
120 priv->module = g_module_open (priv->filename, 0);
121
122 if (priv->module == NULL)
123 goto fail;
124
125 if (!g_module_symbol (priv->module, LOAD_SYMBOL, &symbol))
126 goto fail;
127
128 priv->load = symbol;
129
130 if (!g_module_symbol (priv->module, UNLOAD_SYMBOL, &symbol))
131 goto fail;
132
133 priv->unload = symbol;
134
135 priv->load (type_module);
136
137 /* XXX This is a Band-Aid for a design flaw in EExtension. If the
138 * "extensible_type" member of EExtensionClass is set to a GType
139 * that hasn't already been registered, then when the extension's
140 * module is unloaded the GType registration that was triggered
141 * by setting "extensible_type" will be invalidated and cause
142 * Evolution to malfunction when the module is loaded again.
143 *
144 * Extension modules get loaded and unloaded repeatedly by
145 * e_extensible_load_extensions(), which temporarily references
146 * all extension classes and picks out the ones it needs for a
147 * given EExtensible instance based on the "extensible_type"
148 * class member.
149 *
150 * Making the module resident prevents the aforementioned GType
151 * registration from being invalidated when the extension class
152 * is unreferenced.
153 */
154 g_module_make_resident (priv->module);
155
156 return TRUE;
157
158 fail:
159 g_warning ("%s: %s", G_STRFUNC, g_module_error ());
160
161 if (priv->module != NULL)
162 g_module_close (priv->module);
163
164 return FALSE;
165 }
166
167 static void
module_unload(GTypeModule * type_module)168 module_unload (GTypeModule *type_module)
169 {
170 EModulePrivate *priv;
171
172 priv = E_MODULE (type_module)->priv;
173
174 priv->unload (type_module);
175
176 g_module_close (priv->module);
177 priv->module = NULL;
178
179 priv->load = NULL;
180 priv->unload = NULL;
181 }
182
183 static void
e_module_class_init(EModuleClass * class)184 e_module_class_init (EModuleClass *class)
185 {
186 GObjectClass *object_class;
187 GTypeModuleClass *type_module_class;
188
189 object_class = G_OBJECT_CLASS (class);
190 object_class->set_property = module_set_property;
191 object_class->get_property = module_get_property;
192 object_class->finalize = module_finalize;
193
194 type_module_class = G_TYPE_MODULE_CLASS (class);
195 type_module_class->load = module_load;
196 type_module_class->unload = module_unload;
197
198 /**
199 * EModule:filename
200 *
201 * The filename of the module.
202 **/
203 g_object_class_install_property (
204 object_class,
205 PROP_FILENAME,
206 g_param_spec_string (
207 "filename",
208 "Filename",
209 "The filename of the module",
210 NULL,
211 G_PARAM_READWRITE |
212 G_PARAM_CONSTRUCT_ONLY |
213 G_PARAM_STATIC_STRINGS));
214 }
215
216 static void
e_module_init(EModule * module)217 e_module_init (EModule *module)
218 {
219 module->priv = e_module_get_instance_private (module);
220 }
221
222 /**
223 * e_module_new:
224 * @filename: filename of the shared library module
225 *
226 * Creates a new #EModule that will load the specific shared library
227 * when in use.
228 *
229 * Returns: a new #EModule for @filename
230 *
231 * Since: 3.4
232 **/
233 EModule *
e_module_new(const gchar * filename)234 e_module_new (const gchar *filename)
235 {
236 g_return_val_if_fail (filename != NULL, NULL);
237
238 return g_object_new (E_TYPE_MODULE, "filename", filename, NULL);
239 }
240
241 /**
242 * e_module_get_filename:
243 * @module: an #EModule
244 *
245 * Returns the filename of the shared library for @module. The
246 * string is owned by @module and should not be modified or freed.
247 *
248 * Returns: (transfer none): the filename for @module
249 *
250 * Since: 3.4
251 **/
252 const gchar *
e_module_get_filename(EModule * module)253 e_module_get_filename (EModule *module)
254 {
255 g_return_val_if_fail (E_IS_MODULE (module), NULL);
256
257 return module->priv->filename;
258 }
259
260 /**
261 * e_module_load_all_in_directory:
262 * @dirname: pathname for a directory containing modules to load
263 *
264 * Loads all the modules in the specified directory into memory. If
265 * you want to unload them (enabling on-demand loading) you must call
266 * g_type_module_unuse() on all the modules. Free the returned list
267 * with g_list_free().
268 *
269 * Returns: (element-type EModule) (transfer container): a list of #EModules loaded from @dirname
270 *
271 * Since: 3.4
272 **/
273 GList *
e_module_load_all_in_directory(const gchar * dirname)274 e_module_load_all_in_directory (const gchar *dirname)
275 {
276 GDir *dir;
277 const gchar *basename;
278 GList *loaded_modules = NULL;
279 GError *error = NULL;
280
281 g_return_val_if_fail (dirname != NULL, NULL);
282
283 if (!g_module_supported ())
284 return NULL;
285
286 dir = g_dir_open (dirname, 0, &error);
287 if (dir == NULL) {
288 g_debug ("%s: %s", G_STRFUNC, error ? error->message : "Unknown error");
289 g_clear_error (&error);
290 return NULL;
291 }
292
293 while ((basename = g_dir_read_name (dir)) != NULL) {
294 EModule *module;
295 gchar *filename;
296
297 if (!g_str_has_suffix (basename, "." G_MODULE_SUFFIX))
298 continue;
299
300 filename = g_build_filename (dirname, basename, NULL);
301
302 module = e_module_load_file (filename);
303
304 g_free (filename);
305
306 if (module != NULL)
307 loaded_modules = g_list_prepend (loaded_modules, module);
308 }
309
310 g_dir_close (dir);
311
312 return loaded_modules;
313 }
314
315 /**
316 * e_module_load_file:
317 * @filename: filename of the module to load
318 *
319 * Load the module from the specified filename into memory. If
320 * you want to unload it (enabling on-demand loading) you must call
321 * g_type_module_unuse() on the module.
322 *
323 * Returns: (transfer full): an #EModule loaded from @filename
324 *
325 * Since: 3.16
326 **/
327 EModule *
e_module_load_file(const gchar * filename)328 e_module_load_file (const gchar *filename)
329 {
330 EModule *module;
331
332 module = e_module_new (filename);
333
334 if (!g_type_module_use (G_TYPE_MODULE (module))) {
335 g_printerr ("Failed to load module: %s\n", filename);
336 g_clear_object (&module);
337 }
338
339 return module;
340 }
341
342 /**
343 * e_module_load_all_in_directory_and_prefixes:
344 * @dirname: pathname for a directory containing modules to load
345 * @dirprefix: (nullable): prefix of @dirname, which can be replaced by custom prefixes, or %NULL
346 *
347 * Loads all the modules in the specified directory into memory and the other
348 * custom prefixes returned by e_util_get_directory_variants(). If
349 * you want to unload them (enabling on-demand loading) you must call
350 * g_type_module_unuse() on all the modules. Free the returned list
351 * with g_list_free().
352 *
353 * When @dirprefix is %NULL, or not a prefix of @dirname, behaves
354 * the same as e_module_load_all_in_directory().
355 *
356 * Returns: (element-type EModule) (transfer container): a list of #EModules loaded
357 * from @dirname and any extra prefix directory.
358 *
359 * Since: 3.40
360 **/
361 GList *
e_module_load_all_in_directory_and_prefixes(const gchar * dirname,const gchar * dirprefix)362 e_module_load_all_in_directory_and_prefixes (const gchar *dirname,
363 const gchar *dirprefix)
364 {
365 GList *list = NULL;
366 GPtrArray *variants;
367 guint ii;
368
369 g_return_val_if_fail (dirname != NULL, NULL);
370
371 if (!g_module_supported ())
372 return NULL;
373
374 if (!dirprefix || !*dirprefix || !g_str_has_prefix (dirname, dirprefix))
375 return e_module_load_all_in_directory (dirname);
376
377 variants = e_util_get_directory_variants (dirname, dirprefix, TRUE);
378 if (!variants)
379 return e_module_load_all_in_directory (dirname);
380
381 for (ii = 0; ii < variants->len; ii++) {
382 const gchar *path = g_ptr_array_index (variants, ii);
383
384 if (path && *path) {
385 GList *modules;
386
387 modules = e_module_load_all_in_directory (path);
388
389 if (modules)
390 list = g_list_concat (list, modules);
391 }
392 }
393
394 g_ptr_array_unref (variants);
395
396 return list;
397 }
398