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