epan: use json_dumper for json outputs.
[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  * SPDX-License-Identifier: GPL-2.0-or-later
9  */
10
11 #include "config.h"
12
13 #include <time.h>
14
15 #include <stdlib.h>
16 #include <stdio.h>
17 #include <string.h>
18 #include <errno.h>
19
20 #include <glib.h>
21 #include <gmodule.h>
22
23 #include <wsutil/filesystem.h>
24 #include <wsutil/privileges.h>
25 #include <wsutil/file_util.h>
26 #include <wsutil/report_message.h>
27
28 #include <wsutil/plugins.h>
29 #include <wsutil/ws_printf.h> /* ws_debug_printf */
30
31 typedef struct _plugin {
32     GModule        *handle;       /* handle returned by g_module_open */
33     gchar          *name;         /* plugin name */
34     const gchar    *version;      /* plugin version */
35     const gchar    *type_name;    /* user-facing name (what it does). Should these be capitalized? */
36 } plugin;
37
38 #define TYPE_DIR_EPAN       "epan"
39 #define TYPE_DIR_WIRETAP    "wiretap"
40 #define TYPE_DIR_CODECS     "codecs"
41
42 #define TYPE_NAME_DISSECTOR "dissector"
43 #define TYPE_NAME_FILE_TYPE "file type"
44 #define TYPE_NAME_CODEC     "codec"
45
46
47 static GSList *plugins_module_list = NULL;
48
49
50 static inline const char *
51 type_to_dir(plugin_type_e type)
52 {
53     switch (type) {
54     case WS_PLUGIN_EPAN:
55         return TYPE_DIR_EPAN;
56     case WS_PLUGIN_WIRETAP:
57         return TYPE_DIR_WIRETAP;
58     case WS_PLUGIN_CODEC:
59         return TYPE_DIR_CODECS;
60     default:
61         g_error("Unknown plugin type: %u. Aborting.", (unsigned) type);
62         break;
63     }
64     g_assert_not_reached();
65 }
66
67 static inline const char *
68 type_to_name(plugin_type_e type)
69 {
70     switch (type) {
71     case WS_PLUGIN_EPAN:
72         return TYPE_NAME_DISSECTOR;
73     case WS_PLUGIN_WIRETAP:
74         return TYPE_NAME_FILE_TYPE;
75     case WS_PLUGIN_CODEC:
76         return TYPE_NAME_CODEC;
77     default:
78         g_error("Unknown plugin type: %u. Aborting.", (unsigned) type);
79         break;
80     }
81     g_assert_not_reached();
82 }
83
84 static void
85 free_plugin(gpointer data)
86 {
87     plugin *p = (plugin *)data;
88     g_module_close(p->handle);
89     g_free(p->name);
90     g_free(p);
91 }
92
93 static gint
94 compare_plugins(gconstpointer a, gconstpointer b)
95 {
96     return g_strcmp0((*(plugin *const *)a)->name, (*(plugin *const *)b)->name);
97 }
98
99 static gboolean
100 pass_plugin_version_compatibility(GModule *handle, const char *name)
101 {
102     gpointer symb;
103     int major, minor;
104
105     if(!g_module_symbol(handle, "plugin_want_major", &symb)) {
106         report_failure("The plugin '%s' has no \"plugin_want_major\" symbol", name);
107         return FALSE;
108     }
109     major = *(int *)symb;
110
111     if(!g_module_symbol(handle, "plugin_want_minor", &symb)) {
112         report_failure("The plugin '%s' has no \"plugin_want_minor\" symbol", name);
113         return FALSE;
114     }
115     minor = *(int *)symb;
116
117     if (major != VERSION_MAJOR || minor != VERSION_MINOR) {
118         report_failure("The plugin '%s' was compiled for Wireshark version %d.%d",
119                             name, major, minor);
120         return FALSE;
121     }
122
123     return TRUE;
124 }
125
126 static void
127 scan_plugins_dir(GHashTable *plugins_module, const char *dirpath, plugin_type_e type, gboolean append_type)
128 {
129     GDir          *dir;
130     const char    *name;            /* current file name */
131     gchar         *plugin_folder;
132     gchar         *plugin_file;     /* current file full path */
133     GModule       *handle;          /* handle returned by g_module_open */
134     gpointer       symbol;
135     const char    *plug_version;
136     plugin        *new_plug;
137
138     if (append_type)
139         plugin_folder = g_build_filename(dirpath, type_to_dir(type), (gchar *)NULL);
140     else
141         plugin_folder = g_strdup(dirpath);
142
143     dir = g_dir_open(plugin_folder, 0, NULL);
144     if (dir == NULL) {
145         g_free(plugin_folder);
146         return;
147     }
148
149     while ((name = g_dir_read_name(dir)) != NULL) {
150         /* Skip anything but files with G_MODULE_SUFFIX. */
151         if (!g_str_has_suffix(name, "." G_MODULE_SUFFIX))
152             continue;
153
154         /*
155          * Check if the same name is already registered.
156          */
157         if (g_hash_table_lookup(plugins_module, name)) {
158             /* Yes, it is. */
159             report_warning("The plugin '%s' was found "
160                                 "in multiple directories", name);
161             continue;
162         }
163
164         plugin_file = g_build_filename(plugin_folder, name, (gchar *)NULL);
165         handle = g_module_open(plugin_file, G_MODULE_BIND_LOCAL);
166         g_free(plugin_file);
167         if (handle == NULL) {
168             /* g_module_error() provides file path. */
169             report_failure("Couldn't load plugin '%s': %s", name,
170                             g_module_error());
171             continue;
172         }
173
174         if (!g_module_symbol(handle, "plugin_version", &symbol))
175         {
176             report_failure("The plugin '%s' has no \"plugin_version\" symbol", name);
177             g_module_close(handle);
178             continue;
179         }
180         plug_version = (const char *)symbol;
181
182         if (!pass_plugin_version_compatibility(handle, name)) {
183             g_module_close(handle);
184             continue;
185         }
186
187         /* Search for the entry point for the plugin registration function */
188         if (!g_module_symbol(handle, "plugin_register", &symbol)) {
189             report_failure("The plugin '%s' has no \"plugin_register\" symbol", name);
190             g_module_close(handle);
191             continue;
192         }
193
194 DIAG_OFF_PEDANTIC
195         /* Found it, call the plugin registration function. */
196         ((plugin_register_func)symbol)();
197 DIAG_ON_PEDANTIC
198
199         new_plug = (plugin *)g_malloc(sizeof(plugin));
200         new_plug->handle = handle;
201         new_plug->name = g_strdup(name);
202         new_plug->version = plug_version;
203         new_plug->type_name = type_to_name(type);
204
205         /* Add it to the list of plugins. */
206         g_hash_table_insert(plugins_module, new_plug->name, new_plug);
207     }
208     ws_dir_close(dir);
209     g_free(plugin_folder);
210 }
211
212 /*
213  * Scan for plugins.
214  */
215 plugins_t *
216 plugins_init(plugin_type_e type)
217 {
218     if (!g_module_supported())
219         return NULL; /* nothing to do */
220
221     GHashTable *plugins_module = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, free_plugin);
222
223     /*
224      * Scan the global plugin directory.
225      */
226     scan_plugins_dir(plugins_module, get_plugins_dir_with_version(), type, TRUE);
227
228     /*
229      * If the program wasn't started with special privileges,
230      * scan the users plugin directory.  (Even if we relinquish
231      * them, plugins aren't safe unless we've *permanently*
232      * relinquished them, and we can't do that in Wireshark as,
233      * if we need privileges to start capturing, we'd need to
234      * reclaim them before each time we start capturing.)
235      */
236     if (!started_with_special_privs()) {
237         scan_plugins_dir(plugins_module, get_plugins_pers_dir_with_version(), type, TRUE);
238     }
239
240     plugins_module_list = g_slist_prepend(plugins_module_list, plugins_module);
241
242     return plugins_module;
243 }
244
245 WS_DLL_PUBLIC void
246 plugins_get_descriptions(plugin_description_callback callback, void *callback_data)
247 {
248     GPtrArray *plugins_array = g_ptr_array_new();
249     GHashTableIter iter;
250     gpointer value;
251
252     for (GSList *l = plugins_module_list; l != NULL; l = l->next) {
253         g_hash_table_iter_init (&iter, (GHashTable *)l->data);
254         while (g_hash_table_iter_next (&iter, NULL, &value)) {
255             g_ptr_array_add(plugins_array, value);
256         }
257     }
258
259     g_ptr_array_sort(plugins_array, compare_plugins);
260
261     for (guint i = 0; i < plugins_array->len; i++) {
262         plugin *plug = (plugin *)plugins_array->pdata[i];
263         callback(plug->name, plug->version, plug->type_name, g_module_name(plug->handle), callback_data);
264     }
265
266     g_ptr_array_free(plugins_array, TRUE);
267 }
268
269 static void
270 print_plugin_description(const char *name, const char *version,
271                          const char *description, const char *filename,
272                          void *user_data _U_)
273 {
274     ws_debug_printf("%-16s\t%s\t%s\t%s\n", name, version, description, filename);
275 }
276
277 void
278 plugins_dump_all(void)
279 {
280     plugins_get_descriptions(print_plugin_description, NULL);
281 }
282
283 int
284 plugins_get_count(void)
285 {
286     guint count = 0;
287
288     for (GSList *l = plugins_module_list; l != NULL; l = l->next) {
289         count += g_hash_table_size((GHashTable *)l->data);
290     }
291     return count;
292 }
293
294 void
295 plugins_cleanup(plugins_t *plugins)
296 {
297     if (!plugins)
298         return;
299
300     plugins_module_list = g_slist_remove(plugins_module_list, plugins);
301     g_hash_table_destroy((GHashTable *)plugins);
302 }
303
304 /*
305  * Editor modelines
306  *
307  * Local Variables:
308  * c-basic-offset: 4
309  * tab-width: 8
310  * indent-tabs-mode: nil
311  * End:
312  *
313  * ex: set shiftwidth=4 tabstop=8 expandtab:
314  * :indentSize=4:tabSize=8:noTabs=true:
315  */