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