1 // SPDX-License-Identifier: LGPL-2.1
2 /*
3 * Copyright (C) 2009, 2010 Red Hat Inc, Steven Rostedt <[email protected]>
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_free;
130 }
131
132 list = malloc(sizeof(*list));
133 if (!list) {
134 tracecmd_warning("could not allocate plugin memory");
135 goto out_free;
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_free:
148 free(plugin);
149 }
150
151 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)152 load_plugins_dir(struct trace_plugin_context *trace, const char *suffix,
153 const char *path,
154 void (*load_plugin)(struct trace_plugin_context *trace,
155 const char *path,
156 const char *name,
157 void *data),
158 void *data)
159 {
160 struct dirent *dent;
161 struct stat st;
162 DIR *dir;
163 int ret;
164
165 ret = stat(path, &st);
166 if (ret < 0)
167 return;
168
169 if (!S_ISDIR(st.st_mode))
170 return;
171
172 dir = opendir(path);
173 if (!dir)
174 return;
175
176 while ((dent = readdir(dir))) {
177 const char *name = dent->d_name;
178
179 if (strcmp(name, ".") == 0 ||
180 strcmp(name, "..") == 0)
181 continue;
182
183 /* Only load plugins that end in suffix */
184 if (strcmp(name + (strlen(name) - strlen(suffix)), suffix) != 0)
185 continue;
186
187 load_plugin(trace, path, name, data);
188 }
189
190 closedir(dir);
191 }
192
get_source_plugins_dir(void)193 static char *get_source_plugins_dir(void)
194 {
195 char *p, path[PATH_MAX+1];
196 int ret;
197
198 ret = readlink("/proc/self/exe", path, PATH_MAX);
199 if (ret > PATH_MAX || ret < 0)
200 return NULL;
201
202 path[ret] = 0;
203 dirname(path);
204 p = strrchr(path, '/');
205 if (!p)
206 return NULL;
207 /* Check if we are in the the source tree */
208 if (strcmp(p, "/tracecmd") != 0)
209 return NULL;
210
211 strcpy(p, "/lib/trace-cmd/plugins");
212 return strdup(path);
213 }
214
215 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)216 load_plugins_hook(struct trace_plugin_context *trace, const char *suffix,
217 void (*load_plugin)(struct trace_plugin_context *trace,
218 const char *path,
219 const char *name,
220 void *data),
221 void *data)
222 {
223 char *home;
224 char *path;
225 char *envdir;
226 int ret;
227
228 if (trace && trace->flags & TRACECMD_DISABLE_PLUGINS)
229 return;
230
231 /*
232 * If a system plugin directory was defined,
233 * check that first.
234 */
235 #ifdef PLUGIN_TRACECMD_DIR
236 if (!trace || !(trace->flags & TRACECMD_DISABLE_SYS_PLUGINS))
237 load_plugins_dir(trace, suffix, PLUGIN_TRACECMD_DIR,
238 load_plugin, data);
239 #endif
240
241 /*
242 * Next let the environment-set plugin directory
243 * override the system defaults.
244 */
245 envdir = getenv("TRACECMD_PLUGIN_DIR");
246 if (envdir)
247 load_plugins_dir(trace, suffix, envdir, load_plugin, data);
248
249 /*
250 * Now let the home directory override the environment
251 * or system defaults.
252 */
253 home = getenv("HOME");
254 if (!home)
255 return;
256
257 ret = asprintf(&path, "%s/%s", home, LOCAL_PLUGIN_DIR);
258 if (ret < 0) {
259 tracecmd_warning("could not allocate plugin memory");
260 return;
261 }
262
263 load_plugins_dir(trace, suffix, path, load_plugin, data);
264
265 free(path);
266
267 path = get_source_plugins_dir();
268 if (path) {
269 load_plugins_dir(trace, suffix, path, load_plugin, data);
270 free(path);
271 }
272 }
273
274 /**
275 * tracecmd_load_plugins - Load trace-cmd specific plugins.
276 * @context: Context of the trace-cmd command, will be passed to the plugins
277 * at load time.
278 *
279 * Returns a list of loaded plugins
280 */
281 struct trace_plugin_list*
tracecmd_load_plugins(struct trace_plugin_context * trace)282 tracecmd_load_plugins(struct trace_plugin_context *trace)
283 {
284 struct trace_plugin_list *list = NULL;
285
286 load_plugins_hook(trace, ".so", load_plugin, &list);
287 return list;
288 }
289
290 /**
291 * tracecmd_unload_plugins - Unload trace-cmd specific plugins.
292 * @plugin_list - List of plugins, previously loaded with tracecmd_load_plugins.
293 * @context: Context of the trace-cmd command, will be passed to the plugins
294 * at unload time.
295 *
296 */
297 void
tracecmd_unload_plugins(struct trace_plugin_list * plugin_list,struct trace_plugin_context * trace)298 tracecmd_unload_plugins(struct trace_plugin_list *plugin_list,
299 struct trace_plugin_context *trace)
300 {
301 tracecmd_plugin_unload_func func;
302 struct trace_plugin_list *list;
303
304 while (plugin_list) {
305 list = plugin_list;
306 plugin_list = list->next;
307 func = dlsym(list->handle, TRACECMD_PLUGIN_UNLOADER_NAME);
308 if (func)
309 func(trace);
310 dlclose(list->handle);
311 free(list->name);
312 free(list);
313 }
314 }
315