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