4 * Wireshark - Network traffic analyzer
5 * By Gerald Combs <gerald@wireshark.org>
6 * Copyright 1998 Gerald Combs
8 * SPDX-License-Identifier: GPL-2.0+
25 #include <wsutil/filesystem.h>
26 #include <wsutil/privileges.h>
27 #include <wsutil/file_util.h>
28 #include <wsutil/report_message.h>
30 #include <wsutil/plugins.h>
31 #include <wsutil/ws_printf.h> /* ws_debug_printf */
33 typedef struct _plugin {
34 GModule *handle; /* handle returned by g_module_open */
35 gchar *name; /* plugin name */
36 const gchar *version; /* plugin version */
37 const gchar *type_dir; /* filesystem name (where it resides). */
38 const gchar *type_name; /* user-facing name (what it does). Should these be capitalized? */
41 #define TYPE_DIR_EPAN "epan"
42 #define TYPE_DIR_WIRETAP "wiretap"
43 #define TYPE_DIR_CODECS "codecs"
45 #define TYPE_NAME_DISSECTOR "dissector"
46 #define TYPE_NAME_FILE_TYPE "file type"
47 #define TYPE_NAME_CODEC "codec"
49 /* array of plugins */
50 static GPtrArray *plugins_array = NULL;
51 /* map of names to plugin */
52 static GHashTable *plugins_table = NULL;
55 free_plugin(gpointer data)
57 plugin *p = (plugin *)data;
58 g_module_close(p->handle);
64 compare_plugins(gconstpointer a, gconstpointer b)
66 return g_strcmp0((*(const plugin **)a)->name, (*(const plugin **)b)->name);
70 plugins_scan_dir(GPtrArray **plugins_ptr, const char *dirpath, plugin_type_e type, gboolean build_dir)
73 const char *name; /* current file name */
74 gchar *path; /* current file full path */
75 GModule *handle; /* handle returned by g_module_open */
77 const char *plug_version, *plug_release;
81 dir = g_dir_open(dirpath, 0, NULL);
85 while ((name = g_dir_read_name(dir)) != NULL) {
86 /* Skip anything but files with G_MODULE_SUFFIX. */
87 dot = strrchr(name, '.');
88 if (dot == NULL || strcmp(dot+1, G_MODULE_SUFFIX) != 0)
92 if (strncmp(name, "nordic_ble.dll", 14) == 0) {
94 * Skip the Nordic BLE Sniffer dll on WIN32 because
95 * the dissector has been added as internal.
102 * Check if the same name is already registered.
104 if (g_hash_table_lookup(plugins_table, name)) {
107 report_warning("The plugin '%s' was found "
108 "in multiple directories", name);
113 path = g_build_filename(dirpath, name, (gchar *)NULL);
114 handle = g_module_open(path, G_MODULE_BIND_LOCAL);
116 if (handle == NULL) {
117 /* g_module_error() provides file path. */
118 report_failure("Couldn't load plugin '%s': %s", name,
123 if (!g_module_symbol(handle, "plugin_version", &symbol))
125 report_failure("The plugin '%s' has no \"plugin_version\" symbol", name);
126 g_module_close(handle);
129 plug_version = (const char *)symbol;
131 if (!g_module_symbol(handle, "plugin_release", &symbol))
133 report_failure("The plugin '%s' has no \"plugin_release\" symbol", name);
134 g_module_close(handle);
137 plug_release = (const char *)symbol;
138 if (strcmp(plug_release, VERSION_RELEASE) != 0) {
139 report_failure("The plugin '%s' was compiled for Wireshark version %s", name, plug_release);
140 g_module_close(handle);
144 /* Search for the entry point for the plugin type */
145 if (!g_module_symbol(handle, "plugin_register", &symbol)) {
146 report_failure("The plugin '%s' has no \"plugin_register\" symbol", name);
147 g_module_close(handle);
152 /* Found it, call the plugin registration function. */
153 ((plugin_register_func)symbol)();
156 new_plug = (plugin *)g_malloc(sizeof(plugin));
157 new_plug->handle = handle;
158 new_plug->name = g_strdup(name);
159 new_plug->version = plug_version;
160 new_plug->type_dir = "[build]";
164 new_plug->type_dir = TYPE_DIR_EPAN;
166 // XXX This isn't true for stats_tree and TRANSUM.
167 new_plug->type_name = TYPE_NAME_DISSECTOR;
169 case WS_PLUGIN_WIRETAP:
171 new_plug->type_dir = TYPE_DIR_WIRETAP;
173 new_plug->type_name = TYPE_NAME_FILE_TYPE;
175 case WS_PLUGIN_CODEC:
177 new_plug->type_dir = TYPE_DIR_CODECS;
179 new_plug->type_name = TYPE_NAME_CODEC;
182 g_error("Unknown plugin type: %u. Aborting.", (unsigned) type);
186 /* Add it to the list of plugins. */
187 if (*plugins_ptr == NULL)
188 *plugins_ptr = g_ptr_array_new_with_free_func(free_plugin);
189 g_ptr_array_add(*plugins_ptr, new_plug);
190 g_ptr_array_add(plugins_array, new_plug);
191 g_hash_table_insert(plugins_table, new_plug->name, new_plug);
197 * Scan the buildir for plugins.
200 scan_plugins_build_dir(GPtrArray **plugins_ptr, plugin_type_e type)
202 const char *plugin_dir;
204 char *plugin_dir_path;
205 WS_DIR *dir; /* scanned directory */
206 WS_DIRENT *file; /* current file */
208 plugin_dir = get_plugins_dir();
209 if ((dir = ws_dir_open(plugin_dir, 0, NULL)) == NULL)
212 plugins_scan_dir(plugins_ptr, plugin_dir, type, TRUE);
213 while ((file = ws_dir_read_name(dir)) != NULL)
215 name = ws_dir_get_name(file);
216 if (strcmp(name, ".") == 0 || strcmp(name, "..") == 0)
217 continue; /* skip "." and ".." */
219 * Get the full path of a ".libs" subdirectory of that
222 plugin_dir_path = g_build_filename(plugin_dir, name, ".libs", (gchar *)NULL);
223 if (test_for_directory(plugin_dir_path) != EISDIR) {
225 * Either it doesn't refer to a directory or it
226 * refers to something that doesn't exist.
228 * Assume that means that the plugins are in
229 * the subdirectory of the plugin directory, not
230 * a ".libs" subdirectory of that subdirectory.
232 g_free(plugin_dir_path);
233 plugin_dir_path = g_build_filename(plugin_dir, name, (gchar *)NULL);
235 plugins_scan_dir(plugins_ptr, plugin_dir_path, type, TRUE);
236 g_free(plugin_dir_path);
245 plugins_init(plugin_type_e type)
247 if (!g_module_supported())
248 return NULL; /* nothing to do */
250 const char *type_dir;
254 type_dir = TYPE_DIR_EPAN;
256 case WS_PLUGIN_WIRETAP:
257 type_dir = TYPE_DIR_WIRETAP;
259 case WS_PLUGIN_CODEC:
260 type_dir = TYPE_DIR_CODECS;
263 g_error("Unknown plugin type: %u. Aborting.", (unsigned) type);
268 GPtrArray *plugins = NULL;
270 if (plugins_table == NULL)
271 plugins_table = g_hash_table_new(g_str_hash, g_str_equal);
272 if (plugins_array == NULL)
273 plugins_array = g_ptr_array_new();
276 * Scan the global plugin directory.
277 * If we're running from a build directory, scan the "plugins"
278 * subdirectory, as that's where plugins are located in an
279 * out-of-tree build. If we find subdirectories scan those since
280 * they will contain plugins in the case of an in-tree build.
282 if (running_in_build_directory())
284 scan_plugins_build_dir(&plugins, type);
288 dirpath = g_build_filename(get_plugins_dir_with_version(), type_dir, (gchar *)NULL);
289 plugins_scan_dir(&plugins, dirpath, type, FALSE);
294 * If the program wasn't started with special privileges,
295 * scan the users plugin directory. (Even if we relinquish
296 * them, plugins aren't safe unless we've *permanently*
297 * relinquished them, and we can't do that in Wireshark as,
298 * if we need privileges to start capturing, we'd need to
299 * reclaim them before each time we start capturing.)
301 if (!started_with_special_privs())
303 dirpath = g_build_filename(get_plugins_pers_dir_with_version(), type_dir, (gchar *)NULL);
304 plugins_scan_dir(&plugins, dirpath, type, FALSE);
308 g_ptr_array_sort(plugins_array, compare_plugins);
314 plugins_get_descriptions(plugin_description_callback callback, void *callback_data)
319 for (guint i = 0; i < plugins_array->len; i++) {
320 plugin *plug = (plugin *)plugins_array->pdata[i];
321 callback(plug->name, plug->version, plug->type_name, g_module_name(plug->handle), callback_data);
326 print_plugin_description(const char *name, const char *version,
327 const char *description, const char *filename,
330 ws_debug_printf("%s\t%s\t%s\t%s\n", name, version, description, filename);
334 plugins_dump_all(void)
336 plugins_get_descriptions(print_plugin_description, NULL);
340 plugins_get_count(void)
343 return g_hash_table_size(plugins_table);
348 plugins_cleanup(plugins_t *plugins)
351 g_ptr_array_free((GPtrArray *)plugins, TRUE);
354 * This module uses global bookkeeping data structures and per-library
355 * objects sharing data. To avoid having to walk the plugins GPtrArray
356 * and delete each plugin from the global data structures we purge them
357 * once the first plugin cleanup function is called. This means that after
358 * calling ONE OF POSSIBLY MANY plugin cleanup function NO OTHER plugin
359 * APIs can be used except plugins_cleanup. If it ever becomes an issue
360 * it will be easy to change, for a small performance penalty.
363 g_hash_table_destroy(plugins_table);
364 plugins_table = NULL;
367 g_ptr_array_free(plugins_array, FALSE);
368 plugins_array = NULL;
372 #endif /* HAVE_PLUGINS */
380 * indent-tabs-mode: nil
383 * ex: set shiftwidth=4 tabstop=8 expandtab:
384 * :indentSize=4:tabSize=8:noTabs=true: