1 /*
2 * Copyright (C) 2003-2012 Edscott Wilson Garcia
3 * EMail: edscott@users.sf.net
4 *
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This program 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
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program;
18 */
19
20 #define __MODULES_C__
21 #ifdef HAVE_CONFIG_H
22 # include <config.h>
23 #endif
24
25 //#define DEBUG
26
27 #include "rfm.h"
28 #include "rfm_modules.h"
29
30 static GHashTable *module_hash = NULL;
31
32
33 static RfmProgramOptions lite_plugin_options[]={
34 {"fstab",N_("Mount local disks and devices"),TRUE,NULL},
35 {"ps",N_("View current processes and monitor system state"),TRUE,NULL},
36 {"dotdesktop",N_("Next-generation application launcher."),TRUE,NULL},
37
38 {"submodule-label",N_("Windows networks (SMB)"),TRUE,NULL},
39 {"smb",N_("SMB Browser"),TRUE,NULL},
40 {"submodule-indent",NULL,TRUE,NULL},
41 {"workgroup",N_("Windows workgroup"),TRUE,NULL},
42 {"shares",N_("Windows Shares"),TRUE,NULL},
43 {"submodule-unindent",NULL,TRUE,NULL},
44
45 {"submodule-label",N_("FUSE Volume"),TRUE,NULL},
46 {"fuse",N_("Mount user-space filesystems (FUSE)"),TRUE,NULL},
47 {"submodule-indent",NULL,TRUE,NULL},
48 {"nfs",N_("NFS Network Volume"),TRUE,NULL},
49 {"sftp",N_("Secure FTP (SSH)"),TRUE,NULL},
50 #ifdef THIS_IS_LINUX
51 {"ecryptfs",N_("Encrypted filesystem"),TRUE,NULL},
52 {"obex",N_("Bluetooth Transfer"),TRUE,NULL},
53 {"ftp",N_("FTP Client"),TRUE,NULL},
54 #endif
55 {"cifs",N_("CIFS Volume"),TRUE,NULL},
56 {"submodule-unindent",NULL,TRUE,NULL},
57 {NULL,NULL,FALSE,NULL}
58 };
59
60 static RfmProgramOptions lite_module_options[]={
61 {"callbacks",N_("Callbacks"),FALSE,NULL},
62 {"bcrypt",N_("Blowfish"),FALSE,NULL},
63 {"settings",N_("Settings"),FALSE,NULL},
64 {"run",N_("Run program and return its output"),FALSE,NULL},
65 {"properties",N_("Properties"),TRUE,NULL},
66 {"completion",N_("Text completion"),TRUE,NULL},
67 {"combobox",N_("History of combo url."),TRUE,NULL},
68 {"mime",N_("Mime Type"),FALSE,NULL},
69 {"mimemagic",N_("Use MIME type magic"),TRUE,NULL},
70 {"mimezip",N_("ZIP archive plugin"),TRUE,NULL},
71 {"icons",N_("Icon Themes"),TRUE,NULL},
72 {NULL,NULL,FALSE,NULL}
73 };
74
rfm_get_lite_module_options(void)75 RfmProgramOptions *rfm_get_lite_module_options(void){return lite_module_options;}
rfm_get_lite_plugin_options(void)76 RfmProgramOptions *rfm_get_lite_plugin_options(void){return lite_plugin_options;}
77
78 // This is to set downstream modules/plugins to be used...
79 static gchar *plugin_dir=NULL;
80
rfm_set_module_dir(const gchar * libdir,const gchar * dir)81 const gchar *rfm_set_module_dir(const gchar *libdir, const gchar *dir){
82 DBG("rfm_set_module_dir() is no longer used\n");
83 return NULL;
84 }
85
rfm_set_plugin_dir(const gchar * libdir,const gchar * dir)86 const gchar *rfm_set_plugin_dir(const gchar *libdir, const gchar *dir){
87 g_free(plugin_dir);
88 plugin_dir = g_build_filename (libdir, dir, NULL);
89 return (const gchar *) plugin_dir;
90 }
91
rfm_plugin_dir(void)92 const gchar *rfm_plugin_dir(void){
93 if (!plugin_dir){
94 plugin_dir = g_build_filename (LIBDIR, "rfm", "plugins", NULL);
95
96 DBG("rfm_set_plugin_dir() not set. Using default %s\n",plugin_dir);
97 }
98 return (const gchar *) plugin_dir;
99 }
100
101 static pthread_mutex_t *
get_module_hash_mutex(void)102 get_module_hash_mutex(void){
103 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
104 return &mutex;
105 }
106
107
108 const gchar *
rfm_get_module_popup_id(void)109 rfm_get_module_popup_id(void){
110 return "module_popup_widget";
111 }
112
113 gboolean
rfm_is_root_plugin(const gchar * module_name)114 rfm_is_root_plugin(const gchar *module_name){
115 if (!module_name) return FALSE;
116 return (GPOINTER_TO_INT(rfm_void(PLUGIN_DIR, module_name, "is_root_module")));
117 }
118 const gchar *
rfm_get_plugin_icon(const gchar * module_name)119 rfm_get_plugin_icon(const gchar *module_name){
120 if (!module_name) return NULL;
121 return (rfm_void(PLUGIN_DIR, module_name, "module_icon_id"));
122 }
123 gchar *
rfm_get_plugin_label(const gchar * module_name)124 rfm_get_plugin_label(const gchar *module_name){
125 if (!module_name) return NULL;
126 return (rfm_void(PLUGIN_DIR, module_name, "module_label"));
127 }
128
129 void
rfm_sanity_check(int argc,char ** argv,int librfm_serial)130 rfm_sanity_check (int argc, char **argv, int librfm_serial) {
131 if(librfm_serial != LIBRFM_SERIAL) {
132 gchar *p = g_strdup_printf ("%s needs to be recompiled \n(has obsolete library headers)", argv[0]);
133 rfm_confirm(NULL,GTK_MESSAGE_ERROR,p,NULL,NULL);
134 g_assert_not_reached();
135 //g_error("%s\n", p);
136 }
137 return;
138 }
139
140 static gint
compare_strings(gconstpointer a,gconstpointer b)141 compare_strings (gconstpointer a, gconstpointer b) {
142 return (strcmp ((char *)a, (char *)b));
143 }
144
145 GSList *
rfm_find_plugins(void)146 rfm_find_plugins (void) {
147 static GSList *plugin_list = NULL;
148 static gsize initialized = 0;
149
150 // Only once...
151 if (g_once_init_enter(&initialized)){
152 GError *error = NULL;
153
154 GDir *dir;
155 if((dir = g_dir_open (PLUGIN_DIR, 0, &error)) != NULL) {
156 const gchar *name;
157 while((name = g_dir_read_name (dir)) != NULL) {
158 gchar *plugin;
159 if(strncmp (name, "lib", strlen ("lib"))){
160 plugin = g_strdup (name);
161 } else {
162 plugin = g_strdup (name + strlen ("lib"));
163 }
164 if(strchr (plugin, '.')) plugin = strtok (plugin, ".");
165
166 if(g_slist_find_custom (plugin_list, plugin, compare_strings)) {
167 g_free (plugin);
168 } else {
169 NOOP ("modules: g_slist_append %s\n", plugin);
170 plugin_list = g_slist_prepend (plugin_list, plugin);
171 }
172 }
173 g_dir_close (dir);
174 }
175 g_once_init_leave(&initialized, 1);
176 }
177 return plugin_list;
178 }
179
180
181 /*************************************************************/
182
183 //void unload_module (const gchar * module_name);
184
185 static void *
module_error(const gchar * module_name,gchar * module,gchar * warning)186 module_error(const gchar * module_name, gchar *module, gchar *warning){
187 pthread_mutex_t *module_hash_mutex = get_module_hash_mutex();
188 pthread_mutex_lock(module_hash_mutex);
189 g_hash_table_insert (module_hash, (gpointer) module_name, GINT_TO_POINTER(-1));
190 pthread_mutex_unlock(module_hash_mutex);
191 DBG ("Module %s (%s): %s\n", module, module_name, warning);
192 g_free (warning);
193 g_free (module);
194 return NULL;
195 }
196
197
198 GModule *
get_module_info(const gchar * librarydir,const gchar * module_name)199 get_module_info (const gchar * librarydir, const gchar * module_name) {
200 if (!module_name || !librarydir || !strlen(module_name) ) {
201 DBG("Failed to get module info on !module_name (%p) || !librarydir (%p) || !strlen(module_name) \"%s\"\n",
202 module_name, librarydir, module_name);
203 return NULL;
204 }
205
206 //Quick hack to avoid loading icon module when icontheme is not selected as an option...
207 if (strcmp(module_name, "icons")==0){
208 gboolean gtk_icontheme = (getenv("RFM_USE_GTK_ICON_THEME") && strlen(getenv("RFM_USE_GTK_ICON_THEME")));
209 if (!gtk_icontheme){
210 NOOP(stderr, "quick hack to avoid loading icon module...\n");
211 return NULL;
212 }
213 }
214
215
216
217 rfm_global_t *rfm_global_p = rfm_global();
218 if (rfm_global_p && rfm_global_p->status == STATUS_EXIT) {
219 TRACE("Refusing to get module info on STATUS_EXIT for %s\n",module_name);
220 return NULL;
221 }
222 GModule *module_cm = NULL;
223 void *(*module_sanity) (void);
224 int init_version;
225
226 gchar *module = g_module_build_path (librarydir, module_name);
227
228 if(!rfm_g_file_test (module, G_FILE_TEST_EXISTS)) {
229 DBG("Module \"%s\" does not exist!\n", module_name);
230 g_free (module);
231 return NULL;
232 }
233
234 if (strstr(module_name,"gridview"))DBG("getting module info for %s (librarydir=%s)\n", module, librarydir);
235
236 if(!module_hash) {
237 TRACE("get_module_info: creating module hash\n");
238 module_hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
239 if(!module_hash) g_assert_not_reached ();
240 }
241 NOOP ("looking for module in hash: %s\n", module_name);
242 pthread_mutex_t *module_hash_mutex = get_module_hash_mutex();
243 pthread_mutex_lock(module_hash_mutex);
244 module_cm = g_hash_table_lookup (module_hash, module_name);
245 pthread_mutex_unlock(module_hash_mutex);
246 if (GPOINTER_TO_INT(module_cm) == -1){
247 DBG("Module %s cannot be loaded.\n", module);
248 g_free(module);
249 return NULL;
250 }
251 if(module_cm != NULL) {
252 NOOP("module info for %s found (already loaded) 0x%x\n",
253 module,
254 GPOINTER_TO_INT(module_cm));
255 g_free(module);
256 return module_cm;
257 }
258
259 NOOP ("get_module_info(): RSS changes with load of %s\n", module);
260 // Here's the rub: lazy mode willfetch a symbol with the same name
261 // from another module than that which was specified.
262 // Only use lazy if different modules do not export symbols with the
263 // same name (or just do not use lazy).
264 #ifdef DEBUG
265 module_cm = g_module_open (module, G_MODULE_BIND_LAZY | G_MODULE_BIND_LOCAL);
266 #else
267 module_cm = g_module_open (module, G_MODULE_BIND_LOCAL);
268 #endif
269
270 if(!module_cm) {
271 return module_error(module_name, module,
272 g_strdup("g_module_open() == NULL\nmodule cannot be opened: Check if correctly installed or unresolved symbols within...\n****\n"));
273 }
274
275 // Check for module sanity
276 if(!g_module_symbol (module_cm, "module_sanity", (gpointer) & (module_sanity))) {
277 return module_error(module_name, module,
278 g_strdup ("Module is not sane!\nDoes module specify: \"LIBRFM_MODULE\"?"));
279 }
280
281 // Check for construction with correct structure serial (types.h)
282 init_version = GPOINTER_TO_INT ((*module_sanity) ());
283 if(init_version != LIBRFM_SERIAL) {
284 return module_error(module_name, module,
285 g_strdup ("Module is compiled with obsolete headers (not loaded)"));
286 }
287
288
289
290 NOOP ("adding module to module hash: %s (0x%lx)\n", module_name, (unsigned long)module_cm);
291 pthread_mutex_lock(module_hash_mutex);
292 g_hash_table_insert (module_hash, g_strdup(module_name), module_cm);
293 pthread_mutex_unlock(module_hash_mutex);
294 TRACE("get_module_info: module %s successfully loaded\n", module);
295 g_free (module);
296 return module_cm;
297
298 }
299
300
rfm_destroy_module_hash(void)301 void rfm_destroy_module_hash(void){
302 pthread_mutex_t *module_hash_mutex = get_module_hash_mutex();
303 pthread_mutex_lock(module_hash_mutex);
304 if (module_hash) g_hash_table_destroy(module_hash);
305 pthread_mutex_unlock(module_hash_mutex);
306 }
307
308
309 void *
rfm_void(const gchar * librarydir,const gchar * module_name,const gchar * function_id)310 rfm_void (const gchar * librarydir, const gchar * module_name, const gchar * function_id) {
311 if (!module_name || !strlen(module_name)) return NULL;
312 gchar *(*function) (void);
313 if(!librarydir || !module_name || !function_id) return NULL;
314 TRACE("attempting module function: %s\n", function_id);
315 GModule *module_cm = get_module_info (librarydir, module_name);
316 if(!module_cm){
317 DBG("get_module_info failed: %s\n", module_name);
318 return NULL;
319 }
320
321 if(!g_module_symbol (module_cm, function_id, (gpointer) & (function))) {
322 /*if (strcmp(function_id,"submodule_name")!=0 && strcmp(function_id,"submodule_dir")!=0) */
323 {
324 TRACE("g_module_symbol(%s) != FALSE in module %s\n", function_id, module_name);
325 }
326 /*unload_module(module_name); */
327 return NULL;
328 }
329 return (*function) ();
330 }
331
332 void *
rfm_natural(const gchar * librarydir,const gchar * module_name,void * n,const gchar * function_id)333 rfm_natural (const gchar * librarydir, const gchar * module_name, void *n, const gchar * function_id) {
334 gchar *(*function) (void *n);
335 if(!librarydir || !module_name || !function_id){
336 DBG("!librarydir (%s)|| !module_name (%s)|| !function_id(%s)\n",
337 librarydir, module_name, function_id);
338 return NULL;
339 }
340 GModule *module_cm = get_module_info (librarydir, module_name);
341 if(!module_cm) return NULL;
342 if(!g_module_symbol (module_cm, function_id, (gpointer) & (function))) {
343 if(strcmp (function_id, "submodule_name") != 0 && strcmp (function_id, "submodule_dir") != 0) {
344 DBG ("g_module_symbol(%s) != FALSE in module %s\n", function_id, module_name);
345 }
346 /*unload_module(module_name); */
347 return NULL;
348 }
349 return (*function) (n);
350 }
351
352 void *
rfm_rational(const gchar * librarydir,const gchar * module_name,void * p,void * q,const gchar * function_id)353 rfm_rational (const gchar * librarydir, const gchar * module_name, void *p, void *q, const gchar * function_id) {
354 gchar *(*function) (void *p, void *q);
355 if(!librarydir || !module_name || !function_id) return NULL;
356 GModule *module_cm = get_module_info (librarydir, module_name);
357 if(!module_cm) return NULL;
358 if(!g_module_symbol (module_cm, function_id, (gpointer) & (function))) {
359 if(strcmp (function_id, "submodule_name") != 0 && strcmp (function_id, "submodule_dir") != 0) {
360 DBG ("g_module_symbol(%s) != FALSE in module %s\n", function_id, module_name);
361 }
362 /*unload_module(module_name); */
363 return NULL;
364 }
365 return (*function) (p, q);
366 }
367
368 void *
rfm_complex(const gchar * librarydir,const gchar * module_name,void * p,void * q,void * r,const gchar * function_id)369 rfm_complex (const gchar * librarydir, const gchar * module_name, void *p, void *q, void *r, const gchar * function_id) {
370 gchar *(*function) (void *p, void *q, void *r);
371 if(!librarydir || !module_name || !function_id) return NULL;
372 GModule *module_cm = get_module_info (librarydir, module_name);
373 if(!module_cm) return NULL;
374 if(!g_module_symbol (module_cm, function_id, (gpointer) & (function))) {
375 if(strcmp (function_id, "submodule_name") != 0 && strcmp (function_id, "submodule_dir") != 0) {
376 DBG ("g_module_symbol(%s) != FALSE in module %s\n", function_id, module_name);
377 }
378 /*unload_module(module_name); */
379 return NULL;
380 }
381 return (*function) (p, q, r);
382 }
383
384 static void **
argument_container_new(gint size,const void ** in_vector)385 argument_container_new(gint size, const void **in_vector){
386 void **argument_container = (void **)malloc(size * sizeof(void *));
387 if (!argument_container) g_error("malloc %s\n", strerror(errno));
388 memset(argument_container, 0, size * sizeof(void *));
389 if (in_vector){
390 gint i;
391 for (i=0; i < size; i++) argument_container[i] = (void *)in_vector[i];
392 }
393 return argument_container;
394 }
395
396
397 G_MODULE_EXPORT
398 void *
rfm_vector_run(const gchar * librarydir,const gchar * module_name,void * vector_size_p,const void ** vector,const gchar * function_id)399 rfm_vector_run(const gchar * librarydir, const gchar * module_name,
400 void *vector_size_p, const void **vector, const gchar *function_id){
401
402 void **arg = argument_container_new(GPOINTER_TO_INT(vector_size_p), vector);
403 TRACE("vector run %d: %s\n", GPOINTER_TO_INT(vector_size_p), function_id);
404 return rfm_natural(librarydir, module_name, arg, function_id);
405 }
406
407
408 #if 0
409 void *
410 load_module (const gchar * librarydir, const gchar * module_name) {
411 GModule *module_cm = get_module_info (librarydir, module_name);
412 NOOP ("finished load attempt of module %s (%s)\n", module_name, librarydir);
413 if(!module_cm) {
414 DBG ("Show stopper: cannot get module info for %s\n", module_name);
415 g_assert_not_reached ();
416 }
417 return GINT_TO_POINTER(1);
418 }
419 #endif
420
421 void
rfm_unload_module(const gchar * module_name)422 rfm_unload_module (const gchar * module_name) {
423
424 if(!module_hash) return;
425 pthread_mutex_t *module_hash_mutex = get_module_hash_mutex();
426 pthread_mutex_lock(module_hash_mutex);
427 GModule *module_cm = g_hash_table_lookup (module_hash, module_name);
428 pthread_mutex_unlock(module_hash_mutex);
429 if(!module_cm) {
430 DBG ("module %s is not loaded\n", module_name);
431 return;
432 }
433 TRACE ("unloading module %s\n", module_name);
434
435 if(!g_module_close (module_cm)) {
436 DBG ("g_module_close (%s) failed\n", module_name);
437 } else {
438 pthread_mutex_lock(module_hash_mutex);
439 if(!g_hash_table_remove (module_hash, module_name)) {
440 DBG ("could not remove %s from module hash\n", module_name);
441 }
442 pthread_mutex_unlock(module_hash_mutex);
443 TRACE("module %s unloaded\n", module_name);
444 }
445 }
446
447
448 /*********************************************************************************************/
449
450
451