xref: /aosp_15_r20/external/trace-cmd/lib/trace-cmd/trace-plugin.c (revision 58e6ee5f017f6a8912852c892d18457e4bafb554)
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