// SPDX-License-Identifier: LGPL-2.1 /* * Copyright (C) 2017 VMware Inc, Yordan Karadzhov */ /** * @file libkshark-plugin.c * @brief KernelShark plugins. */ // C #ifndef _GNU_SOURCE /** Use GNU C Library. */ #define _GNU_SOURCE #endif #include #include #include #include #include #include // KernelShark #include "libkshark-plugin.h" #include "libkshark.h" static struct kshark_event_handler * gui_event_handler_alloc(int event_id, kshark_plugin_event_handler_func evt_func, kshark_plugin_draw_handler_func dw_func) { struct kshark_event_handler *handler = malloc(sizeof(*handler)); if (!handler) { fprintf(stderr, "failed to allocate memory for gui eventhandler"); return NULL; } handler->next = NULL; handler->id = event_id; handler->event_func = evt_func; handler->draw_func = dw_func; return handler; } /** * @brief Search the list of event handlers for a handle associated with a * given event type. * * @param handlers: Input location for the Event handler list. * @param event_id: Event Id to search for. */ struct kshark_event_handler * kshark_find_event_handler(struct kshark_event_handler *handlers, int event_id) { for (; handlers; handlers = handlers->next) if (handlers->id == event_id) return handlers; return NULL; } /** * @brief Add new event handler to an existing list of handlers. * * @param handlers: Input location for the Event handler list. * @param event_id: Event Id. * @param evt_func: Input location for an Event action provided by the plugin. * @param dw_func: Input location for a Draw action provided by the plugin. * * @returns Zero on success, or a negative error code on failure. */ int kshark_register_event_handler(struct kshark_event_handler **handlers, int event_id, kshark_plugin_event_handler_func evt_func, kshark_plugin_draw_handler_func dw_func) { struct kshark_event_handler *handler = gui_event_handler_alloc(event_id, evt_func, dw_func); if(!handler) return -ENOMEM; handler->next = *handlers; *handlers = handler; return 0; } /** * @brief Search the list for a specific plugin handle. If such a plugin handle * exists, unregister (remove and free) this handle from the list. * * @param handlers: Input location for the Event handler list. * @param event_id: Event Id of the plugin handler to be unregistered. * @param evt_func: Event action function of the handler to be unregistered. * @param dw_func: Draw action function of the handler to be unregistered. */ void kshark_unregister_event_handler(struct kshark_event_handler **handlers, int event_id, kshark_plugin_event_handler_func evt_func, kshark_plugin_draw_handler_func dw_func) { struct kshark_event_handler **last; for (last = handlers; *last; last = &(*last)->next) { if ((*last)->id == event_id && (*last)->event_func == evt_func && (*last)->draw_func == dw_func) { struct kshark_event_handler *this_handler; this_handler = *last; *last = this_handler->next; free(this_handler); return; } } } /** * @brief Free all Event handlers in a given list. * * @param handlers: Input location for the Event handler list. */ void kshark_free_event_handler_list(struct kshark_event_handler *handlers) { struct kshark_event_handler *last; while (handlers) { last = handlers; handlers = handlers->next; free(last); } } /** * @brief Allocate memory for a new plugin. Add this plugin to the list of * plugins used by the session. * * @param kshark_ctx: Input location for the session context pointer. * @param file: The plugin object file to load. * * @returns Zero on success, or a negative error code on failure. */ int kshark_register_plugin(struct kshark_context *kshark_ctx, const char *file) { struct kshark_plugin_list *plugin = kshark_ctx->plugins; struct stat st; int ret; while (plugin) { if (strcmp(plugin->file, file) == 0) return -EEXIST; plugin = plugin->next; } ret = stat(file, &st); if (ret < 0) { fprintf(stderr, "plugin %s not found\n", file); return -ENODEV; } plugin = calloc(sizeof(struct kshark_plugin_list), 1); if (!plugin) { fprintf(stderr, "failed to allocate memory for plugin\n"); return -ENOMEM; } if (asprintf(&plugin->file, "%s", file) <= 0) { fprintf(stderr, "failed to allocate memory for plugin file name"); return -ENOMEM; } plugin->handle = dlopen(plugin->file, RTLD_NOW | RTLD_GLOBAL); if (!plugin->handle) goto fail; plugin->init = dlsym(plugin->handle, KSHARK_PLUGIN_INITIALIZER_NAME); plugin->close = dlsym(plugin->handle, KSHARK_PLUGIN_DEINITIALIZER_NAME); if (!plugin->init || !plugin->close) goto fail; plugin->next = kshark_ctx->plugins; kshark_ctx->plugins = plugin; return 0; fail: fprintf(stderr, "cannot load plugin '%s'\n%s\n", plugin->file, dlerror()); if (plugin->handle) { dlclose(plugin->handle); plugin->handle = NULL; } free(plugin); return EFAULT; } /** * @brief Unrgister a plugin. * * @param kshark_ctx: Input location for context pointer. * @param file: The plugin object file to unregister. */ void kshark_unregister_plugin(struct kshark_context *kshark_ctx, const char *file) { struct kshark_plugin_list **last; for (last = &kshark_ctx->plugins; *last; last = &(*last)->next) { if (strcmp((*last)->file, file) == 0) { struct kshark_plugin_list *this_plugin; this_plugin = *last; *last = this_plugin->next; dlclose(this_plugin->handle); free(this_plugin); return; } } } /** * @brief Free all plugins in a given list. * * @param plugins: Input location for the plugins list. */ void kshark_free_plugin_list(struct kshark_plugin_list *plugins) { struct kshark_plugin_list *last; while (plugins) { last = plugins; plugins = plugins->next; free(last->file); dlclose(last->handle); free(last); } } /** * @brief Use this function to initialize/update/deinitialize all registered * plugins. * * @param kshark_ctx: Input location for context pointer. * @param task_id: Action identifier specifying the action to be executed. * * @returns The number of successful added/removed plugin handlers on success, * or a negative error code on failure. */ int kshark_handle_plugins(struct kshark_context *kshark_ctx, enum kshark_plugin_actions task_id) { struct kshark_plugin_list *plugin; int handler_count = 0; for (plugin = kshark_ctx->plugins; plugin; plugin = plugin->next) { switch (task_id) { case KSHARK_PLUGIN_INIT: handler_count += plugin->init(kshark_ctx); break; case KSHARK_PLUGIN_UPDATE: plugin->close(kshark_ctx); handler_count += plugin->init(kshark_ctx); break; case KSHARK_PLUGIN_CLOSE: handler_count += plugin->close(kshark_ctx); break; default: return -EINVAL; } } return handler_count; }