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
 124                        name = malloc(strlen(op->name) + strlen(alias) + 2);
 125                        if (!name)
 126                                goto err;
 127
 128                        sprintf(name, "%s:%s", alias, op->name);
 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
 294        plugin = malloc(strlen(path) + strlen(file) + 2);
 295        if (!plugin) {
 296                warning("could not allocate plugin memory\n");
 297                return;
 298        }
 299
 300        strcpy(plugin, path);
 301        strcat(plugin, "/");
 302        strcat(plugin, file);
 303
 304        handle = dlopen(plugin, RTLD_NOW | RTLD_GLOBAL);
 305        if (!handle) {
 306                warning("could not load plugin '%s'\n%s\n",
 307                        plugin, dlerror());
 308                goto out_free;
 309        }
 310
 311        alias = dlsym(handle, PEVENT_PLUGIN_ALIAS_NAME);
 312        if (!alias)
 313                alias = file;
 314
 315        func = dlsym(handle, PEVENT_PLUGIN_LOADER_NAME);
 316        if (!func) {
 317                warning("could not find func '%s' in plugin '%s'\n%s\n",
 318                        PEVENT_PLUGIN_LOADER_NAME, plugin, dlerror());
 319                goto out_free;
 320        }
 321
 322        list = malloc(sizeof(*list));
 323        if (!list) {
 324                warning("could not allocate plugin memory\n");
 325                goto out_free;
 326        }
 327
 328        list->next = *plugin_list;
 329        list->handle = handle;
 330        list->name = plugin;
 331        *plugin_list = list;
 332
 333        pr_stat("registering plugin: %s", plugin);
 334        func(pevent);
 335        return;
 336
 337 out_free:
 338        free(plugin);
 339}
 340
 341static void
 342load_plugins_dir(struct pevent *pevent, const char *suffix,
 343                 const char *path,
 344                 void (*load_plugin)(struct pevent *pevent,
 345                                     const char *path,
 346                                     const char *name,
 347                                     void *data),
 348                 void *data)
 349{
 350        struct dirent *dent;
 351        struct stat st;
 352        DIR *dir;
 353        int ret;
 354
 355        ret = stat(path, &st);
 356        if (ret < 0)
 357                return;
 358
 359        if (!S_ISDIR(st.st_mode))
 360                return;
 361
 362        dir = opendir(path);
 363        if (!dir)
 364                return;
 365
 366        while ((dent = readdir(dir))) {
 367                const char *name = dent->d_name;
 368
 369                if (strcmp(name, ".") == 0 ||
 370                    strcmp(name, "..") == 0)
 371                        continue;
 372
 373                /* Only load plugins that end in suffix */
 374                if (strcmp(name + (strlen(name) - strlen(suffix)), suffix) != 0)
 375                        continue;
 376
 377                load_plugin(pevent, path, name, data);
 378        }
 379
 380        closedir(dir);
 381}
 382
 383static void
 384load_plugins(struct pevent *pevent, const char *suffix,
 385             void (*load_plugin)(struct pevent *pevent,
 386                                 const char *path,
 387                                 const char *name,
 388                                 void *data),
 389             void *data)
 390{
 391        char *home;
 392        char *path;
 393        char *envdir;
 394
 395        if (pevent->flags & PEVENT_DISABLE_PLUGINS)
 396                return;
 397
 398        /*
 399         * If a system plugin directory was defined,
 400         * check that first.
 401         */
 402#ifdef PLUGIN_DIR
 403        if (!(pevent->flags & PEVENT_DISABLE_SYS_PLUGINS))
 404                load_plugins_dir(pevent, suffix, PLUGIN_DIR,
 405                                 load_plugin, data);
 406#endif
 407
 408        /*
 409         * Next let the environment-set plugin directory
 410         * override the system defaults.
 411         */
 412        envdir = getenv("TRACEEVENT_PLUGIN_DIR");
 413        if (envdir)
 414                load_plugins_dir(pevent, suffix, envdir, load_plugin, data);
 415
 416        /*
 417         * Now let the home directory override the environment
 418         * or system defaults.
 419         */
 420        home = getenv("HOME");
 421        if (!home)
 422                return;
 423
 424        path = malloc(strlen(home) + strlen(LOCAL_PLUGIN_DIR) + 2);
 425        if (!path) {
 426                warning("could not allocate plugin memory\n");
 427                return;
 428        }
 429
 430        strcpy(path, home);
 431        strcat(path, "/");
 432        strcat(path, LOCAL_PLUGIN_DIR);
 433
 434        load_plugins_dir(pevent, suffix, path, load_plugin, data);
 435
 436        free(path);
 437}
 438
 439struct plugin_list*
 440traceevent_load_plugins(struct pevent *pevent)
 441{
 442        struct plugin_list *list = NULL;
 443
 444        load_plugins(pevent, ".so", load_plugin, &list);
 445        return list;
 446}
 447
 448void
 449traceevent_unload_plugins(struct plugin_list *plugin_list, struct pevent *pevent)
 450{
 451        pevent_plugin_unload_func func;
 452        struct plugin_list *list;
 453
 454        while (plugin_list) {
 455                list = plugin_list;
 456                plugin_list = list->next;
 457                func = dlsym(list->handle, PEVENT_PLUGIN_UNLOADER_NAME);
 458                if (func)
 459                        func(pevent);
 460                dlclose(list->handle);
 461                free(list->name);
 462                free(list);
 463        }
 464}
 465