event-plugin.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711
  1. // SPDX-License-Identifier: LGPL-2.1
  2. /*
  3. * Copyright (C) 2009, 2010 Red Hat Inc, Steven Rostedt <srostedt@redhat.com>
  4. *
  5. */
  6. #include <ctype.h>
  7. #include <stdio.h>
  8. #include <string.h>
  9. #include <dlfcn.h>
  10. #include <stdlib.h>
  11. #include <sys/types.h>
  12. #include <sys/stat.h>
  13. #include <unistd.h>
  14. #include <dirent.h>
  15. #include <errno.h>
  16. #include "event-parse.h"
  17. #include "event-parse-local.h"
  18. #include "event-utils.h"
  19. #include "trace-seq.h"
  20. #define LOCAL_PLUGIN_DIR ".local/lib/traceevent/plugins/"
  21. static struct registered_plugin_options {
  22. struct registered_plugin_options *next;
  23. struct tep_plugin_option *options;
  24. } *registered_options;
  25. static struct trace_plugin_options {
  26. struct trace_plugin_options *next;
  27. char *plugin;
  28. char *option;
  29. char *value;
  30. } *trace_plugin_options;
  31. struct tep_plugin_list {
  32. struct tep_plugin_list *next;
  33. char *name;
  34. void *handle;
  35. };
  36. struct tep_plugins_dir {
  37. struct tep_plugins_dir *next;
  38. char *path;
  39. enum tep_plugin_load_priority prio;
  40. };
  41. static void lower_case(char *str)
  42. {
  43. if (!str)
  44. return;
  45. for (; *str; str++)
  46. *str = tolower(*str);
  47. }
  48. static int update_option_value(struct tep_plugin_option *op, const char *val)
  49. {
  50. char *op_val;
  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. * If the option has a value then it takes a string
  61. * otherwise the option is a boolean.
  62. */
  63. if (op->value) {
  64. op->value = val;
  65. return 0;
  66. }
  67. /* Option is boolean, must be either "1", "0", "true" or "false" */
  68. op_val = strdup(val);
  69. if (!op_val)
  70. return -1;
  71. lower_case(op_val);
  72. if (strcmp(val, "1") == 0 || strcmp(val, "true") == 0)
  73. op->set = 1;
  74. else if (strcmp(val, "0") == 0 || strcmp(val, "false") == 0)
  75. op->set = 0;
  76. free(op_val);
  77. return 0;
  78. }
  79. /**
  80. * tep_plugin_list_options - get list of plugin options
  81. *
  82. * Returns an array of char strings that list the currently registered
  83. * plugin options in the format of <plugin>:<option>. This list can be
  84. * used by toggling the option.
  85. *
  86. * Returns NULL if there's no options registered. On error it returns
  87. * INVALID_PLUGIN_LIST_OPTION
  88. *
  89. * Must be freed with tep_plugin_free_options_list().
  90. */
  91. char **tep_plugin_list_options(void)
  92. {
  93. struct registered_plugin_options *reg;
  94. struct tep_plugin_option *op;
  95. char **list = NULL;
  96. char *name;
  97. int count = 0;
  98. for (reg = registered_options; reg; reg = reg->next) {
  99. for (op = reg->options; op->name; op++) {
  100. char *alias = op->plugin_alias ? op->plugin_alias : op->file;
  101. char **temp = list;
  102. int ret;
  103. ret = asprintf(&name, "%s:%s", alias, op->name);
  104. if (ret < 0)
  105. goto err;
  106. list = realloc(list, count + 2);
  107. if (!list) {
  108. list = temp;
  109. free(name);
  110. goto err;
  111. }
  112. list[count++] = name;
  113. list[count] = NULL;
  114. }
  115. }
  116. return list;
  117. err:
  118. while (--count >= 0)
  119. free(list[count]);
  120. free(list);
  121. return INVALID_PLUGIN_LIST_OPTION;
  122. }
  123. void tep_plugin_free_options_list(char **list)
  124. {
  125. int i;
  126. if (!list)
  127. return;
  128. if (list == INVALID_PLUGIN_LIST_OPTION)
  129. return;
  130. for (i = 0; list[i]; i++)
  131. free(list[i]);
  132. free(list);
  133. }
  134. static int
  135. update_option(const char *file, struct tep_plugin_option *option)
  136. {
  137. struct trace_plugin_options *op;
  138. char *plugin;
  139. int ret = 0;
  140. if (option->plugin_alias) {
  141. plugin = strdup(option->plugin_alias);
  142. if (!plugin)
  143. return -1;
  144. } else {
  145. char *p;
  146. plugin = strdup(file);
  147. if (!plugin)
  148. return -1;
  149. p = strstr(plugin, ".");
  150. if (p)
  151. *p = '\0';
  152. }
  153. /* first look for named options */
  154. for (op = trace_plugin_options; op; op = op->next) {
  155. if (!op->plugin)
  156. continue;
  157. if (strcmp(op->plugin, plugin) != 0)
  158. continue;
  159. if (strcmp(op->option, option->name) != 0)
  160. continue;
  161. ret = update_option_value(option, op->value);
  162. if (ret)
  163. goto out;
  164. break;
  165. }
  166. /* first look for unnamed options */
  167. for (op = trace_plugin_options; op; op = op->next) {
  168. if (op->plugin)
  169. continue;
  170. if (strcmp(op->option, option->name) != 0)
  171. continue;
  172. ret = update_option_value(option, op->value);
  173. break;
  174. }
  175. out:
  176. free(plugin);
  177. return ret;
  178. }
  179. /**
  180. * tep_plugin_add_options - Add a set of options by a plugin
  181. * @name: The name of the plugin adding the options
  182. * @options: The set of options being loaded
  183. *
  184. * Sets the options with the values that have been added by user.
  185. */
  186. int tep_plugin_add_options(const char *name,
  187. struct tep_plugin_option *options)
  188. {
  189. struct registered_plugin_options *reg;
  190. reg = malloc(sizeof(*reg));
  191. if (!reg)
  192. return -1;
  193. reg->next = registered_options;
  194. reg->options = options;
  195. registered_options = reg;
  196. while (options->name) {
  197. update_option(name, options);
  198. options++;
  199. }
  200. return 0;
  201. }
  202. /**
  203. * tep_plugin_remove_options - remove plugin options that were registered
  204. * @options: Options to removed that were registered with tep_plugin_add_options
  205. */
  206. void tep_plugin_remove_options(struct tep_plugin_option *options)
  207. {
  208. struct registered_plugin_options **last;
  209. struct registered_plugin_options *reg;
  210. for (last = &registered_options; *last; last = &(*last)->next) {
  211. if ((*last)->options == options) {
  212. reg = *last;
  213. *last = reg->next;
  214. free(reg);
  215. return;
  216. }
  217. }
  218. }
  219. static int parse_option_name(char **option, char **plugin)
  220. {
  221. char *p;
  222. *plugin = NULL;
  223. if ((p = strstr(*option, ":"))) {
  224. *plugin = *option;
  225. *p = '\0';
  226. *option = strdup(p + 1);
  227. if (!*option)
  228. return -1;
  229. }
  230. return 0;
  231. }
  232. static struct tep_plugin_option *
  233. find_registered_option(const char *plugin, const char *option)
  234. {
  235. struct registered_plugin_options *reg;
  236. struct tep_plugin_option *op;
  237. const char *op_plugin;
  238. for (reg = registered_options; reg; reg = reg->next) {
  239. for (op = reg->options; op->name; op++) {
  240. if (op->plugin_alias)
  241. op_plugin = op->plugin_alias;
  242. else
  243. op_plugin = op->file;
  244. if (plugin && strcmp(plugin, op_plugin) != 0)
  245. continue;
  246. if (strcmp(option, op->name) != 0)
  247. continue;
  248. return op;
  249. }
  250. }
  251. return NULL;
  252. }
  253. static int process_option(const char *plugin, const char *option, const char *val)
  254. {
  255. struct tep_plugin_option *op;
  256. op = find_registered_option(plugin, option);
  257. if (!op)
  258. return 0;
  259. return update_option_value(op, val);
  260. }
  261. /**
  262. * tep_plugin_add_option - add an option/val pair to set plugin options
  263. * @name: The name of the option (format: <plugin>:<option> or just <option>)
  264. * @val: (optional) the value for the option
  265. *
  266. * Modify a plugin option. If @val is given than the value of the option
  267. * is set (note, some options just take a boolean, so @val must be either
  268. * "1" or "0" or "true" or "false").
  269. */
  270. int tep_plugin_add_option(const char *name, const char *val)
  271. {
  272. struct trace_plugin_options *op;
  273. char *option_str;
  274. char *plugin;
  275. option_str = strdup(name);
  276. if (!option_str)
  277. return -ENOMEM;
  278. if (parse_option_name(&option_str, &plugin) < 0)
  279. return -ENOMEM;
  280. /* If the option exists, update the val */
  281. for (op = trace_plugin_options; op; op = op->next) {
  282. /* Both must be NULL or not NULL */
  283. if ((!plugin || !op->plugin) && plugin != op->plugin)
  284. continue;
  285. if (plugin && strcmp(plugin, op->plugin) != 0)
  286. continue;
  287. if (strcmp(op->option, option_str) != 0)
  288. continue;
  289. /* update option */
  290. free(op->value);
  291. if (val) {
  292. op->value = strdup(val);
  293. if (!op->value)
  294. goto out_free;
  295. } else
  296. op->value = NULL;
  297. /* plugin and option_str don't get freed at the end */
  298. free(plugin);
  299. free(option_str);
  300. plugin = op->plugin;
  301. option_str = op->option;
  302. break;
  303. }
  304. /* If not found, create */
  305. if (!op) {
  306. op = malloc(sizeof(*op));
  307. if (!op)
  308. goto out_free;
  309. memset(op, 0, sizeof(*op));
  310. op->plugin = plugin;
  311. op->option = option_str;
  312. if (val) {
  313. op->value = strdup(val);
  314. if (!op->value) {
  315. free(op);
  316. goto out_free;
  317. }
  318. }
  319. op->next = trace_plugin_options;
  320. trace_plugin_options = op;
  321. }
  322. return process_option(plugin, option_str, val);
  323. out_free:
  324. free(plugin);
  325. free(option_str);
  326. return -ENOMEM;
  327. }
  328. static void print_op_data(struct trace_seq *s, const char *name,
  329. const char *op)
  330. {
  331. if (op)
  332. trace_seq_printf(s, "%8s:\t%s\n", name, op);
  333. }
  334. /**
  335. * tep_plugin_print_options - print out the registered plugin options
  336. * @s: The trace_seq descriptor to write the plugin options into
  337. *
  338. * Writes a list of options into trace_seq @s.
  339. */
  340. void tep_plugin_print_options(struct trace_seq *s)
  341. {
  342. struct registered_plugin_options *reg;
  343. struct tep_plugin_option *op;
  344. for (reg = registered_options; reg; reg = reg->next) {
  345. if (reg != registered_options)
  346. trace_seq_printf(s, "============\n");
  347. for (op = reg->options; op->name; op++) {
  348. if (op != reg->options)
  349. trace_seq_printf(s, "------------\n");
  350. print_op_data(s, "file", op->file);
  351. print_op_data(s, "plugin", op->plugin_alias);
  352. print_op_data(s, "option", op->name);
  353. print_op_data(s, "desc", op->description);
  354. print_op_data(s, "value", op->value);
  355. trace_seq_printf(s, "%8s:\t%d\n", "set", op->set);
  356. }
  357. }
  358. }
  359. /**
  360. * tep_print_plugins - print out the list of plugins loaded
  361. * @s: the trace_seq descripter to write to
  362. * @prefix: The prefix string to add before listing the option name
  363. * @suffix: The suffix string ot append after the option name
  364. * @list: The list of plugins (usually returned by tep_load_plugins()
  365. *
  366. * Writes to the trace_seq @s the list of plugins (files) that is
  367. * returned by tep_load_plugins(). Use @prefix and @suffix for formating:
  368. * @prefix = " ", @suffix = "\n".
  369. */
  370. void tep_print_plugins(struct trace_seq *s,
  371. const char *prefix, const char *suffix,
  372. const struct tep_plugin_list *list)
  373. {
  374. while (list) {
  375. trace_seq_printf(s, "%s%s%s", prefix, list->name, suffix);
  376. list = list->next;
  377. }
  378. }
  379. static void
  380. load_plugin(struct tep_handle *tep, const char *path,
  381. const char *file, void *data)
  382. {
  383. struct tep_plugin_list **plugin_list = data;
  384. struct tep_plugin_option *options;
  385. tep_plugin_load_func func;
  386. struct tep_plugin_list *list;
  387. const char *alias;
  388. char *plugin;
  389. void *handle;
  390. int ret;
  391. ret = asprintf(&plugin, "%s/%s", path, file);
  392. if (ret < 0) {
  393. warning("could not allocate plugin memory\n");
  394. return;
  395. }
  396. handle = dlopen(plugin, RTLD_NOW | RTLD_GLOBAL);
  397. if (!handle) {
  398. warning("could not load plugin '%s'\n%s\n",
  399. plugin, dlerror());
  400. goto out_free;
  401. }
  402. alias = dlsym(handle, TEP_PLUGIN_ALIAS_NAME);
  403. if (!alias)
  404. alias = file;
  405. options = dlsym(handle, TEP_PLUGIN_OPTIONS_NAME);
  406. if (options) {
  407. while (options->name) {
  408. ret = update_option(alias, options);
  409. if (ret < 0)
  410. goto out_free;
  411. options++;
  412. }
  413. }
  414. func = dlsym(handle, TEP_PLUGIN_LOADER_NAME);
  415. if (!func) {
  416. warning("could not find func '%s' in plugin '%s'\n%s\n",
  417. TEP_PLUGIN_LOADER_NAME, plugin, dlerror());
  418. goto out_free;
  419. }
  420. list = malloc(sizeof(*list));
  421. if (!list) {
  422. warning("could not allocate plugin memory\n");
  423. goto out_free;
  424. }
  425. list->next = *plugin_list;
  426. list->handle = handle;
  427. list->name = plugin;
  428. *plugin_list = list;
  429. pr_stat("registering plugin: %s", plugin);
  430. func(tep);
  431. return;
  432. out_free:
  433. free(plugin);
  434. }
  435. static void
  436. load_plugins_dir(struct tep_handle *tep, const char *suffix,
  437. const char *path,
  438. void (*load_plugin)(struct tep_handle *tep,
  439. const char *path,
  440. const char *name,
  441. void *data),
  442. void *data)
  443. {
  444. struct dirent *dent;
  445. struct stat st;
  446. DIR *dir;
  447. int ret;
  448. ret = stat(path, &st);
  449. if (ret < 0)
  450. return;
  451. if (!S_ISDIR(st.st_mode))
  452. return;
  453. dir = opendir(path);
  454. if (!dir)
  455. return;
  456. while ((dent = readdir(dir))) {
  457. const char *name = dent->d_name;
  458. if (strcmp(name, ".") == 0 ||
  459. strcmp(name, "..") == 0)
  460. continue;
  461. /* Only load plugins that end in suffix */
  462. if (strcmp(name + (strlen(name) - strlen(suffix)), suffix) != 0)
  463. continue;
  464. load_plugin(tep, path, name, data);
  465. }
  466. closedir(dir);
  467. }
  468. /**
  469. * tep_load_plugins_hook - call a user specified callback to load a plugin
  470. * @tep: handler to traceevent context
  471. * @suffix: filter only plugin files with given suffix
  472. * @load_plugin: user specified callback, called for each plugin file
  473. * @data: custom context, passed to @load_plugin
  474. *
  475. * Searches for traceevent plugin files and calls @load_plugin for each
  476. * The order of plugins search is:
  477. * - Directories, specified in @tep->plugins_dir and priority TEP_PLUGIN_FIRST
  478. * - Directory, specified at compile time with PLUGIN_TRACEEVENT_DIR
  479. * - Directory, specified by environment variable TRACEEVENT_PLUGIN_DIR
  480. * - In user's home: ~/.local/lib/traceevent/plugins/
  481. * - Directories, specified in @tep->plugins_dir and priority TEP_PLUGIN_LAST
  482. *
  483. */
  484. void tep_load_plugins_hook(struct tep_handle *tep, const char *suffix,
  485. void (*load_plugin)(struct tep_handle *tep,
  486. const char *path,
  487. const char *name,
  488. void *data),
  489. void *data)
  490. {
  491. struct tep_plugins_dir *dir = NULL;
  492. char *home;
  493. char *path;
  494. char *envdir;
  495. int ret;
  496. if (tep && tep->flags & TEP_DISABLE_PLUGINS)
  497. return;
  498. if (tep)
  499. dir = tep->plugins_dir;
  500. while (dir) {
  501. if (dir->prio == TEP_PLUGIN_FIRST)
  502. load_plugins_dir(tep, suffix, dir->path,
  503. load_plugin, data);
  504. dir = dir->next;
  505. }
  506. /*
  507. * If a system plugin directory was defined,
  508. * check that first.
  509. */
  510. #ifdef PLUGIN_DIR
  511. if (!tep || !(tep->flags & TEP_DISABLE_SYS_PLUGINS))
  512. load_plugins_dir(tep, suffix, PLUGIN_DIR,
  513. load_plugin, data);
  514. #endif
  515. /*
  516. * Next let the environment-set plugin directory
  517. * override the system defaults.
  518. */
  519. envdir = getenv("TRACEEVENT_PLUGIN_DIR");
  520. if (envdir)
  521. load_plugins_dir(tep, suffix, envdir, load_plugin, data);
  522. /*
  523. * Now let the home directory override the environment
  524. * or system defaults.
  525. */
  526. home = getenv("HOME");
  527. if (!home)
  528. return;
  529. ret = asprintf(&path, "%s/%s", home, LOCAL_PLUGIN_DIR);
  530. if (ret < 0) {
  531. warning("could not allocate plugin memory\n");
  532. return;
  533. }
  534. load_plugins_dir(tep, suffix, path, load_plugin, data);
  535. if (tep)
  536. dir = tep->plugins_dir;
  537. while (dir) {
  538. if (dir->prio == TEP_PLUGIN_LAST)
  539. load_plugins_dir(tep, suffix, dir->path,
  540. load_plugin, data);
  541. dir = dir->next;
  542. }
  543. free(path);
  544. }
  545. struct tep_plugin_list*
  546. tep_load_plugins(struct tep_handle *tep)
  547. {
  548. struct tep_plugin_list *list = NULL;
  549. tep_load_plugins_hook(tep, ".so", load_plugin, &list);
  550. return list;
  551. }
  552. /**
  553. * tep_add_plugin_path - Add a new plugin directory.
  554. * @tep: Trace event handler.
  555. * @path: Path to a directory. All plugin files in that
  556. * directory will be loaded.
  557. *@prio: Load priority of the plugins in that directory.
  558. *
  559. * Returns -1 in case of an error, 0 otherwise.
  560. */
  561. int tep_add_plugin_path(struct tep_handle *tep, char *path,
  562. enum tep_plugin_load_priority prio)
  563. {
  564. struct tep_plugins_dir *dir;
  565. if (!tep || !path)
  566. return -1;
  567. dir = calloc(1, sizeof(*dir));
  568. if (!dir)
  569. return -1;
  570. dir->path = strdup(path);
  571. if (!dir->path) {
  572. free(dir);
  573. return -1;
  574. }
  575. dir->prio = prio;
  576. dir->next = tep->plugins_dir;
  577. tep->plugins_dir = dir;
  578. return 0;
  579. }
  580. __hidden void free_tep_plugin_paths(struct tep_handle *tep)
  581. {
  582. struct tep_plugins_dir *dir;
  583. if (!tep)
  584. return;
  585. dir = tep->plugins_dir;
  586. while (dir) {
  587. tep->plugins_dir = tep->plugins_dir->next;
  588. free(dir->path);
  589. free(dir);
  590. dir = tep->plugins_dir;
  591. }
  592. }
  593. void
  594. tep_unload_plugins(struct tep_plugin_list *plugin_list, struct tep_handle *tep)
  595. {
  596. tep_plugin_unload_func func;
  597. struct tep_plugin_list *list;
  598. while (plugin_list) {
  599. list = plugin_list;
  600. plugin_list = list->next;
  601. func = dlsym(list->handle, TEP_PLUGIN_UNLOADER_NAME);
  602. if (func)
  603. func(tep);
  604. dlclose(list->handle);
  605. free(list->name);
  606. free(list);
  607. }
  608. }