• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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