at v4.17-rc5 458 lines 10 kB view raw
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}