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