| // SPDX-License-Identifier: LGPL-2.1 |
| |
| /* |
| * Copyright (C) 2017 VMware Inc, Yordan Karadzhov <y.karadz@gmail.com> |
| */ |
| |
| /** |
| * @file libkshark-plugin.c |
| * @brief KernelShark plugins. |
| */ |
| |
| // C |
| #define _GNU_SOURCE |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <sys/stat.h> |
| #include <dlfcn.h> |
| #include <errno.h> |
| |
| // 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; |
| } |