1 /*
2  *  nautilus-module.h - Interface to nautilus extensions
3  *
4  *  Copyright (C) 2003 Novell, Inc.
5  *
6  *  This library is free software; you can redistribute it and/or
7  *  modify it under the terms of the GNU Library General Public
8  *  License as published by the Free Software Foundation; either
9  *  version 2 of the License, or (at your option) any later version.
10  *
11  *  This library is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  *  Library General Public License for more details.
15  *
16  *  You should have received a copy of the GNU Library General Public
17  *  License along with this library; if not, see <http://www.gnu.org/licenses/>.
18  *
19  *  Author: Dave Camp <dave@ximian.com>
20  *
21  */
22 
23 #include <config.h>
24 #include "nautilus-module.h"
25 
26 #include <eel/eel-debug.h>
27 #include <gmodule.h>
28 
29 #define NAUTILUS_TYPE_MODULE            (nautilus_module_get_type ())
30 #define NAUTILUS_MODULE(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NAUTILUS_TYPE_MODULE, NautilusModule))
31 #define NAUTILUS_MODULE_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), NAUTILUS_TYPE_MODULE, NautilusModule))
32 #define NAUTILUS_IS_MODULE(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NAUTILUS_TYPE_MODULE))
33 #define NAUTILUS_IS_MODULE_CLASS(klass) (G_TYPE_CLASS_CHECK_CLASS_TYPE ((klass), NAUTILUS_TYPE_MODULE))
34 
35 typedef struct _NautilusModule NautilusModule;
36 typedef struct _NautilusModuleClass NautilusModuleClass;
37 
38 struct _NautilusModule
39 {
40     GTypeModule parent;
41 
42     GModule *library;
43 
44     char *path;
45 
46     void (*initialize) (GTypeModule *module);
47     void (*shutdown)   (void);
48 
49     void (*list_types) (const GType **types,
50                         int          *num_types);
51 };
52 
53 struct _NautilusModuleClass
54 {
55     GTypeModuleClass parent;
56 };
57 
58 static GList *module_objects = NULL;
59 
60 static GType nautilus_module_get_type (void);
61 
62 G_DEFINE_TYPE (NautilusModule, nautilus_module, G_TYPE_TYPE_MODULE);
63 
64 static gboolean
module_pulls_in_orbit(GModule * module)65 module_pulls_in_orbit (GModule *module)
66 {
67     gpointer symbol;
68     gboolean res;
69 
70     res = g_module_symbol (module, "ORBit_realloc_tcval", &symbol);
71 
72     return res;
73 }
74 
75 static gboolean
nautilus_module_load(GTypeModule * gmodule)76 nautilus_module_load (GTypeModule *gmodule)
77 {
78     NautilusModule *module;
79 
80     module = NAUTILUS_MODULE (gmodule);
81 
82     module->library = g_module_open (module->path, G_MODULE_BIND_LAZY | G_MODULE_BIND_LOCAL);
83 
84     if (!module->library)
85     {
86         g_warning ("%s", g_module_error ());
87         return FALSE;
88     }
89 
90     /* ORBit installs atexit() handlers, which would get unloaded together
91      * with the module now that the main process doesn't depend on GConf anymore,
92      * causing nautilus to sefgault at exit.
93      * If we detect that an extension would pull in ORBit, we make the
94      * module resident to prevent that.
95      */
96     if (module_pulls_in_orbit (module->library))
97     {
98         g_module_make_resident (module->library);
99     }
100 
101     if (!g_module_symbol (module->library,
102                           "nautilus_module_initialize",
103                           (gpointer *) &module->initialize) ||
104         !g_module_symbol (module->library,
105                           "nautilus_module_shutdown",
106                           (gpointer *) &module->shutdown) ||
107         !g_module_symbol (module->library,
108                           "nautilus_module_list_types",
109                           (gpointer *) &module->list_types))
110     {
111         g_warning ("%s", g_module_error ());
112         g_module_close (module->library);
113 
114         return FALSE;
115     }
116 
117     module->initialize (gmodule);
118 
119     return TRUE;
120 }
121 
122 static void
nautilus_module_unload(GTypeModule * gmodule)123 nautilus_module_unload (GTypeModule *gmodule)
124 {
125     NautilusModule *module;
126 
127     module = NAUTILUS_MODULE (gmodule);
128 
129     module->shutdown ();
130 
131     g_module_close (module->library);
132 
133     module->initialize = NULL;
134     module->shutdown = NULL;
135     module->list_types = NULL;
136 }
137 
138 static void
nautilus_module_finalize(GObject * object)139 nautilus_module_finalize (GObject *object)
140 {
141     NautilusModule *module;
142 
143     module = NAUTILUS_MODULE (object);
144 
145     g_free (module->path);
146 
147     G_OBJECT_CLASS (nautilus_module_parent_class)->finalize (object);
148 }
149 
150 static void
nautilus_module_init(NautilusModule * module)151 nautilus_module_init (NautilusModule *module)
152 {
153 }
154 
155 static void
nautilus_module_class_init(NautilusModuleClass * class)156 nautilus_module_class_init (NautilusModuleClass *class)
157 {
158     G_OBJECT_CLASS (class)->finalize = nautilus_module_finalize;
159     G_TYPE_MODULE_CLASS (class)->load = nautilus_module_load;
160     G_TYPE_MODULE_CLASS (class)->unload = nautilus_module_unload;
161 }
162 
163 static void
module_object_weak_notify(gpointer user_data,GObject * object)164 module_object_weak_notify (gpointer  user_data,
165                            GObject  *object)
166 {
167     module_objects = g_list_remove (module_objects, object);
168 }
169 
170 static void
add_module_objects(NautilusModule * module)171 add_module_objects (NautilusModule *module)
172 {
173     const GType *types;
174     int num_types;
175     int i;
176 
177     module->list_types (&types, &num_types);
178 
179     for (i = 0; i < num_types; i++)
180     {
181         if (types[i] == 0)           /* Work around broken extensions */
182         {
183             break;
184         }
185         nautilus_module_add_type (types[i]);
186     }
187 }
188 
189 static NautilusModule *
nautilus_module_load_file(const char * filename)190 nautilus_module_load_file (const char *filename)
191 {
192     NautilusModule *module;
193 
194     module = g_object_new (NAUTILUS_TYPE_MODULE, NULL);
195     module->path = g_strdup (filename);
196 
197     if (g_type_module_use (G_TYPE_MODULE (module)))
198     {
199         add_module_objects (module);
200         g_type_module_unuse (G_TYPE_MODULE (module));
201         return module;
202     }
203     else
204     {
205         g_object_unref (module);
206         return NULL;
207     }
208 }
209 
210 static void
load_module_dir(const char * dirname)211 load_module_dir (const char *dirname)
212 {
213     GDir *dir;
214 
215     dir = g_dir_open (dirname, 0, NULL);
216 
217     if (dir)
218     {
219         const char *name;
220 
221         while ((name = g_dir_read_name (dir)))
222         {
223             if (g_str_has_suffix (name, "." G_MODULE_SUFFIX))
224             {
225                 char *filename;
226 
227                 filename = g_build_filename (dirname,
228                                              name,
229                                              NULL);
230                 nautilus_module_load_file (filename);
231                 g_free (filename);
232             }
233         }
234 
235         g_dir_close (dir);
236     }
237 }
238 
239 static void
free_module_objects(void)240 free_module_objects (void)
241 {
242     GList *l, *next;
243 
244     for (l = module_objects; l != NULL; l = next)
245     {
246         next = l->next;
247         g_object_unref (l->data);
248     }
249 
250     g_list_free (module_objects);
251 }
252 
253 void
nautilus_module_setup(void)254 nautilus_module_setup (void)
255 {
256     static gboolean initialized = FALSE;
257 
258     if (!initialized)
259     {
260         initialized = TRUE;
261 
262         load_module_dir (NAUTILUS_EXTENSIONDIR);
263 
264         eel_debug_call_at_shutdown (free_module_objects);
265     }
266 }
267 
268 GList *
nautilus_module_get_extensions_for_type(GType type)269 nautilus_module_get_extensions_for_type (GType type)
270 {
271     GList *l;
272     GList *ret = NULL;
273 
274     for (l = module_objects; l != NULL; l = l->next)
275     {
276         if (G_TYPE_CHECK_INSTANCE_TYPE (G_OBJECT (l->data),
277                                         type))
278         {
279             g_object_ref (l->data);
280             ret = g_list_prepend (ret, l->data);
281         }
282     }
283 
284     return ret;
285 }
286 
287 void
nautilus_module_extension_list_free(GList * extensions)288 nautilus_module_extension_list_free (GList *extensions)
289 {
290     GList *l, *next;
291 
292     for (l = extensions; l != NULL; l = next)
293     {
294         next = l->next;
295         g_object_unref (l->data);
296     }
297     g_list_free (extensions);
298 }
299 
300 void
nautilus_module_add_type(GType type)301 nautilus_module_add_type (GType type)
302 {
303     GObject *object;
304 
305     object = g_object_new (type, NULL);
306     g_object_weak_ref (object,
307                        (GWeakNotify) module_object_weak_notify,
308                        NULL);
309 
310     module_objects = g_list_prepend (module_objects, object);
311 }
312