linux/tools/lib/traceevent/event-plugin.c
<<
>>
Prefs
   1/*
   2 * Copyright (C) 2009, 2010 Red Hat Inc, Steven Rostedt <srostedt@redhat.com>
   3 *
   4 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   5 * This program is free software; you can redistribute it and/or
   6 * modify it under the terms of the GNU Lesser General Public
   7 * License as published by the Free Software Foundation;
   8 * version 2.1 of the License (not later!)
   9 *
  10 * This program is distributed in the hope that it will be useful,
  11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13 * GNU Lesser General Public License for more details.
  14 *
  15 * You should have received a copy of the GNU Lesser General Public
  16 * License along with this program; if not,  see <http://www.gnu.org/licenses>
  17 *
  18 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  19 */
  20
  21#include <stdio.h>
  22#include <string.h>
  23#include <dlfcn.h>
  24#include <stdlib.h>
  25#include <sys/types.h>
  26#include <sys/stat.h>
  27#include <unistd.h>
  28#include <dirent.h>
  29#include "event-parse.h"
  30#include "event-utils.h"
  31
  32#define LOCAL_PLUGIN_DIR ".traceevent/plugins"
  33
  34static struct registered_plugin_options {
  35        struct registered_plugin_options        *next;
  36        struct pevent_plugin_option             *options;
  37} *registered_options;
  38
  39static struct trace_plugin_options {
  40        struct trace_plugin_options     *next;
  41        char                            *plugin;
  42        char                            *option;
  43        char                            *value;
  44} *trace_plugin_options;
  45
  46struct plugin_list {
  47        struct plugin_list      *next;
  48        char                    *name;
  49        void                    *handle;
  50};
  51
  52/**
  53 * traceevent_plugin_list_options - get list of plugin options
  54 *
  55 * Returns an array of char strings that list the currently registered
  56 * plugin options in the format of <plugin>:<option>. This list can be
  57 * used by toggling the option.
  58 *
  59 * Returns NULL if there's no options registered. On error it returns
  60 * INVALID_PLUGIN_LIST_OPTION
  61 *
  62 * Must be freed with traceevent_plugin_free_options_list().
  63 */
  64char **traceevent_plugin_list_options(void)
  65{
  66        struct registered_plugin_options *reg;
  67        struct pevent_plugin_option *op;
  68        char **list = NULL;
  69        char *name;
  70        int count = 0;
  71
  72        for (reg = registered_options; reg; reg = reg->next) {
  73                for (op = reg->options; op->name; op++) {
  74                        char *alias = op->plugin_alias ? op->plugin_alias : op->file;
  75                        char **temp = list;
  76
  77                        name = malloc(strlen(op->name) + strlen(alias) + 2);
  78                        if (!name)
  79                                goto err;
  80
  81                        sprintf(name, "%s:%s", alias, op->name);
  82                        list = realloc(list, count + 2);
  83                        if (!list) {
  84                                list = temp;
  85                                free(name);
  86                                goto err;
  87                        }
  88                        list[count++] = name;
  89                        list[count] = NULL;
  90                }
  91        }
  92        return list;
  93
  94 err:
  95        while (--count >= 0)
  96                free(list[count]);
  97        free(list);
  98
  99        return INVALID_PLUGIN_LIST_OPTION;
 100}
 101
 102void traceevent_plugin_free_options_list(char **list)
 103{
 104        int i;
 105
 106        if (!list)
 107                return;
 108
 109        if (list == INVALID_PLUGIN_LIST_OPTION)
 110                return;
 111
 112        for (i = 0; list[i]; i++)
 113                free(list[i]);
 114
 115        free(list);
 116}
 117
 118static int
 119update_option(const char *file, struct pevent_plugin_option *option)
 120{
 121        struct trace_plugin_options *op;
 122        char *plugin;
 123
 124        if (option->plugin_alias) {
 125                plugin = strdup(option->plugin_alias);
 126                if (!plugin)
 127                        return -1;
 128        } else {
 129                char *p;
 130                plugin = strdup(file);
 131                if (!plugin)
 132                        return -1;
 133                p = strstr(plugin, ".");
 134                if (p)
 135                        *p = '\0';
 136        }
 137
 138        /* first look for named options */
 139        for (op = trace_plugin_options; op; op = op->next) {
 140                if (!op->plugin)
 141                        continue;
 142                if (strcmp(op->plugin, plugin) != 0)
 143                        continue;
 144                if (strcmp(op->option, option->name) != 0)
 145                        continue;
 146
 147                option->value = op->value;
 148                option->set ^= 1;
 149                goto out;
 150        }
 151
 152        /* first look for unnamed options */
 153        for (op = trace_plugin_options; op; op = op->next) {
 154                if (op->plugin)
 155                        continue;
 156                if (strcmp(op->option, option->name) != 0)
 157                        continue;
 158
 159                option->value = op->value;
 160                option->set ^= 1;
 161                break;
 162        }
 163
 164 out:
 165        free(plugin);
 166        return 0;
 167}
 168
 169/**
 170 * traceevent_plugin_add_options - Add a set of options by a plugin
 171 * @name: The name of the plugin adding the options
 172 * @options: The set of options being loaded
 173 *
 174 * Sets the options with the values that have been added by user.
 175 */
 176int traceevent_plugin_add_options(const char *name,
 177                                  struct pevent_plugin_option *options)
 178{
 179        struct registered_plugin_options *reg;
 180
 181        reg = malloc(sizeof(*reg));
 182        if (!reg)
 183                return -1;
 184        reg->next = registered_options;
 185        reg->options = options;
 186        registered_options = reg;
 187
 188        while (options->name) {
 189                update_option(name, options);
 190                options++;
 191        }
 192        return 0;
 193}
 194
 195/**
 196 * traceevent_plugin_remove_options - remove plugin options that were registered
 197 * @options: Options to removed that were registered with traceevent_plugin_add_options
 198 */
 199void traceevent_plugin_remove_options(struct pevent_plugin_option *options)
 200{
 201        struct registered_plugin_options **last;
 202        struct registered_plugin_options *reg;
 203
 204        for (last = &registered_options; *last; last = &(*last)->next) {
 205                if ((*last)->options == options) {
 206                        reg = *last;
 207                        *last = reg->next;
 208                        free(reg);
 209                        return;
 210                }
 211        }
 212}
 213
 214/**
 215 * traceevent_print_plugins - print out the list of plugins loaded
 216 * @s: the trace_seq descripter to write to
 217 * @prefix: The prefix string to add before listing the option name
 218 * @suffix: The suffix string ot append after the option name
 219 * @list: The list of plugins (usually returned by traceevent_load_plugins()
 220 *
 221 * Writes to the trace_seq @s the list of plugins (files) that is
 222 * returned by traceevent_load_plugins(). Use @prefix and @suffix for formating:
 223 * @prefix = "  ", @suffix = "\n".
 224 */
 225void traceevent_print_plugins(struct trace_seq *s,
 226                              const char *prefix, const char *suffix,
 227                              const struct plugin_list *list)
 228{
 229        while (list) {
 230                trace_seq_printf(s, "%s%s%s", prefix, list->name, suffix);
 231                list = list->next;
 232        }
 233}
 234
 235static void
 236load_plugin(struct pevent *pevent, const char *path,
 237            const char *file, void *data)
 238{
 239        struct plugin_list **plugin_list = data;
 240        pevent_plugin_load_func func;
 241        struct plugin_list *list;
 242        const char *alias;
 243        char *plugin;
 244        void *handle;
 245
 246        plugin = malloc(strlen(path) + strlen(file) + 2);
 247        if (!plugin) {
 248                warning("could not allocate plugin memory\n");
 249                return;
 250        }
 251
 252        strcpy(plugin, path);
 253        strcat(plugin, "/");
 254        strcat(plugin, file);
 255
 256        handle = dlopen(plugin, RTLD_NOW | RTLD_GLOBAL);
 257        if (!handle) {
 258                warning("could not load plugin '%s'\n%s\n",
 259                        plugin, dlerror());
 260                goto out_free;
 261        }
 262
 263        alias = dlsym(handle, PEVENT_PLUGIN_ALIAS_NAME);
 264        if (!alias)
 265                alias = file;
 266
 267        func = dlsym(handle, PEVENT_PLUGIN_LOADER_NAME);
 268        if (!func) {
 269                warning("could not find func '%s' in plugin '%s'\n%s\n",
 270                        PEVENT_PLUGIN_LOADER_NAME, plugin, dlerror());
 271                goto out_free;
 272        }
 273
 274        list = malloc(sizeof(*list));
 275        if (!list) {
 276                warning("could not allocate plugin memory\n");
 277                goto out_free;
 278        }
 279
 280        list->next = *plugin_list;
 281        list->handle = handle;
 282        list->name = plugin;
 283        *plugin_list = list;
 284
 285        pr_stat("registering plugin: %s", plugin);
 286        func(pevent);
 287        return;
 288
 289 out_free:
 290        free(plugin);
 291}
 292
 293static void
 294load_plugins_dir(struct pevent *pevent, const char *suffix,
 295                 const char *path,
 296                 void (*load_plugin)(struct pevent *pevent,
 297                                     const char *path,
 298                                     const char *name,
 299                                     void *data),
 300                 void *data)
 301{
 302        struct dirent *dent;
 303        struct stat st;
 304        DIR *dir;
 305        int ret;
 306
 307        ret = stat(path, &st);
 308        if (ret < 0)
 309                return;
 310
 311        if (!S_ISDIR(st.st_mode))
 312                return;
 313
 314        dir = opendir(path);
 315        if (!dir)
 316                return;
 317
 318        while ((dent = readdir(dir))) {
 319                const char *name = dent->d_name;
 320
 321                if (strcmp(name, ".") == 0 ||
 322                    strcmp(name, "..") == 0)
 323                        continue;
 324
 325                /* Only load plugins that end in suffix */
 326                if (strcmp(name + (strlen(name) - strlen(suffix)), suffix) != 0)
 327                        continue;
 328
 329                load_plugin(pevent, path, name, data);
 330        }
 331
 332        closedir(dir);
 333}
 334
 335static void
 336load_plugins(struct pevent *pevent, const char *suffix,
 337             void (*load_plugin)(struct pevent *pevent,
 338                                 const char *path,
 339                                 const char *name,
 340                                 void *data),
 341             void *data)
 342{
 343        char *home;
 344        char *path;
 345        char *envdir;
 346
 347        if (pevent->flags & PEVENT_DISABLE_PLUGINS)
 348                return;
 349
 350        /*
 351         * If a system plugin directory was defined,
 352         * check that first.
 353         */
 354#ifdef PLUGIN_DIR
 355        if (!(pevent->flags & PEVENT_DISABLE_SYS_PLUGINS))
 356                load_plugins_dir(pevent, suffix, PLUGIN_DIR,
 357                                 load_plugin, data);
 358#endif
 359
 360        /*
 361         * Next let the environment-set plugin directory
 362         * override the system defaults.
 363         */
 364        envdir = getenv("TRACEEVENT_PLUGIN_DIR");
 365        if (envdir)
 366                load_plugins_dir(pevent, suffix, envdir, load_plugin, data);
 367
 368        /*
 369         * Now let the home directory override the environment
 370         * or system defaults.
 371         */
 372        home = getenv("HOME");
 373        if (!home)
 374                return;
 375
 376        path = malloc(strlen(home) + strlen(LOCAL_PLUGIN_DIR) + 2);
 377        if (!path) {
 378                warning("could not allocate plugin memory\n");
 379                return;
 380        }
 381
 382        strcpy(path, home);
 383        strcat(path, "/");
 384        strcat(path, LOCAL_PLUGIN_DIR);
 385
 386        load_plugins_dir(pevent, suffix, path, load_plugin, data);
 387
 388        free(path);
 389}
 390
 391struct plugin_list*
 392traceevent_load_plugins(struct pevent *pevent)
 393{
 394        struct plugin_list *list = NULL;
 395
 396        load_plugins(pevent, ".so", load_plugin, &list);
 397        return list;
 398}
 399
 400void
 401traceevent_unload_plugins(struct plugin_list *plugin_list, struct pevent *pevent)
 402{
 403        pevent_plugin_unload_func func;
 404        struct plugin_list *list;
 405
 406        while (plugin_list) {
 407                list = plugin_list;
 408                plugin_list = list->next;
 409                func = dlsym(list->handle, PEVENT_PLUGIN_UNLOADER_NAME);
 410                if (func)
 411                        func(pevent);
 412                dlclose(list->handle);
 413                free(list->name);
 414                free(list);
 415        }
 416}
 417