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 = ®istered_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