1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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
67 if (op->value)
68
69 return 0;
70 op->set ^= 1;
71 return 0;
72 }
73
74
75
76
77
78 if (op->value) {
79 op->value = val;
80 return 0;
81 }
82
83
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
101
102
103
104
105
106
107
108
109
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 int ret;
124
125 ret = asprintf(&name, "%s:%s", alias, op->name);
126 if (ret < 0)
127 goto err;
128
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
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
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
219
220
221
222
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
245
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
264
265
266
267
268
269
270
271
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 int ret;
294
295 ret = asprintf(&plugin, "%s/%s", path, file);
296 if (ret < 0) {
297 warning("could not allocate plugin memory\n");
298 return;
299 }
300
301 handle = dlopen(plugin, RTLD_NOW | RTLD_GLOBAL);
302 if (!handle) {
303 warning("could not load plugin '%s'\n%s\n",
304 plugin, dlerror());
305 goto out_free;
306 }
307
308 alias = dlsym(handle, PEVENT_PLUGIN_ALIAS_NAME);
309 if (!alias)
310 alias = file;
311
312 func = dlsym(handle, PEVENT_PLUGIN_LOADER_NAME);
313 if (!func) {
314 warning("could not find func '%s' in plugin '%s'\n%s\n",
315 PEVENT_PLUGIN_LOADER_NAME, plugin, dlerror());
316 goto out_free;
317 }
318
319 list = malloc(sizeof(*list));
320 if (!list) {
321 warning("could not allocate plugin memory\n");
322 goto out_free;
323 }
324
325 list->next = *plugin_list;
326 list->handle = handle;
327 list->name = plugin;
328 *plugin_list = list;
329
330 pr_stat("registering plugin: %s", plugin);
331 func(pevent);
332 return;
333
334 out_free:
335 free(plugin);
336}
337
338static void
339load_plugins_dir(struct pevent *pevent, const char *suffix,
340 const char *path,
341 void (*load_plugin)(struct pevent *pevent,
342 const char *path,
343 const char *name,
344 void *data),
345 void *data)
346{
347 struct dirent *dent;
348 struct stat st;
349 DIR *dir;
350 int ret;
351
352 ret = stat(path, &st);
353 if (ret < 0)
354 return;
355
356 if (!S_ISDIR(st.st_mode))
357 return;
358
359 dir = opendir(path);
360 if (!dir)
361 return;
362
363 while ((dent = readdir(dir))) {
364 const char *name = dent->d_name;
365
366 if (strcmp(name, ".") == 0 ||
367 strcmp(name, "..") == 0)
368 continue;
369
370
371 if (strcmp(name + (strlen(name) - strlen(suffix)), suffix) != 0)
372 continue;
373
374 load_plugin(pevent, path, name, data);
375 }
376
377 closedir(dir);
378}
379
380static void
381load_plugins(struct pevent *pevent, const char *suffix,
382 void (*load_plugin)(struct pevent *pevent,
383 const char *path,
384 const char *name,
385 void *data),
386 void *data)
387{
388 char *home;
389 char *path;
390 char *envdir;
391 int ret;
392
393 if (pevent->flags & PEVENT_DISABLE_PLUGINS)
394 return;
395
396
397
398
399
400#ifdef PLUGIN_DIR
401 if (!(pevent->flags & PEVENT_DISABLE_SYS_PLUGINS))
402 load_plugins_dir(pevent, suffix, PLUGIN_DIR,
403 load_plugin, data);
404#endif
405
406
407
408
409
410 envdir = getenv("TRACEEVENT_PLUGIN_DIR");
411 if (envdir)
412 load_plugins_dir(pevent, suffix, envdir, load_plugin, data);
413
414
415
416
417
418 home = getenv("HOME");
419 if (!home)
420 return;
421
422 ret = asprintf(&path, "%s/%s", home, LOCAL_PLUGIN_DIR);
423 if (ret < 0) {
424 warning("could not allocate plugin memory\n");
425 return;
426 }
427
428 load_plugins_dir(pevent, suffix, path, load_plugin, data);
429
430 free(path);
431}
432
433struct plugin_list*
434traceevent_load_plugins(struct pevent *pevent)
435{
436 struct plugin_list *list = NULL;
437
438 load_plugins(pevent, ".so", load_plugin, &list);
439 return list;
440}
441
442void
443traceevent_unload_plugins(struct plugin_list *plugin_list, struct pevent *pevent)
444{
445 pevent_plugin_unload_func func;
446 struct plugin_list *list;
447
448 while (plugin_list) {
449 list = plugin_list;
450 plugin_list = list->next;
451 func = dlsym(list->handle, PEVENT_PLUGIN_UNLOADER_NAME);
452 if (func)
453 func(pevent);
454 dlclose(list->handle);
455 free(list->name);
456 free(list);
457 }
458}
459