plugins: Fix crash loading binary module twice
[metze/wireshark/wip.git] / wsutil / plugins.c
1 /* plugins.c
2  * plugin routines
3  *
4  * Wireshark - Network traffic analyzer
5  * By Gerald Combs <gerald@wireshark.org>
6  * Copyright 1998 Gerald Combs
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License
10  * as published by the Free Software Foundation; either version 2
11  * of the License, or (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21  */
22
23 #include "config.h"
24
25 #ifdef HAVE_PLUGINS
26
27 #include <time.h>
28
29 #include <stdlib.h>
30 #include <stdio.h>
31 #include <string.h>
32 #include <errno.h>
33
34 #include <glib.h>
35 #include <gmodule.h>
36
37 #include <wsutil/filesystem.h>
38 #include <wsutil/privileges.h>
39 #include <wsutil/file_util.h>
40 #include <wsutil/report_message.h>
41
42 #include <wsutil/plugins.h>
43 #include <wsutil/ws_printf.h> /* ws_debug_printf */
44
45 /* linked list of all plugins */
46 typedef struct _plugin {
47     GModule        *handle;       /* handle returned by g_module_open */
48     gchar          *name;         /* plugin name */
49     gchar          *version;      /* plugin version */
50     guint32         types;        /* bitmask of plugin types this plugin supports */
51     struct _plugin *next;         /* forward link */
52 } plugin;
53
54 static plugin *plugin_list = NULL;
55
56 /*
57  * Add a new plugin type.
58  * Takes a callback routine as an argument; it is called for each plugin
59  * we find, and handed a handle for the plugin, the name of the plugin,
60  * and the version string for the plugin.  The plugin returns TRUE if
61  * it's a plugin for that type and FALSE if not.
62  */
63 typedef struct {
64     const char *type;
65     plugin_check_type_callback callback;
66     guint type_val;
67 } plugin_type;
68
69 static GSList *plugin_types = NULL;
70
71 void
72 add_plugin_type(const char *type, plugin_check_type_callback callback)
73 {
74     plugin_type *new_type;
75     static guint type_val;
76
77     if (type_val >= 32) {
78         /*
79          * There's a bitmask of types that a plugin provides, and it's
80          * 32 bits, so we don't support types > 31.
81          */
82         report_failure("At most 32 plugin types can be supported, so the plugin type '%s' won't be supported.",
83                        type);
84         return;
85     }
86     new_type = (plugin_type *)g_malloc(sizeof (plugin_type));
87     new_type->type = type;
88     new_type->callback = callback;
89     new_type->type_val = type_val;
90     plugin_types = g_slist_append(plugin_types, new_type);
91     type_val++;
92 }
93
94 /*
95  * add a new plugin to the list
96  */
97 static void
98 add_plugin(plugin *new_plug)
99 {
100     plugin *pt_plug;
101
102     pt_plug = plugin_list;
103     if (!pt_plug) /* the list is empty */
104     {
105         plugin_list = new_plug;
106     }
107     else
108     {
109         while (1)
110         {
111             /* we found the last plugin in the list */
112             if (pt_plug->next == NULL)
113                 break;
114
115             pt_plug = pt_plug->next;
116         }
117         pt_plug->next = new_plug;
118     }
119 }
120
121 static gboolean
122 check_if_plugin_exists(const char *name)
123 {
124     for (plugin *p = plugin_list; p != NULL; p = p->next) {
125         if (strcmp(p->name, name) == 0) {
126             return TRUE;
127         }
128     }
129     return FALSE;
130 }
131
132 static void
133 call_plugin_callback(gpointer data, gpointer user_data)
134 {
135     plugin_type *type = (plugin_type *)data;
136     plugin *new_plug = (plugin *)user_data;
137
138     if ((*type->callback)(new_plug->handle)) {
139         /* The plugin supports this type */
140         new_plug->types |= 1 << type->type_val;
141     }
142 }
143
144 static void
145 plugins_scan_dir(const char *dirname, plugin_load_failure_mode mode)
146 {
147 #define FILENAME_LEN        1024
148     WS_DIR        *dir;             /* scanned directory */
149     WS_DIRENT     *file;            /* current file */
150     const char    *name;
151     gchar          filename[FILENAME_LEN];   /* current file name */
152     GModule       *handle;          /* handle returned by g_module_open */
153     gpointer       gp;
154     plugin        *new_plug;
155     gchar         *dot;
156
157     if (!g_file_test(dirname, G_FILE_TEST_EXISTS) || !g_file_test(dirname, G_FILE_TEST_IS_DIR)) {
158         return;
159     }
160
161     if ((dir = ws_dir_open(dirname, 0, NULL)) != NULL)
162     {
163         while ((file = ws_dir_read_name(dir)) != NULL)
164         {
165             name = ws_dir_get_name(file);
166
167             /*
168              * GLib 2.x defines G_MODULE_SUFFIX as the extension used on
169              * this platform for loadable modules.
170              */
171             /* skip anything but files with G_MODULE_SUFFIX */
172             dot = strrchr(name, '.');
173             if (dot == NULL || strcmp(dot+1, G_MODULE_SUFFIX) != 0)
174                 continue;
175 #if WIN32
176             if (strncmp(name, "nordic_ble.dll", 14) == 0)
177                 /*
178                  * Skip the Nordic BLE Sniffer dll on WIN32 because
179                  * the dissector has been added as internal.
180                  */
181                 continue;
182 #endif
183             g_snprintf(filename, FILENAME_LEN, "%s" G_DIR_SEPARATOR_S "%s",
184                        dirname, name);
185
186             /*
187              * Check if the same name is already registered.
188              */
189             if (check_if_plugin_exists(name)) {
190                 /* Yes, it is. */
191                 if (mode == REPORT_LOAD_FAILURE) {
192                     report_warning("The plugin '%s' was found "
193                             "in multiple directories.\n", name);
194                 }
195                 continue;
196             }
197
198             if ((handle = g_module_open(filename, G_MODULE_BIND_LOCAL)) == NULL)
199             {
200                 /*
201                  * Only report load failures if we were asked to.
202                  *
203                  * XXX - we really should put different types of plugins
204                  * (libwiretap, libwireshark) in different subdirectories,
205                  * give libwiretap and libwireshark init routines that
206                  * load the plugins, and have them scan the appropriate
207                  * subdirectories so tha we don't even *try* to, for
208                  * example, load libwireshark plugins in programs that
209                  * only use libwiretap.
210                  */
211                 if (mode == REPORT_LOAD_FAILURE) {
212                     report_failure("Couldn't load module %s: %s", filename,
213                                    g_module_error());
214                 }
215                 continue;
216             }
217
218             if (!g_module_symbol(handle, "version", &gp))
219             {
220                 report_failure("The plugin %s has no version symbol", name);
221                 g_module_close(handle);
222                 continue;
223             }
224
225             new_plug = (plugin *)g_malloc(sizeof(plugin));
226             new_plug->handle = handle;
227             new_plug->name = g_strdup(name);
228             new_plug->version = (char *)gp;
229             new_plug->types = 0;
230             new_plug->next = NULL;
231
232             /*
233              * Hand the plugin to each of the plugin type callbacks.
234              */
235             g_slist_foreach(plugin_types, call_plugin_callback, new_plug);
236
237             /*
238              * Does this dissector do anything useful?
239              */
240             if (new_plug->types == 0)
241             {
242                 /*
243                  * No.
244                  *
245                  * Only report this failure if we were asked to; it might
246                  * just mean that it's a plugin type that this program
247                  * doesn't support, such as a libwireshark plugin in
248                  * a program that doesn't use libwireshark.
249                  *
250                  * XXX - we really should put different types of plugins
251                  * (libwiretap, libwireshark) in different subdirectories,
252                  * give libwiretap and libwireshark init routines that
253                  * load the plugins, and have them scan the appropriate
254                  * subdirectories so tha we don't even *try* to, for
255                  * example, load libwireshark plugins in programs that
256                  * only use libwiretap.
257                  */
258                 if (mode == REPORT_LOAD_FAILURE) {
259                     report_failure("The plugin '%s' has no registration routines",
260                                    name);
261                 }
262                 g_module_close(handle);
263                 g_free(new_plug->name);
264                 g_free(new_plug);
265                 continue;
266             }
267
268             /*
269              * OK, add it to the list of plugins.
270              */
271             add_plugin(new_plug);
272         }
273         ws_dir_close(dir);
274     }
275 }
276
277
278 /*
279  * Scan for plugins.
280  */
281 void
282 scan_plugins(plugin_load_failure_mode mode)
283 {
284     const char *plugin_dir;
285     const char *name;
286     char *plugin_dir_path;
287     WS_DIR *dir;                /* scanned directory */
288     WS_DIRENT *file;            /* current file */
289
290     if (plugin_list == NULL)    /* only scan for plugins once */
291     {
292         /*
293          * Scan the global plugin directory.
294          * If we're running from a build directory, scan the "plugins"
295          * subdirectory, as that's where plugins are located in an
296          * out-of-tree build. If we find subdirectories scan those since
297          * they will contain plugins in the case of an in-tree build.
298          */
299         plugin_dir = get_plugins_dir();
300         if (plugin_dir == NULL)
301         {
302             /* We couldn't find the plugin directory. */
303             return;
304         }
305         if (running_in_build_directory())
306         {
307             if ((dir = ws_dir_open(plugin_dir, 0, NULL)) != NULL)
308             {
309                 plugins_scan_dir(plugin_dir, mode);
310                 while ((file = ws_dir_read_name(dir)) != NULL)
311                 {
312                     name = ws_dir_get_name(file);
313                     if (strcmp(name, ".") == 0 || strcmp(name, "..") == 0)
314                         continue;        /* skip "." and ".." */
315                     /*
316                      * Get the full path of a ".libs" subdirectory of that
317                      * directory.
318                      */
319                     plugin_dir_path = g_strdup_printf(
320                         "%s" G_DIR_SEPARATOR_S "%s" G_DIR_SEPARATOR_S ".libs",
321                         plugin_dir, name);
322                     if (test_for_directory(plugin_dir_path) != EISDIR) {
323                         /*
324                          * Either it doesn't refer to a directory or it
325                          * refers to something that doesn't exist.
326                          *
327                          * Assume that means that the plugins are in
328                          * the subdirectory of the plugin directory, not
329                          * a ".libs" subdirectory of that subdirectory.
330                          */
331                         g_free(plugin_dir_path);
332                         plugin_dir_path = g_strdup_printf("%s" G_DIR_SEPARATOR_S "%s",
333                             plugin_dir, name);
334                     }
335                     plugins_scan_dir(plugin_dir_path, mode);
336                     g_free(plugin_dir_path);
337                 }
338                 ws_dir_close(dir);
339             }
340         }
341         else
342         {
343             plugins_scan_dir(get_plugins_dir_with_version(), mode);
344         }
345
346         /*
347          * If the program wasn't started with special privileges,
348          * scan the users plugin directory.  (Even if we relinquish
349          * them, plugins aren't safe unless we've *permanently*
350          * relinquished them, and we can't do that in Wireshark as,
351          * if we need privileges to start capturing, we'd need to
352          * reclaim them before each time we start capturing.)
353          */
354         if (!started_with_special_privs())
355         {
356             plugins_scan_dir(get_plugins_pers_dir_with_version(), mode);
357         }
358     }
359 }
360
361 /*
362  * Iterate over all plugins, calling a callback with information about
363  * the plugin.
364  */
365 typedef struct {
366     plugin  *pt_plug;
367     GString *types;
368     const char *sep;
369 } type_callback_info;
370
371 static void
372 add_plugin_type_description(gpointer data, gpointer user_data)
373 {
374     plugin_type *type = (plugin_type *)data;
375     type_callback_info *info = (type_callback_info *)user_data;
376
377     /*
378      * If the plugin handles this type, add the type to the list of types.
379      */
380     if (info->pt_plug->types & (1 << type->type_val)) {
381         g_string_append_printf(info->types, "%s%s", info->sep, type->type);
382         info->sep = ", ";
383     }
384 }
385
386 WS_DLL_PUBLIC void
387 plugins_get_descriptions(plugin_description_callback callback, void *user_data)
388 {
389     type_callback_info info;
390
391     info.types = NULL; /* Certain compiler suites need a init state for this variable */
392     for (info.pt_plug = plugin_list; info.pt_plug != NULL;
393          info.pt_plug = info.pt_plug->next)
394     {
395         info.sep = "";
396         info.types = g_string_new("");
397
398         /*
399          * Build a list of all the plugin types.
400          */
401         g_slist_foreach(plugin_types, add_plugin_type_description, &info);
402
403         /*
404          * And hand the information to the callback.
405          */
406         callback(info.pt_plug->name, info.pt_plug->version, info.types->str,
407                  g_module_name(info.pt_plug->handle), user_data);
408
409         g_string_free(info.types, TRUE);
410     }
411 }
412
413 static void
414 print_plugin_description(const char *name, const char *version,
415                          const char *description, const char *filename,
416                          void *user_data _U_)
417 {
418     ws_debug_printf("%s\t%s\t%s\t%s\n", name, version, description, filename);
419 }
420
421 void
422 plugins_dump_all(void)
423 {
424     plugins_get_descriptions(print_plugin_description, NULL);
425 }
426
427 static void
428 free_plugin_type(gpointer p, gpointer user_data _U_)
429 {
430     g_free(p);
431 }
432
433 void
434 plugins_cleanup(void)
435 {
436     plugin* cur, *next;
437
438     for (cur = plugin_list; cur != NULL; cur = next) {
439         next = cur->next;
440         g_free(cur->name);
441         g_free(cur);
442     }
443
444     g_slist_foreach(plugin_types, free_plugin_type, NULL);
445     g_slist_free(plugin_types);
446 }
447
448 #endif /* HAVE_PLUGINS */
449
450 /*
451  * Editor modelines
452  *
453  * Local Variables:
454  * c-basic-offset: 4
455  * tab-width: 8
456  * indent-tabs-mode: nil
457  * End:
458  *
459  * ex: set shiftwidth=4 tabstop=8 expandtab:
460  * :indentSize=4:tabSize=8:noTabs=true:
461  */