regen pidl all: rm epan/dissectors/pidl/*-stamp; pushd epan/dissectors/pidl/ && make...
[wireshark-sm.git] / wsutil / plugins.c
blob2386294992046494fcfc027165ef7b5fdf12430f
1 /* plugins.c
2 * plugin routines
4 * Wireshark - Network traffic analyzer
5 * By Gerald Combs <gerald@wireshark.org>
6 * Copyright 1998 Gerald Combs
8 * SPDX-License-Identifier: GPL-2.0-or-later
9 */
11 #include "config.h"
12 #define WS_LOG_DOMAIN LOG_DOMAIN_PLUGINS
13 #include "plugins.h"
15 #include <time.h>
17 #include <stdlib.h>
18 #include <stdio.h>
19 #include <string.h>
21 #include <gmodule.h>
23 #include <wsutil/filesystem.h>
24 #include <wsutil/privileges.h>
25 #include <wsutil/file_util.h>
26 #include <wsutil/report_message.h>
27 #include <wsutil/wslog.h>
29 typedef struct _plugin {
30 GModule *handle; /* handle returned by g_module_open */
31 char *name; /* plugin name */
32 const char *version; /* plugin version */
33 uint32_t flags; /* plugin flags */
34 } plugin;
36 #define TYPE_DIR_EPAN "epan"
37 #define TYPE_DIR_WIRETAP "wiretap"
38 #define TYPE_DIR_CODECS "codecs"
40 static GSList *plugins_module_list;
43 static inline const char *
44 type_to_dir(plugin_type_e type)
46 switch (type) {
47 case WS_PLUGIN_EPAN:
48 return TYPE_DIR_EPAN;
49 case WS_PLUGIN_WIRETAP:
50 return TYPE_DIR_WIRETAP;
51 case WS_PLUGIN_CODEC:
52 return TYPE_DIR_CODECS;
53 default:
54 ws_error("Unknown plugin type: %u. Aborting.", (unsigned) type);
55 break;
57 ws_assert_not_reached();
60 static inline const char *
61 flags_to_str(uint32_t flags)
63 /* XXX: Allow joining multiple types? Our plugins only implement a
64 * single type but out in the wild this may not be true. */
65 if (flags & WS_PLUGIN_DESC_DISSECTOR)
66 return "dissector";
67 else if (flags & WS_PLUGIN_DESC_FILE_TYPE)
68 return "file type";
69 else if (flags & WS_PLUGIN_DESC_CODEC)
70 return "codec";
71 else if (flags & WS_PLUGIN_DESC_EPAN)
72 return "epan";
73 else if (flags & WS_PLUGIN_DESC_TAP_LISTENER)
74 return "tap listener";
75 else if (flags & WS_PLUGIN_DESC_DFILTER)
76 return "dfilter";
77 else
78 return "unknown";
81 static void
82 free_plugin(void * data)
84 plugin *p = (plugin *)data;
85 g_module_close(p->handle);
86 g_free(p->name);
87 g_free(p);
90 static int
91 compare_plugins(const void *a, const void *b)
93 return g_strcmp0((*(plugin *const *)a)->name, (*(plugin *const *)b)->name);
96 static bool
97 pass_plugin_version_compatibility(GModule *handle, const char *name)
99 void * symb;
100 int major, minor;
102 if(!g_module_symbol(handle, "plugin_want_major", &symb)) {
103 report_failure("The plugin '%s' has no \"plugin_want_major\" symbol", name);
104 return false;
106 major = *(int *)symb;
108 if(!g_module_symbol(handle, "plugin_want_minor", &symb)) {
109 report_failure("The plugin '%s' has no \"plugin_want_minor\" symbol", name);
110 return false;
112 minor = *(int *)symb;
114 if (major != VERSION_MAJOR || minor != VERSION_MINOR) {
115 report_failure("The plugin '%s' was compiled for Wireshark version %d.%d",
116 name, major, minor);
117 return false;
120 return true;
123 // GLib and Qt allow ".dylib" and ".so" on macOS. Should we do the same?
124 #ifdef _WIN32
125 #define MODULE_SUFFIX ".dll"
126 #else
127 #define MODULE_SUFFIX ".so"
128 #endif
130 static void
131 scan_plugins_dir(GHashTable *plugins_module, const char *dirpath, plugin_type_e type, bool append_type)
133 GDir *dir;
134 const char *name; /* current file name */
135 char *plugin_folder;
136 char *plugin_file; /* current file full path */
137 GModule *handle; /* handle returned by g_module_open */
138 void * symbol;
139 const char *plug_version;
140 uint32_t flags;
141 plugin *new_plug;
143 if (append_type)
144 plugin_folder = g_build_filename(dirpath, type_to_dir(type), (char *)NULL);
145 else
146 plugin_folder = g_strdup(dirpath);
148 dir = g_dir_open(plugin_folder, 0, NULL);
149 if (dir == NULL) {
150 g_free(plugin_folder);
151 return;
154 ws_debug("Scanning plugins folder \"%s\"", plugin_folder);
156 while ((name = g_dir_read_name(dir)) != NULL) {
157 /* Skip anything but files with .dll or .so. */
158 if (!g_str_has_suffix(name, MODULE_SUFFIX))
159 continue;
162 * Check if the same name is already registered.
164 if (g_hash_table_lookup(plugins_module, name)) {
165 /* Yes, it is. */
166 report_warning("The plugin '%s' was found "
167 "in multiple directories", name);
168 continue;
171 plugin_file = g_build_filename(plugin_folder, name, (char *)NULL);
172 handle = g_module_open(plugin_file, G_MODULE_BIND_LOCAL);
173 if (handle == NULL) {
174 /* g_module_error() provides file path. */
175 report_failure("Couldn't load plugin '%s': %s", name,
176 g_module_error());
177 g_free(plugin_file);
178 continue;
181 if (!g_module_symbol(handle, "plugin_version", &symbol))
183 report_failure("The plugin '%s' has no \"plugin_version\" symbol", name);
184 g_module_close(handle);
185 g_free(plugin_file);
186 continue;
188 plug_version = (const char *)symbol;
190 if (!pass_plugin_version_compatibility(handle, name)) {
191 g_module_close(handle);
192 g_free(plugin_file);
193 continue;
196 /* Search for the entry point for the plugin registration function */
197 if (!g_module_symbol(handle, "plugin_register", &symbol)) {
198 report_failure("The plugin '%s' has no \"plugin_register\" symbol", name);
199 g_module_close(handle);
200 g_free(plugin_file);
201 continue;
204 DIAG_OFF_PEDANTIC
205 /* Found it, call the plugin registration function. */
206 ((plugin_register_func)symbol)();
207 DIAG_ON_PEDANTIC
209 /* Search for the (optional) description flag registration function */
210 if (g_module_symbol(handle, "plugin_describe", &symbol))
211 flags = ((plugin_describe_func)symbol)();
212 else
213 flags = 0;
215 new_plug = g_new(plugin, 1);
216 new_plug->handle = handle;
217 new_plug->name = g_strdup(name);
218 new_plug->version = plug_version;
219 new_plug->flags = flags;
221 /* Add it to the list of plugins. */
222 g_hash_table_replace(plugins_module, new_plug->name, new_plug);
223 ws_info("Registered plugin: %s (%s)", new_plug->name, plugin_file);
224 g_free(plugin_file);
226 ws_dir_close(dir);
227 g_free(plugin_folder);
231 * Scan for plugins.
233 plugins_t *
234 plugins_init(plugin_type_e type)
236 if (!g_module_supported())
237 return NULL; /* nothing to do */
239 GHashTable *plugins_module = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, free_plugin);
242 * Scan the global plugin directory.
244 scan_plugins_dir(plugins_module, get_plugins_dir_with_version(), type, true);
247 * If the program wasn't started with special privileges,
248 * scan the users plugin directory. (Even if we relinquish
249 * them, plugins aren't safe unless we've *permanently*
250 * relinquished them, and we can't do that in Wireshark as,
251 * if we need privileges to start capturing, we'd need to
252 * reclaim them before each time we start capturing.)
254 if (!started_with_special_privs()) {
255 scan_plugins_dir(plugins_module, get_plugins_pers_dir_with_version(), type, true);
258 plugins_module_list = g_slist_prepend(plugins_module_list, plugins_module);
260 return plugins_module;
263 WS_DLL_PUBLIC void
264 plugins_get_descriptions(plugin_description_callback callback, void *callback_data)
266 GPtrArray *plugins_array = g_ptr_array_new();
267 GHashTableIter iter;
268 void * value;
270 for (GSList *l = plugins_module_list; l != NULL; l = l->next) {
271 g_hash_table_iter_init (&iter, (GHashTable *)l->data);
272 while (g_hash_table_iter_next (&iter, NULL, &value)) {
273 g_ptr_array_add(plugins_array, value);
277 g_ptr_array_sort(plugins_array, compare_plugins);
279 for (unsigned i = 0; i < plugins_array->len; i++) {
280 plugin *plug = (plugin *)plugins_array->pdata[i];
281 callback(plug->name, plug->version, plug->flags, g_module_name(plug->handle), callback_data);
284 g_ptr_array_free(plugins_array, true);
287 static void
288 print_plugin_description(const char *name, const char *version,
289 uint32_t flags, const char *filename,
290 void *user_data _U_)
292 printf("%-16s\t%s\t%s\t%s\n", name, version, flags_to_str(flags), filename);
295 void
296 plugins_dump_all(void)
298 plugins_get_descriptions(print_plugin_description, NULL);
302 plugins_get_count(void)
304 unsigned count = 0;
306 for (GSList *l = plugins_module_list; l != NULL; l = l->next) {
307 count += g_hash_table_size((GHashTable *)l->data);
309 return count;
312 void
313 plugins_cleanup(plugins_t *plugins)
315 if (!plugins)
316 return;
318 plugins_module_list = g_slist_remove(plugins_module_list, plugins);
319 g_hash_table_destroy((GHashTable *)plugins);
322 bool
323 plugins_supported(void)
325 return g_module_supported();
329 * Editor modelines
331 * Local Variables:
332 * c-basic-offset: 4
333 * tab-width: 8
334 * indent-tabs-mode: nil
335 * End:
337 * ex: set shiftwidth=4 tabstop=8 expandtab:
338 * :indentSize=4:tabSize=8:noTabs=true: