diff options
Diffstat (limited to 'wsutil/plugins.c')
-rw-r--r-- | wsutil/plugins.c | 415 |
1 files changed, 415 insertions, 0 deletions
diff --git a/wsutil/plugins.c b/wsutil/plugins.c new file mode 100644 index 0000000000..ebd337ca43 --- /dev/null +++ b/wsutil/plugins.c @@ -0,0 +1,415 @@ +/* plugins.c + * plugin routines + * + * $Id$ + * + * Wireshark - Network traffic analyzer + * By Gerald Combs <gerald@wireshark.org> + * Copyright 1998 Gerald Combs + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "config.h" + +#ifdef HAVE_PLUGINS + +#include <time.h> + +#ifdef HAVE_DIRENT_H +#include <dirent.h> +#endif + +#ifdef HAVE_DIRECT_H +#include <direct.h> +#endif + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> + +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif + +#include <glib.h> +#include <gmodule.h> + +#include <wsutil/filesystem.h> +#include <wsutil/privileges.h> +#include <wsutil/file_util.h> +#include <wsutil/report_err.h> + +#include <wsutil/plugins.h> + +/* linked list of all plugins */ +typedef struct _plugin { + GModule *handle; /* handle returned by g_module_open */ + gchar *name; /* plugin name */ + gchar *version; /* plugin version */ + guint32 types; /* bitmask of plugin types this plugin supports */ + struct _plugin *next; /* forward link */ +} plugin; + +static plugin *plugin_list = NULL; + +/* + * Add a new plugin type. + * Takes a callback routine as an argument; it is called for each plugin + * we find, and handed a handle for the plugin, the name of the plugin, + * and the version string for the plugin. The plugin returns TRUE if + * it's a plugin for that type and FALSE if not. + */ +typedef struct { + const char *type; + plugin_callback callback; + guint type_val; +} plugin_type; + +static GSList *plugin_types = NULL; + +void +add_plugin_type(const char *type, plugin_callback callback) +{ + plugin_type *new_type; + static guint type_val; + + if (type_val >= 32) { + /* + * There's a bitmask of types that a plugin provides, and it's + * 32 bits, so we don't support types > 31. + */ + report_failure("At most 32 plugin types can be supported, so the plugin type '%s' won't be supported.", + type); + return; + } + new_type = (plugin_type *)g_malloc(sizeof (plugin_type)); + new_type->type = type; + new_type->callback = callback; + new_type->type_val = type_val; + plugin_types = g_slist_append(plugin_types, new_type); + type_val++; +} + +/* + * add a new plugin to the list + * returns : + * - 0 : OK + * - ENOMEM : memory allocation problem + * - EEXIST : the same plugin (i.e. name/version) was already registered. + */ +static int +add_plugin(plugin *new_plug) +{ + plugin *pt_plug; + + pt_plug = plugin_list; + if (!pt_plug) /* the list is empty */ + { + plugin_list = new_plug; + } + else + { + while (1) + { + /* check if the same name/version is already registered */ + if (strcmp(pt_plug->name, new_plug->name) == 0 && + strcmp(pt_plug->version, new_plug->version) == 0) + { + return EEXIST; + } + + /* we found the last plugin in the list */ + if (pt_plug->next == NULL) + break; + + pt_plug = pt_plug->next; + } + pt_plug->next = new_plug; + } + + return 0; +} + +static void +call_plugin_callback(gpointer data, gpointer user_data) +{ + plugin_type *type = (plugin_type *)data; + plugin *new_plug = (plugin *)user_data; + + if ((*type->callback)(new_plug->handle)) { + /* The plugin supports this type */ + new_plug->types |= 1 << type->type_val; + } +} + +static void +plugins_scan_dir(const char *dirname) +{ +#define FILENAME_LEN 1024 + WS_DIR *dir; /* scanned directory */ + WS_DIRENT *file; /* current file */ + const char *name; + gchar filename[FILENAME_LEN]; /* current file name */ + GModule *handle; /* handle returned by g_module_open */ + gpointer gp; + plugin *new_plug; + gchar *dot; + int cr; + + if ((dir = ws_dir_open(dirname, 0, NULL)) != NULL) + { + while ((file = ws_dir_read_name(dir)) != NULL) + { + name = ws_dir_get_name(file); + + /* + * GLib 2.x defines G_MODULE_SUFFIX as the extension used on + * this platform for loadable modules. + */ + /* skip anything but files with G_MODULE_SUFFIX */ + dot = strrchr(name, '.'); + if (dot == NULL || strcmp(dot+1, G_MODULE_SUFFIX) != 0) + continue; + + g_snprintf(filename, FILENAME_LEN, "%s" G_DIR_SEPARATOR_S "%s", + dirname, name); + if ((handle = g_module_open(filename, (GModuleFlags)0)) == NULL) + { + report_failure("Couldn't load module %s: %s", filename, + g_module_error()); + continue; + } + + if (!g_module_symbol(handle, "version", &gp)) + { + report_failure("The plugin %s has no version symbol", name); + g_module_close(handle); + continue; + } + + new_plug = (plugin *)g_malloc(sizeof(plugin)); + new_plug->handle = handle; + new_plug->name = g_strdup(name); + new_plug->version = (char *)gp; + new_plug->types = 0; + new_plug->next = NULL; + + /* + * Hand the plugin to each of the plugin type callbacks. + */ + g_slist_foreach(plugin_types, call_plugin_callback, new_plug); + + /* + * Does this dissector do anything useful? + */ + if (new_plug->types == 0) + { + /* + * No. + */ + report_failure("The plugin '%s' has no registration routines", + name); + g_module_close(handle); + g_free(new_plug->name); + g_free(new_plug); + continue; + } + + /* + * OK, attempt to add it to the list of plugins. + */ + if ((cr = add_plugin(new_plug))) + { + if (cr == EEXIST) + fprintf(stderr, "The plugin %s, version %s\n" + "was found in multiple directories\n", + new_plug->name, new_plug->version); + else + fprintf(stderr, "Memory allocation problem\n" + "when processing plugin %s, version %s\n", + new_plug->name, new_plug->version); + g_module_close(handle); + g_free(new_plug->name); + g_free(new_plug); + continue; + } + + } + ws_dir_close(dir); + } +} + + +/* + * Scan for plugins. + */ +void +scan_plugins(void) +{ + const char *plugin_dir; + const char *name; + char *plugin_dir_path; + char *plugins_pers_dir; + WS_DIR *dir; /* scanned directory */ + WS_DIRENT *file; /* current file */ + + if (plugin_list == NULL) /* ensure scan_plugins is only run once */ + { + /* + * Scan the global plugin directory. + * If we're running from a build directory, scan the subdirectories + * of that directory, as the global plugin directory is the + * "plugins" directory of the source tree, and the subdirectories + * are the source directories for the plugins, with the plugins + * built in those subdirectories. + */ + plugin_dir = get_plugin_dir(); + if (running_in_build_directory()) + { + if ((dir = ws_dir_open(plugin_dir, 0, NULL)) != NULL) + { + while ((file = ws_dir_read_name(dir)) != NULL) + { + name = ws_dir_get_name(file); + if (strcmp(name, ".") == 0 || strcmp(name, "..") == 0) + continue; /* skip "." and ".." */ + /* + * Get the full path of a ".libs" subdirectory of that + * directory. + */ + plugin_dir_path = g_strdup_printf( + "%s" G_DIR_SEPARATOR_S "%s" G_DIR_SEPARATOR_S ".libs", + plugin_dir, name); + if (test_for_directory(plugin_dir_path) != EISDIR) { + /* + * Either it doesn't refer to a directory or it + * refers to something that doesn't exist. + * + * Assume that means that the plugins are in + * the subdirectory of the plugin directory, not + * a ".libs" subdirectory of that subdirectory. + */ + g_free(plugin_dir_path); + plugin_dir_path = g_strdup_printf("%s" G_DIR_SEPARATOR_S "%s", + plugin_dir, name); + } + plugins_scan_dir(plugin_dir_path); + g_free(plugin_dir_path); + } + ws_dir_close(dir); + } + } + else + plugins_scan_dir(plugin_dir); + + /* + * If the program wasn't started with special privileges, + * scan the users plugin directory. (Even if we relinquish + * them, plugins aren't safe unless we've *permanently* + * relinquished them, and we can't do that in Wireshark as, + * if we need privileges to start capturing, we'd need to + * reclaim them before each time we start capturing.) + */ + if (!started_with_special_privs()) + { + plugins_pers_dir = get_plugins_pers_dir(); + plugins_scan_dir(plugins_pers_dir); + g_free(plugins_pers_dir); + } + } +} + +/* + * Iterate over all plugins, calling a callback with information about + * the plugin. + */ +typedef struct { + plugin *pt_plug; + GString *types; + const char *sep; +} type_callback_info; + +static void +add_plugin_type_description(gpointer data, gpointer user_data) +{ + plugin_type *type = (plugin_type *)data; + type_callback_info *info = (type_callback_info *)user_data; + + /* + * If the plugin handles this type, add the type to the list of types. + */ + if (info->pt_plug->types & (1 << type->type_val)) { + g_string_append_printf(info->types, "%s%s", info->sep, type->type); + info->sep = ", "; + } +} + +WS_DLL_PUBLIC void +plugins_get_descriptions(plugin_description_callback callback, void *user_data) +{ + type_callback_info info; + + info.types = NULL; /* FUCK LLVM UP THE ASS WITH A RED HOT POKER */ + for (info.pt_plug = plugin_list; info.pt_plug != NULL; + info.pt_plug = info.pt_plug->next) + { + info.sep = ""; + info.types = g_string_new(""); + + /* + * Build a list of all the plugin types. + */ + g_slist_foreach(plugin_types, add_plugin_type_description, &info); + + /* + * And hand the information to the callback. + */ + callback(info.pt_plug->name, info.pt_plug->version, info.types->str, + g_module_name(info.pt_plug->handle), user_data); + + g_string_free(info.types, TRUE); + } +} + +static void +print_plugin_description(const char *name, const char *version, + const char *description, const char *filename, + void *user_data _U_) +{ + printf("%s\t%s\t%s\t%s\n", name, version, description, filename); +} + +void +plugins_dump_all(void) +{ + plugins_get_descriptions(print_plugin_description, NULL); +} + +#endif /* HAVE_PLUGINS */ + +/* + * Editor modelines + * + * Local Variables: + * c-basic-offset: 4 + * tab-width: 8 + * indent-tabs-mode: nil + * End: + * + * ex: set shiftwidth=4 tabstop=8 expandtab: + * :indentSize=4:tabSize=8:noTabs=true: + */ |