Merge branch 'for-4.20/apple' into for-linus
[linux-2.6-block.git] / tools / lib / traceevent / event-plugin.c
CommitLineData
6ab025ed 1// SPDX-License-Identifier: LGPL-2.1
c877bbd8
JO
2/*
3 * Copyright (C) 2009, 2010 Red Hat Inc, Steven Rostedt <srostedt@redhat.com>
4 *
c877bbd8
JO
5 */
6
5dbcfd93 7#include <ctype.h>
5827f2fa 8#include <stdio.h>
c877bbd8
JO
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
5827f2fa
SR
21static struct registered_plugin_options {
22 struct registered_plugin_options *next;
c32d52b4 23 struct tep_plugin_option *options;
5827f2fa
SR
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
c877bbd8
JO
33struct plugin_list {
34 struct plugin_list *next;
35 char *name;
36 void *handle;
37};
38
5dbcfd93
SR
39static void lower_case(char *str)
40{
41 if (!str)
42 return;
43 for (; *str; str++)
44 *str = tolower(*str);
45}
46
c32d52b4 47static int update_option_value(struct tep_plugin_option *op, const char *val)
5dbcfd93
SR
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
5827f2fa 85/**
ca2921dd 86 * tep_plugin_list_options - get list of plugin options
5827f2fa
SR
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 *
ca2921dd 95 * Must be freed with tep_plugin_free_options_list().
5827f2fa 96 */
ca2921dd 97char **tep_plugin_list_options(void)
5827f2fa
SR
98{
99 struct registered_plugin_options *reg;
c32d52b4 100 struct tep_plugin_option *op;
5827f2fa
SR
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;
67dfc376 109 int ret;
5827f2fa 110
67dfc376
FV
111 ret = asprintf(&name, "%s:%s", alias, op->name);
112 if (ret < 0)
5827f2fa
SR
113 goto err;
114
5827f2fa
SR
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
ca2921dd 135void tep_plugin_free_options_list(char **list)
5827f2fa
SR
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
c32d52b4 152update_option(const char *file, struct tep_plugin_option *option)
5827f2fa
SR
153{
154 struct trace_plugin_options *op;
155 char *plugin;
5dbcfd93 156 int ret = 0;
5827f2fa
SR
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
5dbcfd93
SR
181 ret = update_option_value(option, op->value);
182 if (ret)
183 goto out;
184 break;
5827f2fa
SR
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
5dbcfd93 194 ret = update_option_value(option, op->value);
5827f2fa
SR
195 break;
196 }
197
198 out:
199 free(plugin);
5dbcfd93 200 return ret;
5827f2fa
SR
201}
202
203/**
ca2921dd 204 * tep_plugin_add_options - Add a set of options by a plugin
5827f2fa
SR
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 */
ca2921dd
TSV
210int tep_plugin_add_options(const char *name,
211 struct tep_plugin_option *options)
5827f2fa
SR
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/**
ca2921dd
TSV
230 * tep_plugin_remove_options - remove plugin options that were registered
231 * @options: Options to removed that were registered with tep_plugin_add_options
5827f2fa 232 */
ca2921dd 233void tep_plugin_remove_options(struct tep_plugin_option *options)
5827f2fa
SR
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/**
ca2921dd 249 * tep_print_plugins - print out the list of plugins loaded
5827f2fa
SR
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
fc9b6971 253 * @list: The list of plugins (usually returned by tep_load_plugins()
5827f2fa
SR
254 *
255 * Writes to the trace_seq @s the list of plugins (files) that is
fc9b6971 256 * returned by tep_load_plugins(). Use @prefix and @suffix for formating:
5827f2fa
SR
257 * @prefix = " ", @suffix = "\n".
258 */
ca2921dd
TSV
259void tep_print_plugins(struct trace_seq *s,
260 const char *prefix, const char *suffix,
261 const struct plugin_list *list)
5827f2fa
SR
262{
263 while (list) {
264 trace_seq_printf(s, "%s%s%s", prefix, list->name, suffix);
265 list = list->next;
266 }
267}
268
c877bbd8 269static void
096177a8 270load_plugin(struct tep_handle *pevent, const char *path,
c877bbd8
JO
271 const char *file, void *data)
272{
273 struct plugin_list **plugin_list = data;
c32d52b4 274 tep_plugin_load_func func;
c877bbd8
JO
275 struct plugin_list *list;
276 const char *alias;
277 char *plugin;
278 void *handle;
67dfc376 279 int ret;
c877bbd8 280
67dfc376
FV
281 ret = asprintf(&plugin, "%s/%s", path, file);
282 if (ret < 0) {
f9bb36af
JO
283 warning("could not allocate plugin memory\n");
284 return;
285 }
c877bbd8 286
c877bbd8
JO
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
c32d52b4 294 alias = dlsym(handle, TEP_PLUGIN_ALIAS_NAME);
c877bbd8
JO
295 if (!alias)
296 alias = file;
297
c32d52b4 298 func = dlsym(handle, TEP_PLUGIN_LOADER_NAME);
c877bbd8
JO
299 if (!func) {
300 warning("could not find func '%s' in plugin '%s'\n%s\n",
c32d52b4 301 TEP_PLUGIN_LOADER_NAME, plugin, dlerror());
c877bbd8
JO
302 goto out_free;
303 }
304
f9bb36af
JO
305 list = malloc(sizeof(*list));
306 if (!list) {
307 warning("could not allocate plugin memory\n");
308 goto out_free;
309 }
310
c877bbd8
JO
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
096177a8 325load_plugins_dir(struct tep_handle *pevent, const char *suffix,
c877bbd8 326 const char *path,
096177a8 327 void (*load_plugin)(struct tep_handle *pevent,
c877bbd8
JO
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
096177a8
TSV
367load_plugins(struct tep_handle *pevent, const char *suffix,
368 void (*load_plugin)(struct tep_handle *pevent,
c877bbd8
JO
369 const char *path,
370 const char *name,
371 void *data),
372 void *data)
373{
374 char *home;
375 char *path;
376 char *envdir;
67dfc376 377 int ret;
c877bbd8 378
6fed932e 379 if (pevent->flags & TEP_DISABLE_PLUGINS)
a7c3196c
SRRH
380 return;
381
c877bbd8
JO
382 /*
383 * If a system plugin directory was defined,
384 * check that first.
385 */
386#ifdef PLUGIN_DIR
6fed932e 387 if (!(pevent->flags & TEP_DISABLE_SYS_PLUGINS))
a7c3196c
SRRH
388 load_plugins_dir(pevent, suffix, PLUGIN_DIR,
389 load_plugin, data);
c877bbd8
JO
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
67dfc376
FV
408 ret = asprintf(&path, "%s/%s", home, LOCAL_PLUGIN_DIR);
409 if (ret < 0) {
f9bb36af
JO
410 warning("could not allocate plugin memory\n");
411 return;
412 }
c877bbd8 413
c877bbd8
JO
414 load_plugins_dir(pevent, suffix, path, load_plugin, data);
415
416 free(path);
417}
418
419struct plugin_list*
fc9b6971 420tep_load_plugins(struct tep_handle *pevent)
c877bbd8
JO
421{
422 struct plugin_list *list = NULL;
423
424 load_plugins(pevent, ".so", load_plugin, &list);
425 return list;
426}
427
428void
fc9b6971 429tep_unload_plugins(struct plugin_list *plugin_list, struct tep_handle *pevent)
c877bbd8 430{
c32d52b4 431 tep_plugin_unload_func func;
c877bbd8
JO
432 struct plugin_list *list;
433
434 while (plugin_list) {
435 list = plugin_list;
436 plugin_list = list->next;
c32d52b4 437 func = dlsym(list->handle, TEP_PLUGIN_UNLOADER_NAME);
c877bbd8 438 if (func)
8d0c2224 439 func(pevent);
c877bbd8
JO
440 dlclose(list->handle);
441 free(list->name);
442 free(list);
443 }
444}