linux/tools/lib/traceevent/event-plugin.c
<<
>>
Prefs
   1// SPDX-License-Identifier: LGPL-2.1
   2/*
   3 * Copyright (C) 2009, 2010 Red Hat Inc, Steven Rostedt <srostedt@redhat.com>
   4 *
   5 */
   6
   7#include <ctype.h>
   8#include <stdio.h>
   9#include <string.h>
  10#include <dlfcn.h>
  11#include <stdlib.h>
  12#include <sys/types.h>
  13#include <sys/stat.h>
  14#include <unistd.h>
  15#include <dirent.h>
  16#include <errno.h>
  17#include "event-parse.h"
  18#include "event-parse-local.h"
  19#include "event-utils.h"
  20#include "trace-seq.h"
  21
  22#define LOCAL_PLUGIN_DIR ".local/lib/traceevent/plugins/"
  23
  24static struct registered_plugin_options {
  25        struct registered_plugin_options        *next;
  26        struct tep_plugin_option                *options;
  27} *registered_options;
  28
  29static struct trace_plugin_options {
  30        struct trace_plugin_options     *next;
  31        char                            *plugin;
  32        char                            *option;
  33        char                            *value;
  34} *trace_plugin_options;
  35
  36struct tep_plugin_list {
  37        struct tep_plugin_list  *next;
  38        char                    *name;
  39        void                    *handle;
  40};
  41
  42struct tep_plugins_dir {
  43        struct tep_plugins_dir          *next;
  44        char                            *path;
  45        enum tep_plugin_load_priority   prio;
  46};
  47
  48static void lower_case(char *str)
  49{
  50        if (!str)
  51                return;
  52        for (; *str; str++)
  53                *str = tolower(*str);
  54}
  55
  56static int update_option_value(struct tep_plugin_option *op, const char *val)
  57{
  58        char *op_val;
  59
  60        if (!val) {
  61                /* toggle, only if option is boolean */
  62                if (op->value)
  63                        /* Warn? */
  64                        return 0;
  65                op->set ^= 1;
  66                return 0;
  67        }
  68
  69        /*
  70         * If the option has a value then it takes a string
  71         * otherwise the option is a boolean.
  72         */
  73        if (op->value) {
  74                op->value = val;
  75                return 0;
  76        }
  77
  78        /* Option is boolean, must be either "1", "0", "true" or "false" */
  79
  80        op_val = strdup(val);
  81        if (!op_val)
  82                return -1;
  83        lower_case(op_val);
  84
  85        if (strcmp(val, "1") == 0 || strcmp(val, "true") == 0)
  86                op->set = 1;
  87        else if (strcmp(val, "0") == 0 || strcmp(val, "false") == 0)
  88                op->set = 0;
  89        free(op_val);
  90
  91        return 0;
  92}
  93
  94/**
  95 * tep_plugin_list_options - get list of plugin options
  96 *
  97 * Returns an array of char strings that list the currently registered
  98 * plugin options in the format of <plugin>:<option>. This list can be
  99 * used by toggling the option.
 100 *
 101 * Returns NULL if there's no options registered. On error it returns
 102 * INVALID_PLUGIN_LIST_OPTION
 103 *
 104 * Must be freed with tep_plugin_free_options_list().
 105 */
 106char **tep_plugin_list_options(void)
 107{
 108        struct registered_plugin_options *reg;
 109        struct tep_plugin_option *op;
 110        char **list = NULL;
 111        char *name;
 112        int count = 0;
 113
 114        for (reg = registered_options; reg; reg = reg->next) {
 115                for (op = reg->options; op->name; op++) {
 116                        char *alias = op->plugin_alias ? op->plugin_alias : op->file;
 117                        char **temp = list;
 118                        int ret;
 119
 120                        ret = asprintf(&name, "%s:%s", alias, op->name);
 121                        if (ret < 0)
 122                                goto err;
 123
 124                        list = realloc(list, count + 2);
 125                        if (!list) {
 126                                list = temp;
 127                                free(name);
 128                                goto err;
 129                        }
 130                        list[count++] = name;
 131                        list[count] = NULL;
 132                }
 133        }
 134        return list;
 135
 136 err:
 137        while (--count >= 0)
 138                free(list[count]);
 139        free(list);
 140
 141        return INVALID_PLUGIN_LIST_OPTION;
 142}
 143
 144void tep_plugin_free_options_list(char **list)
 145{
 146        int i;
 147
 148        if (!list)
 149                return;
 150
 151        if (list == INVALID_PLUGIN_LIST_OPTION)
 152                return;
 153
 154        for (i = 0; list[i]; i++)
 155                free(list[i]);
 156
 157        free(list);
 158}
 159
 160static int
 161update_option(const char *file, struct tep_plugin_option *option)
 162{
 163        struct trace_plugin_options *op;
 164        char *plugin;
 165        int ret = 0;
 166
 167        if (option->plugin_alias) {
 168                plugin = strdup(option->plugin_alias);
 169                if (!plugin)
 170                        return -1;
 171        } else {
 172                char *p;
 173                plugin = strdup(file);
 174                if (!plugin)
 175                        return -1;
 176                p = strstr(plugin, ".");
 177                if (p)
 178                        *p = '\0';
 179        }
 180
 181        /* first look for named options */
 182        for (op = trace_plugin_options; op; op = op->next) {
 183                if (!op->plugin)
 184                        continue;
 185                if (strcmp(op->plugin, plugin) != 0)
 186                        continue;
 187                if (strcmp(op->option, option->name) != 0)
 188                        continue;
 189
 190                ret = update_option_value(option, op->value);
 191                if (ret)
 192                        goto out;
 193                break;
 194        }
 195
 196        /* first look for unnamed options */
 197        for (op = trace_plugin_options; op; op = op->next) {
 198                if (op->plugin)
 199                        continue;
 200                if (strcmp(op->option, option->name) != 0)
 201                        continue;
 202
 203                ret = update_option_value(option, op->value);
 204                break;
 205        }
 206
 207 out:
 208        free(plugin);
 209        return ret;
 210}
 211
 212/**
 213 * tep_plugin_add_options - Add a set of options by a plugin
 214 * @name: The name of the plugin adding the options
 215 * @options: The set of options being loaded
 216 *
 217 * Sets the options with the values that have been added by user.
 218 */
 219int tep_plugin_add_options(const char *name,
 220                           struct tep_plugin_option *options)
 221{
 222        struct registered_plugin_options *reg;
 223
 224        reg = malloc(sizeof(*reg));
 225        if (!reg)
 226                return -1;
 227        reg->next = registered_options;
 228        reg->options = options;
 229        registered_options = reg;
 230
 231        while (options->name) {
 232                update_option(name, options);
 233                options++;
 234        }
 235        return 0;
 236}
 237
 238/**
 239 * tep_plugin_remove_options - remove plugin options that were registered
 240 * @options: Options to removed that were registered with tep_plugin_add_options
 241 */
 242void tep_plugin_remove_options(struct tep_plugin_option *options)
 243{
 244        struct registered_plugin_options **last;
 245        struct registered_plugin_options *reg;
 246
 247        for (last = &registered_options; *last; last = &(*last)->next) {
 248                if ((*last)->options == options) {
 249                        reg = *last;
 250                        *last = reg->next;
 251                        free(reg);
 252                        return;
 253                }
 254        }
 255}
 256
 257static int parse_option_name(char **option, char **plugin)
 258{
 259        char *p;
 260
 261        *plugin = NULL;
 262
 263        if ((p = strstr(*option, ":"))) {
 264                *plugin = *option;
 265                *p = '\0';
 266                *option = strdup(p + 1);
 267                if (!*option)
 268                        return -1;
 269        }
 270        return 0;
 271}
 272
 273static struct tep_plugin_option *
 274find_registered_option(const char *plugin, const char *option)
 275{
 276        struct registered_plugin_options *reg;
 277        struct tep_plugin_option *op;
 278        const char *op_plugin;
 279
 280        for (reg = registered_options; reg; reg = reg->next) {
 281                for (op = reg->options; op->name; op++) {
 282                        if (op->plugin_alias)
 283                                op_plugin = op->plugin_alias;
 284                        else
 285                                op_plugin = op->file;
 286
 287                        if (plugin && strcmp(plugin, op_plugin) != 0)
 288                                continue;
 289                        if (strcmp(option, op->name) != 0)
 290                                continue;
 291
 292                        return op;
 293                }
 294        }
 295
 296        return NULL;
 297}
 298
 299static int process_option(const char *plugin, const char *option, const char *val)
 300{
 301        struct tep_plugin_option *op;
 302
 303        op = find_registered_option(plugin, option);
 304        if (!op)
 305                return 0;
 306
 307        return update_option_value(op, val);
 308}
 309
 310/**
 311 * tep_plugin_add_option - add an option/val pair to set plugin options
 312 * @name: The name of the option (format: <plugin>:<option> or just <option>)
 313 * @val: (optional) the value for the option
 314 *
 315 * Modify a plugin option. If @val is given than the value of the option
 316 * is set (note, some options just take a boolean, so @val must be either
 317 * "1" or "0" or "true" or "false").
 318 */
 319int tep_plugin_add_option(const char *name, const char *val)
 320{
 321        struct trace_plugin_options *op;
 322        char *option_str;
 323        char *plugin;
 324
 325        option_str = strdup(name);
 326        if (!option_str)
 327                return -ENOMEM;
 328
 329        if (parse_option_name(&option_str, &plugin) < 0)
 330                return -ENOMEM;
 331
 332        /* If the option exists, update the val */
 333        for (op = trace_plugin_options; op; op = op->next) {
 334                /* Both must be NULL or not NULL */
 335                if ((!plugin || !op->plugin) && plugin != op->plugin)
 336                        continue;
 337                if (plugin && strcmp(plugin, op->plugin) != 0)
 338                        continue;
 339                if (strcmp(op->option, option_str) != 0)
 340                        continue;
 341
 342                /* update option */
 343                free(op->value);
 344                if (val) {
 345                        op->value = strdup(val);
 346                        if (!op->value)
 347                                goto out_free;
 348                } else
 349                        op->value = NULL;
 350
 351                /* plugin and option_str don't get freed at the end */
 352                free(plugin);
 353                free(option_str);
 354
 355                plugin = op->plugin;
 356                option_str = op->option;
 357                break;
 358        }
 359
 360        /* If not found, create */
 361        if (!op) {
 362                op = malloc(sizeof(*op));
 363                if (!op)
 364                        goto out_free;
 365                memset(op, 0, sizeof(*op));
 366                op->plugin = plugin;
 367                op->option = option_str;
 368                if (val) {
 369                        op->value = strdup(val);
 370                        if (!op->value) {
 371                                free(op);
 372                                goto out_free;
 373                        }
 374                }
 375                op->next = trace_plugin_options;
 376                trace_plugin_options = op;
 377        }
 378
 379        return process_option(plugin, option_str, val);
 380
 381out_free:
 382        free(plugin);
 383        free(option_str);
 384        return -ENOMEM;
 385}
 386
 387static void print_op_data(struct trace_seq *s, const char *name,
 388                          const char *op)
 389{
 390        if (op)
 391                trace_seq_printf(s, "%8s:\t%s\n", name, op);
 392}
 393
 394/**
 395 * tep_plugin_print_options - print out the registered plugin options
 396 * @s: The trace_seq descriptor to write the plugin options into
 397 *
 398 * Writes a list of options into trace_seq @s.
 399 */
 400void tep_plugin_print_options(struct trace_seq *s)
 401{
 402        struct registered_plugin_options *reg;
 403        struct tep_plugin_option *op;
 404
 405        for (reg = registered_options; reg; reg = reg->next) {
 406                if (reg != registered_options)
 407                        trace_seq_printf(s, "============\n");
 408                for (op = reg->options; op->name; op++) {
 409                        if (op != reg->options)
 410                                trace_seq_printf(s, "------------\n");
 411                        print_op_data(s, "file", op->file);
 412                        print_op_data(s, "plugin", op->plugin_alias);
 413                        print_op_data(s, "option", op->name);
 414                        print_op_data(s, "desc", op->description);
 415                        print_op_data(s, "value", op->value);
 416                        trace_seq_printf(s, "%8s:\t%d\n", "set", op->set);
 417                }
 418        }
 419}
 420
 421/**
 422 * tep_print_plugins - print out the list of plugins loaded
 423 * @s: the trace_seq descripter to write to
 424 * @prefix: The prefix string to add before listing the option name
 425 * @suffix: The suffix string ot append after the option name
 426 * @list: The list of plugins (usually returned by tep_load_plugins()
 427 *
 428 * Writes to the trace_seq @s the list of plugins (files) that is
 429 * returned by tep_load_plugins(). Use @prefix and @suffix for formating:
 430 * @prefix = "  ", @suffix = "\n".
 431 */
 432void tep_print_plugins(struct trace_seq *s,
 433                       const char *prefix, const char *suffix,
 434                       const struct tep_plugin_list *list)
 435{
 436        while (list) {
 437                trace_seq_printf(s, "%s%s%s", prefix, list->name, suffix);
 438                list = list->next;
 439        }
 440}
 441
 442static void
 443load_plugin(struct tep_handle *tep, const char *path,
 444            const char *file, void *data)
 445{
 446        struct tep_plugin_list **plugin_list = data;
 447        struct tep_plugin_option *options;
 448        tep_plugin_load_func func;
 449        struct tep_plugin_list *list;
 450        const char *alias;
 451        char *plugin;
 452        void *handle;
 453        int ret;
 454
 455        ret = asprintf(&plugin, "%s/%s", path, file);
 456        if (ret < 0) {
 457                warning("could not allocate plugin memory\n");
 458                return;
 459        }
 460
 461        handle = dlopen(plugin, RTLD_NOW | RTLD_GLOBAL);
 462        if (!handle) {
 463                warning("could not load plugin '%s'\n%s\n",
 464                        plugin, dlerror());
 465                goto out_free;
 466        }
 467
 468        alias = dlsym(handle, TEP_PLUGIN_ALIAS_NAME);
 469        if (!alias)
 470                alias = file;
 471
 472        options = dlsym(handle, TEP_PLUGIN_OPTIONS_NAME);
 473        if (options) {
 474                while (options->name) {
 475                        ret = update_option(alias, options);
 476                        if (ret < 0)
 477                                goto out_free;
 478                        options++;
 479                }
 480        }
 481
 482        func = dlsym(handle, TEP_PLUGIN_LOADER_NAME);
 483        if (!func) {
 484                warning("could not find func '%s' in plugin '%s'\n%s\n",
 485                        TEP_PLUGIN_LOADER_NAME, plugin, dlerror());
 486                goto out_free;
 487        }
 488
 489        list = malloc(sizeof(*list));
 490        if (!list) {
 491                warning("could not allocate plugin memory\n");
 492                goto out_free;
 493        }
 494
 495        list->next = *plugin_list;
 496        list->handle = handle;
 497        list->name = plugin;
 498        *plugin_list = list;
 499
 500        pr_stat("registering plugin: %s", plugin);
 501        func(tep);
 502        return;
 503
 504 out_free:
 505        free(plugin);
 506}
 507
 508static void
 509load_plugins_dir(struct tep_handle *tep, const char *suffix,
 510                 const char *path,
 511                 void (*load_plugin)(struct tep_handle *tep,
 512                                     const char *path,
 513                                     const char *name,
 514                                     void *data),
 515                 void *data)
 516{
 517        struct dirent *dent;
 518        struct stat st;
 519        DIR *dir;
 520        int ret;
 521
 522        ret = stat(path, &st);
 523        if (ret < 0)
 524                return;
 525
 526        if (!S_ISDIR(st.st_mode))
 527                return;
 528
 529        dir = opendir(path);
 530        if (!dir)
 531                return;
 532
 533        while ((dent = readdir(dir))) {
 534                const char *name = dent->d_name;
 535
 536                if (strcmp(name, ".") == 0 ||
 537                    strcmp(name, "..") == 0)
 538                        continue;
 539
 540                /* Only load plugins that end in suffix */
 541                if (strcmp(name + (strlen(name) - strlen(suffix)), suffix) != 0)
 542                        continue;
 543
 544                load_plugin(tep, path, name, data);
 545        }
 546
 547        closedir(dir);
 548}
 549
 550/**
 551 * tep_load_plugins_hook - call a user specified callback to load a plugin
 552 * @tep: handler to traceevent context
 553 * @suffix: filter only plugin files with given suffix
 554 * @load_plugin: user specified callback, called for each plugin file
 555 * @data: custom context, passed to @load_plugin
 556 *
 557 * Searches for traceevent plugin files and calls @load_plugin for each
 558 * The order of plugins search is:
 559 *  - Directories, specified in @tep->plugins_dir and priority TEP_PLUGIN_FIRST
 560 *  - Directory, specified at compile time with PLUGIN_TRACEEVENT_DIR
 561 *  - Directory, specified by environment variable TRACEEVENT_PLUGIN_DIR
 562 *  - In user's home: ~/.local/lib/traceevent/plugins/
 563 *  - Directories, specified in @tep->plugins_dir and priority TEP_PLUGIN_LAST
 564 *
 565 */
 566void tep_load_plugins_hook(struct tep_handle *tep, const char *suffix,
 567                           void (*load_plugin)(struct tep_handle *tep,
 568                                               const char *path,
 569                                               const char *name,
 570                                               void *data),
 571                           void *data)
 572{
 573        struct tep_plugins_dir *dir = NULL;
 574        char *home;
 575        char *path;
 576        char *envdir;
 577        int ret;
 578
 579        if (tep && tep->flags & TEP_DISABLE_PLUGINS)
 580                return;
 581
 582        if (tep)
 583                dir = tep->plugins_dir;
 584        while (dir) {
 585                if (dir->prio == TEP_PLUGIN_FIRST)
 586                        load_plugins_dir(tep, suffix, dir->path,
 587                                         load_plugin, data);
 588                dir = dir->next;
 589        }
 590
 591        /*
 592         * If a system plugin directory was defined,
 593         * check that first.
 594         */
 595#ifdef PLUGIN_DIR
 596        if (!tep || !(tep->flags & TEP_DISABLE_SYS_PLUGINS))
 597                load_plugins_dir(tep, suffix, PLUGIN_DIR,
 598                                 load_plugin, data);
 599#endif
 600
 601        /*
 602         * Next let the environment-set plugin directory
 603         * override the system defaults.
 604         */
 605        envdir = getenv("TRACEEVENT_PLUGIN_DIR");
 606        if (envdir)
 607                load_plugins_dir(tep, suffix, envdir, load_plugin, data);
 608
 609        /*
 610         * Now let the home directory override the environment
 611         * or system defaults.
 612         */
 613        home = getenv("HOME");
 614        if (!home)
 615                return;
 616
 617        ret = asprintf(&path, "%s/%s", home, LOCAL_PLUGIN_DIR);
 618        if (ret < 0) {
 619                warning("could not allocate plugin memory\n");
 620                return;
 621        }
 622
 623        load_plugins_dir(tep, suffix, path, load_plugin, data);
 624
 625        if (tep)
 626                dir = tep->plugins_dir;
 627        while (dir) {
 628                if (dir->prio == TEP_PLUGIN_LAST)
 629                        load_plugins_dir(tep, suffix, dir->path,
 630                                         load_plugin, data);
 631                dir = dir->next;
 632        }
 633
 634        free(path);
 635}
 636
 637struct tep_plugin_list*
 638tep_load_plugins(struct tep_handle *tep)
 639{
 640        struct tep_plugin_list *list = NULL;
 641
 642        tep_load_plugins_hook(tep, ".so", load_plugin, &list);
 643        return list;
 644}
 645
 646/**
 647 * tep_add_plugin_path - Add a new plugin directory.
 648 * @tep: Trace event handler.
 649 * @path: Path to a directory. All plugin files in that
 650 *        directory will be loaded.
 651 *@prio: Load priority of the plugins in that directory.
 652 *
 653 * Returns -1 in case of an error, 0 otherwise.
 654 */
 655int tep_add_plugin_path(struct tep_handle *tep, char *path,
 656                        enum tep_plugin_load_priority prio)
 657{
 658        struct tep_plugins_dir *dir;
 659
 660        if (!tep || !path)
 661                return -1;
 662
 663        dir = calloc(1, sizeof(*dir));
 664        if (!dir)
 665                return -1;
 666
 667        dir->path = strdup(path);
 668        if (!dir->path) {
 669                free(dir);
 670                return -1;
 671        }
 672        dir->prio = prio;
 673        dir->next = tep->plugins_dir;
 674        tep->plugins_dir = dir;
 675
 676        return 0;
 677}
 678
 679__hidden void free_tep_plugin_paths(struct tep_handle *tep)
 680{
 681        struct tep_plugins_dir *dir;
 682
 683        if (!tep)
 684                return;
 685
 686        dir = tep->plugins_dir;
 687        while (dir) {
 688                tep->plugins_dir = tep->plugins_dir->next;
 689                free(dir->path);
 690                free(dir);
 691                dir = tep->plugins_dir;
 692        }
 693}
 694
 695void
 696tep_unload_plugins(struct tep_plugin_list *plugin_list, struct tep_handle *tep)
 697{
 698        tep_plugin_unload_func func;
 699        struct tep_plugin_list *list;
 700
 701        while (plugin_list) {
 702                list = plugin_list;
 703                plugin_list = list->next;
 704                func = dlsym(list->handle, TEP_PLUGIN_UNLOADER_NAME);
 705                if (func)
 706                        func(tep);
 707                dlclose(list->handle);
 708                free(list->name);
 709                free(list);
 710        }
 711}
 712