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