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 <stdlib.h>
7 #include <unistd.h>
8 #include <dlfcn.h>
9 #include <sys/stat.h>
10 #include <libgen.h>
11 #include "trace-cmd.h"
12 #include "trace-local.h"
13 #include "trace-cmd-local.h"
14
15 #define LOCAL_PLUGIN_DIR ".local/lib/trace-cmd/plugins/"
16
17 struct trace_plugin_list {
18 struct trace_plugin_list *next;
19 char *name;
20 void *handle;
21 };
22
23 struct trace_plugin_context {
24 enum tracecmd_context context;
25 enum tracecmd_plugin_flag flags;
26 union {
27 void *data;
28 struct tracecmd_input *trace_input;
29 struct tracecmd_output *trace_output;
30 };
31 };
32
33 /**
34 * tracecmd_plugin_context_create - Create and initialize tracecmd plugins context.
35 * @context: Context of the trace-cmd command.
36 * @data: Pointer to the context specific data, which will be passed to plugins.
37 *
38 * Returns a pointer to created tracecmd plugins context, or NULL in case memory
39 * allocation fails. The returned pointer should be freed by free ().
40 */
41 struct trace_plugin_context *
tracecmd_plugin_context_create(enum tracecmd_context context,void * data)42 tracecmd_plugin_context_create(enum tracecmd_context context, void *data)
43 {
44 struct trace_plugin_context *trace;
45
46 trace = calloc(1, sizeof(struct trace_plugin_context));
47 if (!trace)
48 return NULL;
49 trace->context = context;
50 trace->data = data;
51 return trace;
52 }
53
54 /**
55 * tracecmd_plugin_set_flag - Set a flag to tracecmd plugins context.
56 * @context: Context of the trace-cmd command.
57 * @flag: Flag, whil will be set.
58 *
59 */
tracecmd_plugin_set_flag(struct trace_plugin_context * context,enum tracecmd_plugin_flag flag)60 void tracecmd_plugin_set_flag(struct trace_plugin_context *context,
61 enum tracecmd_plugin_flag flag)
62 {
63 if (context)
64 context->flags |= flag;
65 }
66
67 /**
68 * tracecmd_plugin_context_input - Get a tracecmd_input plugin context.
69 * @context: Context of the trace-cmd command.
70 *
71 * Returns pointer to tracecmd_input, if such context is available or
72 * NULL otherwise.
73 */
74 struct tracecmd_input *
tracecmd_plugin_context_input(struct trace_plugin_context * context)75 tracecmd_plugin_context_input(struct trace_plugin_context *context)
76 {
77 if (!context || context->context != TRACECMD_INPUT)
78 return NULL;
79 return context->trace_input;
80 }
81
82 /**
83 * tracecmd_plugin_context_output - Get a tracecmd_output plugin context
84 * @context: Context of the trace-cmd command.
85 *
86 * Returns pointer to tracecmd_output, if such context is available or
87 * NULL otherwise.
88 */
89 struct tracecmd_output *
tracecmd_plugin_context_output(struct trace_plugin_context * context)90 tracecmd_plugin_context_output(struct trace_plugin_context *context)
91 {
92 if (!context || context->context != TRACECMD_OUTPUT)
93 return NULL;
94 return context->trace_output;
95 }
96
97 static void
load_plugin(struct trace_plugin_context * trace,const char * path,const char * file,void * data)98 load_plugin(struct trace_plugin_context *trace, const char *path,
99 const char *file, void *data)
100 {
101 struct trace_plugin_list **plugin_list = data;
102 tracecmd_plugin_load_func func;
103 struct trace_plugin_list *list;
104 const char *alias;
105 char *plugin;
106 void *handle;
107 int ret;
108
109 ret = asprintf(&plugin, "%s/%s", path, file);
110 if (ret < 0) {
111 tracecmd_warning("could not allocate plugin memory");
112 return;
113 }
114
115 handle = dlopen(plugin, RTLD_NOW | RTLD_GLOBAL);
116 if (!handle) {
117 tracecmd_warning("could not load plugin '%s'\n%s", plugin, dlerror());
118 goto out_free;
119 }
120
121 alias = dlsym(handle, TRACECMD_PLUGIN_ALIAS_NAME);
122 if (!alias)
123 alias = file;
124
125 func = dlsym(handle, TRACECMD_PLUGIN_LOADER_NAME);
126 if (!func) {
127 tracecmd_warning("could not find func '%s' in plugin '%s'\n%s",
128 TRACECMD_PLUGIN_LOADER_NAME, plugin, dlerror());
129 goto out_close;
130 }
131
132 list = malloc(sizeof(*list));
133 if (!list) {
134 tracecmd_warning("could not allocate plugin memory");
135 goto out_close;
136 }
137
138 list->next = *plugin_list;
139 list->handle = handle;
140 list->name = plugin;
141 *plugin_list = list;
142
143 tracecmd_info("registering plugin: %s", plugin);
144 func(trace);
145 return;
146
147 out_close:
148 dlclose(handle);
149 out_free:
150 free(plugin);
151 }
152
153 static void
load_plugins_dir(struct trace_plugin_context * trace,const char * suffix,const char * path,void (* load_plugin)(struct trace_plugin_context * trace,const char * path,const char * name,void * data),void * data)154 load_plugins_dir(struct trace_plugin_context *trace, const char *suffix,
155 const char *path,
156 void (*load_plugin)(struct trace_plugin_context *trace,
157 const char *path,
158 const char *name,
159 void *data),
160 void *data)
161 {
162 struct dirent *dent;
163 struct stat st;
164 DIR *dir;
165 int ret;
166
167 ret = stat(path, &st);
168 if (ret < 0)
169 return;
170
171 if (!S_ISDIR(st.st_mode))
172 return;
173
174 dir = opendir(path);
175 if (!dir)
176 return;
177
178 while ((dent = readdir(dir))) {
179 const char *name = dent->d_name;
180
181 if (strcmp(name, ".") == 0 ||
182 strcmp(name, "..") == 0)
183 continue;
184
185 /* Only load plugins that end in suffix */
186 if (strcmp(name + (strlen(name) - strlen(suffix)), suffix) != 0)
187 continue;
188
189 load_plugin(trace, path, name, data);
190 }
191
192 closedir(dir);
193 }
194
get_source_plugins_dir(void)195 static char *get_source_plugins_dir(void)
196 {
197 char *p, path[PATH_MAX+1];
198 int ret;
199
200 ret = readlink("/proc/self/exe", path, PATH_MAX);
201 if (ret > PATH_MAX || ret < 0)
202 return NULL;
203
204 path[ret] = 0;
205 dirname(path);
206 p = strrchr(path, '/');
207 if (!p)
208 return NULL;
209 /* Check if we are in the the source tree */
210 if (strcmp(p, "/tracecmd") != 0)
211 return NULL;
212
213 strcpy(p, "/lib/trace-cmd/plugins");
214 return strdup(path);
215 }
216
217 static void
load_plugins_hook(struct trace_plugin_context * trace,const char * suffix,void (* load_plugin)(struct trace_plugin_context * trace,const char * path,const char * name,void * data),void * data)218 load_plugins_hook(struct trace_plugin_context *trace, const char *suffix,
219 void (*load_plugin)(struct trace_plugin_context *trace,
220 const char *path,
221 const char *name,
222 void *data),
223 void *data)
224 {
225 char *home;
226 char *path;
227 char *envdir;
228 int ret;
229
230 if (trace && trace->flags & TRACECMD_DISABLE_PLUGINS)
231 return;
232
233 /*
234 * If a system plugin directory was defined,
235 * check that first.
236 */
237 #ifdef PLUGIN_TRACECMD_DIR
238 if (!trace || !(trace->flags & TRACECMD_DISABLE_SYS_PLUGINS))
239 load_plugins_dir(trace, suffix, PLUGIN_TRACECMD_DIR,
240 load_plugin, data);
241 #endif
242
243 /*
244 * Next let the environment-set plugin directory
245 * override the system defaults.
246 */
247 envdir = getenv("TRACECMD_PLUGIN_DIR");
248 if (envdir)
249 load_plugins_dir(trace, suffix, envdir, load_plugin, data);
250
251 /*
252 * Now let the home directory override the environment
253 * or system defaults.
254 */
255 home = getenv("HOME");
256 if (!home)
257 return;
258
259 ret = asprintf(&path, "%s/%s", home, LOCAL_PLUGIN_DIR);
260 if (ret < 0) {
261 tracecmd_warning("could not allocate plugin memory");
262 return;
263 }
264
265 load_plugins_dir(trace, suffix, path, load_plugin, data);
266
267 free(path);
268
269 path = get_source_plugins_dir();
270 if (path) {
271 load_plugins_dir(trace, suffix, path, load_plugin, data);
272 free(path);
273 }
274 }
275
276 /**
277 * tracecmd_load_plugins - Load trace-cmd specific plugins.
278 * @context: Context of the trace-cmd command, will be passed to the plugins
279 * at load time.
280 *
281 * Returns a list of loaded plugins
282 */
283 struct trace_plugin_list*
tracecmd_load_plugins(struct trace_plugin_context * trace)284 tracecmd_load_plugins(struct trace_plugin_context *trace)
285 {
286 struct trace_plugin_list *list = NULL;
287
288 load_plugins_hook(trace, ".so", load_plugin, &list);
289 return list;
290 }
291
292 /**
293 * tracecmd_unload_plugins - Unload trace-cmd specific plugins.
294 * @plugin_list - List of plugins, previously loaded with tracecmd_load_plugins.
295 * @context: Context of the trace-cmd command, will be passed to the plugins
296 * at unload time.
297 *
298 */
299 void
tracecmd_unload_plugins(struct trace_plugin_list * plugin_list,struct trace_plugin_context * trace)300 tracecmd_unload_plugins(struct trace_plugin_list *plugin_list,
301 struct trace_plugin_context *trace)
302 {
303 tracecmd_plugin_unload_func func;
304 struct trace_plugin_list *list;
305
306 while (plugin_list) {
307 list = plugin_list;
308 plugin_list = list->next;
309 func = dlsym(list->handle, TRACECMD_PLUGIN_UNLOADER_NAME);
310 if (func)
311 func(trace);
312 dlclose(list->handle);
313 free(list->name);
314 free(list);
315 }
316 }
317