event-plugin.c (15518B)
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 = ®istered_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}