aboutsummaryrefslogtreecommitdiff
path: root/lib/trace-cmd/trace-plugin.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/trace-cmd/trace-plugin.c')
-rw-r--r--lib/trace-cmd/trace-plugin.c314
1 files changed, 314 insertions, 0 deletions
diff --git a/lib/trace-cmd/trace-plugin.c b/lib/trace-cmd/trace-plugin.c
new file mode 100644
index 00000000..127771ea
--- /dev/null
+++ b/lib/trace-cmd/trace-plugin.c
@@ -0,0 +1,314 @@
+// SPDX-License-Identifier: LGPL-2.1
+/*
+ * Copyright (C) 2009, 2010 Red Hat Inc, Steven Rostedt <srostedt@redhat.com>
+ *
+ */
+#include <stdlib.h>
+#include <unistd.h>
+#include <dlfcn.h>
+#include <sys/stat.h>
+#include <libgen.h>
+#include "trace-cmd.h"
+#include "trace-local.h"
+#include "trace-cmd-local.h"
+
+#define LOCAL_PLUGIN_DIR ".local/lib/trace-cmd/plugins/"
+
+struct trace_plugin_list {
+ struct trace_plugin_list *next;
+ char *name;
+ void *handle;
+};
+
+struct trace_plugin_context {
+ enum tracecmd_context context;
+ enum tracecmd_plugin_flag flags;
+ union {
+ void *data;
+ struct tracecmd_input *trace_input;
+ struct tracecmd_output *trace_output;
+ };
+};
+
+/**
+ * tracecmd_plugin_context_create - Create and initialize tracecmd plugins context.
+ * @context: Context of the trace-cmd command.
+ * @data: Pointer to the context specific data, which will be passed to plugins.
+ *
+ * Returns a pointer to created tracecmd plugins context, or NULL in case memory
+ * allocation fails. The returned pointer should be freed by free ().
+ */
+struct trace_plugin_context *
+tracecmd_plugin_context_create(enum tracecmd_context context, void *data)
+{
+ struct trace_plugin_context *trace;
+
+ trace = calloc(1, sizeof(struct trace_plugin_context));
+ if (!trace)
+ return NULL;
+ trace->context = context;
+ trace->data = data;
+ return trace;
+}
+
+/**
+ * tracecmd_plugin_set_flag - Set a flag to tracecmd plugins context.
+ * @context: Context of the trace-cmd command.
+ * @flag: Flag, whil will be set.
+ *
+ */
+void tracecmd_plugin_set_flag(struct trace_plugin_context *context,
+ enum tracecmd_plugin_flag flag)
+{
+ if (context)
+ context->flags |= flag;
+}
+
+/**
+ * tracecmd_plugin_context_input - Get a tracecmd_input plugin context.
+ * @context: Context of the trace-cmd command.
+ *
+ * Returns pointer to tracecmd_input, if such context is available or
+ * NULL otherwise.
+ */
+struct tracecmd_input *
+tracecmd_plugin_context_input(struct trace_plugin_context *context)
+{
+ if (!context || context->context != TRACECMD_INPUT)
+ return NULL;
+ return context->trace_input;
+}
+
+/**
+ * tracecmd_plugin_context_output - Get a tracecmd_output plugin context
+ * @context: Context of the trace-cmd command.
+ *
+ * Returns pointer to tracecmd_output, if such context is available or
+ * NULL otherwise.
+ */
+struct tracecmd_output *
+tracecmd_plugin_context_output(struct trace_plugin_context *context)
+{
+ if (!context || context->context != TRACECMD_OUTPUT)
+ return NULL;
+ return context->trace_output;
+}
+
+static void
+load_plugin(struct trace_plugin_context *trace, const char *path,
+ const char *file, void *data)
+{
+ struct trace_plugin_list **plugin_list = data;
+ tracecmd_plugin_load_func func;
+ struct trace_plugin_list *list;
+ const char *alias;
+ char *plugin;
+ void *handle;
+ int ret;
+
+ ret = asprintf(&plugin, "%s/%s", path, file);
+ if (ret < 0) {
+ tracecmd_warning("could not allocate plugin memory");
+ return;
+ }
+
+ handle = dlopen(plugin, RTLD_NOW | RTLD_GLOBAL);
+ if (!handle) {
+ tracecmd_warning("could not load plugin '%s'\n%s", plugin, dlerror());
+ goto out_free;
+ }
+
+ alias = dlsym(handle, TRACECMD_PLUGIN_ALIAS_NAME);
+ if (!alias)
+ alias = file;
+
+ func = dlsym(handle, TRACECMD_PLUGIN_LOADER_NAME);
+ if (!func) {
+ tracecmd_warning("could not find func '%s' in plugin '%s'\n%s",
+ TRACECMD_PLUGIN_LOADER_NAME, plugin, dlerror());
+ goto out_free;
+ }
+
+ list = malloc(sizeof(*list));
+ if (!list) {
+ tracecmd_warning("could not allocate plugin memory");
+ goto out_free;
+ }
+
+ list->next = *plugin_list;
+ list->handle = handle;
+ list->name = plugin;
+ *plugin_list = list;
+
+ tracecmd_info("registering plugin: %s", plugin);
+ func(trace);
+ return;
+
+ out_free:
+ free(plugin);
+}
+
+static void
+load_plugins_dir(struct trace_plugin_context *trace, const char *suffix,
+ const char *path,
+ void (*load_plugin)(struct trace_plugin_context *trace,
+ const char *path,
+ const char *name,
+ void *data),
+ void *data)
+{
+ struct dirent *dent;
+ struct stat st;
+ DIR *dir;
+ int ret;
+
+ ret = stat(path, &st);
+ if (ret < 0)
+ return;
+
+ if (!S_ISDIR(st.st_mode))
+ return;
+
+ dir = opendir(path);
+ if (!dir)
+ return;
+
+ while ((dent = readdir(dir))) {
+ const char *name = dent->d_name;
+
+ if (strcmp(name, ".") == 0 ||
+ strcmp(name, "..") == 0)
+ continue;
+
+ /* Only load plugins that end in suffix */
+ if (strcmp(name + (strlen(name) - strlen(suffix)), suffix) != 0)
+ continue;
+
+ load_plugin(trace, path, name, data);
+ }
+
+ closedir(dir);
+}
+
+static char *get_source_plugins_dir(void)
+{
+ char *p, path[PATH_MAX+1];
+ int ret;
+
+ ret = readlink("/proc/self/exe", path, PATH_MAX);
+ if (ret > PATH_MAX || ret < 0)
+ return NULL;
+
+ path[ret] = 0;
+ dirname(path);
+ p = strrchr(path, '/');
+ if (!p)
+ return NULL;
+ /* Check if we are in the the source tree */
+ if (strcmp(p, "/tracecmd") != 0)
+ return NULL;
+
+ strcpy(p, "/lib/trace-cmd/plugins");
+ return strdup(path);
+}
+
+static void
+load_plugins_hook(struct trace_plugin_context *trace, const char *suffix,
+ void (*load_plugin)(struct trace_plugin_context *trace,
+ const char *path,
+ const char *name,
+ void *data),
+ void *data)
+{
+ char *home;
+ char *path;
+ char *envdir;
+ int ret;
+
+ if (trace && trace->flags & TRACECMD_DISABLE_PLUGINS)
+ return;
+
+ /*
+ * If a system plugin directory was defined,
+ * check that first.
+ */
+#ifdef PLUGIN_TRACECMD_DIR
+ if (!trace || !(trace->flags & TRACECMD_DISABLE_SYS_PLUGINS))
+ load_plugins_dir(trace, suffix, PLUGIN_TRACECMD_DIR,
+ load_plugin, data);
+#endif
+
+ /*
+ * Next let the environment-set plugin directory
+ * override the system defaults.
+ */
+ envdir = getenv("TRACECMD_PLUGIN_DIR");
+ if (envdir)
+ load_plugins_dir(trace, suffix, envdir, load_plugin, data);
+
+ /*
+ * Now let the home directory override the environment
+ * or system defaults.
+ */
+ home = getenv("HOME");
+ if (!home)
+ return;
+
+ ret = asprintf(&path, "%s/%s", home, LOCAL_PLUGIN_DIR);
+ if (ret < 0) {
+ tracecmd_warning("could not allocate plugin memory");
+ return;
+ }
+
+ load_plugins_dir(trace, suffix, path, load_plugin, data);
+
+ free(path);
+
+ path = get_source_plugins_dir();
+ if (path) {
+ load_plugins_dir(trace, suffix, path, load_plugin, data);
+ free(path);
+ }
+}
+
+/**
+ * tracecmd_load_plugins - Load trace-cmd specific plugins.
+ * @context: Context of the trace-cmd command, will be passed to the plugins
+ * at load time.
+ *
+ * Returns a list of loaded plugins
+ */
+struct trace_plugin_list*
+tracecmd_load_plugins(struct trace_plugin_context *trace)
+{
+ struct trace_plugin_list *list = NULL;
+
+ load_plugins_hook(trace, ".so", load_plugin, &list);
+ return list;
+}
+
+/**
+ * tracecmd_unload_plugins - Unload trace-cmd specific plugins.
+ * @plugin_list - List of plugins, previously loaded with tracecmd_load_plugins.
+ * @context: Context of the trace-cmd command, will be passed to the plugins
+ * at unload time.
+ *
+ */
+void
+tracecmd_unload_plugins(struct trace_plugin_list *plugin_list,
+ struct trace_plugin_context *trace)
+{
+ tracecmd_plugin_unload_func func;
+ struct trace_plugin_list *list;
+
+ while (plugin_list) {
+ list = plugin_list;
+ plugin_list = list->next;
+ func = dlsym(list->handle, TRACECMD_PLUGIN_UNLOADER_NAME);
+ if (func)
+ func(trace);
+ dlclose(list->handle);
+ free(list->name);
+ free(list);
+ }
+}