1 /*
2  * Nautilus-Actions
3  * A Nautilus extension which offers configurable context menu actions.
4  *
5  * Copyright (C) 2005 The GNOME Foundation
6  * Copyright (C) 2006-2008 Frederic Ruaudel and others (see AUTHORS)
7  * Copyright (C) 2009-2014 Pierre Wieser and others (see AUTHORS)
8  *
9  * Nautilus-Actions is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU General Public License as
11  * published by the Free Software Foundation; either version 2 of
12  * the License, or (at your option) any later version.
13  *
14  * Nautilus-Actions is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17  * General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with Nautilus-Actions; see the file COPYING. If not, see
21  * <http://www.gnu.org/licenses/>.
22  *
23  * Authors:
24  *   Frederic Ruaudel <grumz@grumz.net>
25  *   Rodrigo Moya <rodrigo@gnome-db.org>
26  *   Pierre Wieser <pwieser@trychlos.org>
27  *   ... and many others (see AUTHORS)
28  */
29 
30 #ifdef HAVE_CONFIG_H
31 #include <config.h>
32 #endif
33 
34 /* this is a module to test how to use the GLib dynamic-loading functions
35  * it tries to load, then unload, a test-module-plugin.so plugin
36  *
37  * more specifically, I am interested on releasing allocated resources
38  */
39 
40 #include <stdlib.h>
41 
42 #include <glib-object.h>
43 #include <gmodule.h>
44 
45 #define PLUGIN_NAME								"test_module_plugin"
46 
47 
48 #if 0
49 /* this is first version
50  */
51 static GModule *load_plugin( void );
52 static void     call_plugin_fn( GModule *module );
53 static void     unload_plugin( GModule *module );
54 
55 int
56 main( int argc, char **argv )
57 {
58 	GModule *module;
59 
60 #if !GLIB_CHECK_VERSION( 2,36, 0 )
61 	g_type_init();
62 #endif
63 
64 	/* dynamically load the module */
65 	module = load_plugin();
66 
67 	if( module ){
68 		/* call a function in the module */
69 		call_plugin_fn( module );
70 
71 		/* unload the module */
72 		unload_plugin( module );
73 	}
74 
75 	return( 0 );
76 }
77 
78 static GModule *
79 load_plugin( void )
80 {
81 	gchar *module_path;
82 	GModule *module;
83 
84 	module = NULL;
85 
86 	if( g_module_supported()){
87 
88 		module_path = g_module_build_path( PKGLIBDIR, PLUGIN_NAME );
89 		module = g_module_open( module_path, G_MODULE_BIND_LAZY | G_MODULE_BIND_LOCAL );
90 		if( !module ){
91 			g_printerr( "%s: %s\n", module_path, g_module_error());
92 		}
93 		g_free( module_path );
94 	}
95 
96 	return( module );
97 }
98 
99 static void
100 call_plugin_fn( GModule *module )
101 {
102 	typedef void ( *PluginFn )( GModule *module );
103 	PluginFn plugin_fn;
104 
105 	if( !g_module_symbol( module, "say_hello", ( gpointer * ) &plugin_fn )){
106 		g_printerr( "%s\n", g_module_error());
107 		return;
108 	}
109 	if( !plugin_fn ){
110 		g_printerr( "%s\n", g_module_error());
111 		return;
112 	}
113 	plugin_fn( module );
114 }
115 
116 static void
117 unload_plugin( GModule *module )
118 {
119 	if( !g_module_close( module )){
120 		g_printerr( "%s\n", g_module_error());
121 	}
122 }
123 #endif
124 
125 /* version 2
126  * Having the plugin register dynamic GTypes require a GTypeModule
127  * This GTtypeModule is provided by the program as a GTypeModule-derived object
128  * NAModule embeds the GModule
129  *
130  * Result:
131  *
132  * - the main program (the loader) should define a GTypeModule derived class
133  *
134  * - the GTypeModule derived class (here NAModule) embeds a GModule pointer
135  *
136  * - when loading plugins:
137  *
138  *   > allocate a new GTypeModule derived object for each module
139  *     setup the path here
140  *
141  *   > g_type_module_use() it
142  *     this triggers the on_load() virtual method on GTypeModule derived class
143  *       in on_load_derived(), g_module_open() the plugin and check the API
144  *
145  *       on_load_derived() may return FALSE
146  *       while dynamic types have not been registered, we are always safe to unref
147  *       the allocated GTypeModule derived object
148  *       setup the GModule pointer on the loaded library
149  *
150  *     so, if g_module_use() returns FALSE, just unref the object
151  *
152  *     so, g_module_use() cannot be called from instance_init or
153  *     instance_constructed (which have no return value)
154  *
155  * At the end, it is impossible to release the GTypeModule objects.
156  * But we can safely unuse their loaded libraries.
157  *
158  * The main program does not known which GType or GInterface the plugin
159  * declares.
160  * Nautilus defines a get_types() API, and then allocates an object for each
161  * returned type. It is then easy to check if the object implements a given
162  * interface.
163  * Nautilus never release these objects.
164  * We may also ask the plugin to just allocate itself its own management object,
165  * returning it to the program (returning a pointer is possible because we are
166  * in the same process).
167  */
168 
169 #define NA_TYPE_MODULE                  ( na_module_get_type())
170 #define NA_MODULE( object )             ( G_TYPE_CHECK_INSTANCE_CAST( object, NA_TYPE_MODULE, NAModule ))
171 #define NA_MODULE_CLASS( klass )        ( G_TYPE_CHECK_CLASS_CAST( klass, NA_TYPE_MODULE, NAModuleClass ))
172 #define NA_IS_MODULE( object )          ( G_TYPE_CHECK_INSTANCE_TYPE( object, NA_TYPE_MODULE ))
173 #define NA_IS_MODULE_CLASS( klass )     ( G_TYPE_CHECK_CLASS_TYPE(( klass ), NA_TYPE_MODULE ))
174 #define NA_MODULE_GET_CLASS( object )   ( G_TYPE_INSTANCE_GET_CLASS(( object ), NA_TYPE_MODULE, NAModuleClass ))
175 
176 typedef struct _NAModulePrivate         NAModulePrivate;
177 typedef struct _NAModuleClassPrivate    NAModuleClassPrivate;
178 
179 typedef struct {
180 	/*< private >*/
181 	GTypeModule      parent;
182 	NAModulePrivate *private;
183 }
184 	NAModule;
185 
186 typedef struct {
187 	/*< private >*/
188 	GTypeModuleClass      parent;
189 	NAModuleClassPrivate *private;
190 }
191 	NAModuleClass;
192 
193 GType    na_module_get_type               ( void );
194 
195 /* private class data
196  */
197 struct _NAModuleClassPrivate {
198 	void *empty;						/* so that gcc -pedantic is happy */
199 };
200 
201 /* private instance data
202  */
203 struct _NAModulePrivate {
204 	gboolean  dispose_has_run;
205 	GModule  *plugin;
206 };
207 
208 static GTypeModuleClass *st_parent_class = NULL;
209 
210 static GType     register_type( void );
211 static void      class_init( NAModuleClass *klass );
212 static void      instance_init( GTypeInstance *instance, gpointer klass );
213 static void      instance_dispose( GObject *object );
214 static void      instance_finalize( GObject *object );
215 
216 static NAModule *load_plugin( void );
217 static gboolean  on_module_load( GTypeModule *module );
218 static void      call_plugin_fn( NAModule *module );
219 static void      on_unload_plugin( GTypeModule *module );
220 
221 GType
na_module_get_type(void)222 na_module_get_type( void )
223 {
224 	static GType object_type = 0;
225 
226 	if( !object_type ){
227 		object_type = register_type();
228 	}
229 
230 	return( object_type );
231 }
232 
233 static GType
register_type(void)234 register_type( void )
235 {
236 	static const gchar *thisfn = "na_module_register_type";
237 	GType type;
238 
239 	static GTypeInfo info = {
240 		sizeof( NAModuleClass ),
241 		( GBaseInitFunc ) NULL,
242 		( GBaseFinalizeFunc ) NULL,
243 		( GClassInitFunc ) class_init,
244 		NULL,
245 		NULL,
246 		sizeof( NAModule ),
247 		0,
248 		( GInstanceInitFunc ) instance_init
249 	};
250 
251 	g_debug( "%s", thisfn );
252 
253 	type = g_type_register_static( G_TYPE_TYPE_MODULE, "NAModule", &info, 0 );
254 
255 	return( type );
256 }
257 
258 static void
class_init(NAModuleClass * klass)259 class_init( NAModuleClass *klass )
260 {
261 	static const gchar *thisfn = "na_module_class_init";
262 	GObjectClass *object_class;
263 	GTypeModuleClass *module_class;
264 
265 	g_debug( "%s: klass=%p", thisfn, ( void * ) klass );
266 
267 	st_parent_class = g_type_class_peek_parent( klass );
268 
269 	object_class = G_OBJECT_CLASS( klass );
270 	object_class->dispose = instance_dispose;
271 	object_class->finalize = instance_finalize;
272 
273 	module_class = G_TYPE_MODULE_CLASS( klass );
274 	module_class->load = on_module_load;
275 	module_class->unload = on_unload_plugin;
276 
277 	klass->private = g_new0( NAModuleClassPrivate, 1 );
278 }
279 
280 static void
instance_init(GTypeInstance * instance,gpointer klass)281 instance_init( GTypeInstance *instance, gpointer klass )
282 {
283 	static const gchar *thisfn = "na_module_instance_init";
284 	NAModule *self;
285 
286 	g_return_if_fail( NA_IS_MODULE( instance ));
287 
288 	g_debug( "%s: instance=%p (%s), klass=%p",
289 			thisfn, ( void * ) instance, G_OBJECT_TYPE_NAME( instance ), ( void * ) klass );
290 
291 	self = NA_MODULE( instance );
292 
293 	self->private = g_new0( NAModulePrivate, 1 );
294 
295 	self->private->dispose_has_run = FALSE;
296 }
297 
298 static void
instance_dispose(GObject * object)299 instance_dispose( GObject *object )
300 {
301 	static const gchar *thisfn = "na_module_instance_dispose";
302 	NAModule *self;
303 
304 	g_return_if_fail( NA_IS_MODULE( object ));
305 
306 	self = NA_MODULE( object );
307 
308 	if( !self->private->dispose_has_run ){
309 
310 		g_debug( "%s: object=%p (%s)", thisfn, ( void * ) object, G_OBJECT_TYPE_NAME( object ));
311 
312 		self->private->dispose_has_run = TRUE;
313 
314 		/* should trigger unload of the plugin */
315 		/* refused by GLib
316 		 * GLib-GObject-WARNING **: gtypemodule.c:111: unsolicitated invocation of g_object_dispose() on GTypeModule
317 		 */
318 		/*g_type_module_unuse( G_TYPE_MODULE( self ));*/
319 
320 		if( G_OBJECT_CLASS( st_parent_class )->dispose ){
321 			G_OBJECT_CLASS( st_parent_class )->dispose( object );
322 		}
323 	}
324 }
325 
326 static void
instance_finalize(GObject * object)327 instance_finalize( GObject *object )
328 {
329 	static const gchar *thisfn = "na_module_instance_finalize";
330 	NAModule *self;
331 
332 	g_return_if_fail( NA_IS_MODULE( object ));
333 
334 	g_debug( "%s: object=%p (%s)", thisfn, ( void * ) object, G_OBJECT_TYPE_NAME( object ));
335 
336 	self = NA_MODULE( object );
337 
338 	g_free( self->private );
339 
340 	/* chain call to parent class */
341 	if( G_OBJECT_CLASS( st_parent_class )->finalize ){
342 		G_OBJECT_CLASS( st_parent_class )->finalize( object );
343 	}
344 }
345 
346 int
main(int argc,char ** argv)347 main( int argc, char **argv )
348 {
349 	NAModule *module;
350 
351 #if !GLIB_CHECK_VERSION( 2,36, 0 )
352 	g_type_init();
353 #endif
354 
355 	/* dynamically load the module */
356 	module = load_plugin();
357 
358 	if( module ){
359 		/* call a function in the module */
360 		call_plugin_fn( module );
361 
362 		/* try to just unref the NAModule */
363 		/* not ok */
364 		/*g_object_unref( module );*/
365 
366 		/* try to unload the plugin */
367 		g_type_module_unuse( G_TYPE_MODULE( module ));
368 		/* then unref the object */
369 		/* not ok
370 		 * it happens that releasing the GTypeModule after having registered a GType or a
371 		 * GInterface is impossible
372 		 * see http://git.gnome.org/browse/glib/tree/gobject/gtypemodule.c
373 		 * and http://library.gnome.org/devel/gobject/unstable/GTypeModule.html#g-type-module-unuse
374 		 */
375 		/*g_object_unref( module );*/
376 	}
377 
378 	return( 0 );
379 }
380 
381 static NAModule *
load_plugin(void)382 load_plugin( void )
383 {
384 	NAModule *module;
385 
386 	module = NULL;
387 
388 	if( g_module_supported()){
389 
390 		module = g_object_new( NA_TYPE_MODULE, NULL );
391 		g_debug( "test_module_load_plugin: module=%p", ( void * ) module );
392 
393 		if( !g_type_module_use( G_TYPE_MODULE( module ))){
394 			g_object_unref( module );
395 		}
396 	}
397 
398 	return( module );
399 }
400 
401 static gboolean
on_module_load(GTypeModule * module)402 on_module_load( GTypeModule *module )
403 {
404 	gboolean ok;
405 	gchar *module_path;
406 	NAModule *na_module = NA_MODULE( module );
407 
408 	g_debug( "test_module_on_module_load" );
409 
410 	ok = TRUE;
411 	module_path = g_module_build_path( PKGLIBDIR, PLUGIN_NAME );
412 
413 	g_debug( "test_module_on_module_load: opening the library" );
414 	na_module->private->plugin = g_module_open( module_path, G_MODULE_BIND_LAZY | G_MODULE_BIND_LOCAL );
415 
416 	if( !na_module->private->plugin ){
417 		g_printerr( "%s: %s\n", module_path, g_module_error());
418 		ok = FALSE;
419 	}
420 
421 	g_free( module_path );
422 
423 	return( ok );
424 }
425 
426 static void
call_plugin_fn(NAModule * module)427 call_plugin_fn( NAModule *module )
428 {
429 	typedef void ( *PluginInit )( NAModule *module );
430 	PluginInit plugin_fn;
431 
432 	if( !g_module_symbol( module->private->plugin, "plugin_init", ( gpointer * ) &plugin_fn )){
433 		g_printerr( "%s\n", g_module_error());
434 		return;
435 	}
436 	if( !plugin_fn ){
437 		g_printerr( "%s\n", g_module_error());
438 		return;
439 	}
440 	plugin_fn( module );
441 }
442 
443 static void
on_unload_plugin(GTypeModule * module)444 on_unload_plugin( GTypeModule *module )
445 {
446 	g_debug( "test_module_on_unload_plugin" );
447 	if( !g_module_close( NA_MODULE( module )->private->plugin )){
448 		g_printerr( "%s\n", g_module_error());
449 	}
450 }
451