Move utils_expand_path and utils_expand_path_keep_symlink to libpath.la
[lttng-tools.git] / src / bin / lttng / uprobe.c
1 /*
2 * Copyright (C) 2020 EfficiOS, Inc.
3 *
4 * SPDX-License-Identifier: GPL-2.0-only
5 *
6 */
7
8 #include "uprobe.h"
9
10 #include <sys/types.h>
11 #include <sys/stat.h>
12 #include <unistd.h>
13
14 #include "common/compat/getenv.h"
15 #include "common/string-utils/string-utils.h"
16 #include "common/utils.h"
17 #include "common/path.h"
18 #include "lttng/constant.h"
19
20 #include "command.h"
21
22 /*
23 * Walk the directories in the PATH environment variable to find the target
24 * binary passed as parameter.
25 *
26 * On success, the full path of the binary is copied in binary_full_path out
27 * parameter. This buffer is allocated by the caller and must be at least
28 * LTTNG_PATH_MAX bytes long.
29 * On failure, returns -1;
30 */
31 static
32 int walk_command_search_path(const char *binary, char *binary_full_path)
33 {
34 char *tentative_binary_path = NULL;
35 char *command_search_path = NULL;
36 char *curr_search_dir_end = NULL;
37 char *curr_search_dir = NULL;
38 struct stat stat_output;
39 int ret = 0;
40
41 command_search_path = lttng_secure_getenv("PATH");
42 if (!command_search_path) {
43 ret = -1;
44 goto end;
45 }
46
47 /*
48 * Duplicate the $PATH string as the char pointer returned by getenv() should
49 * not be modified.
50 */
51 command_search_path = strdup(command_search_path);
52 if (!command_search_path) {
53 ret = -1;
54 goto end;
55 }
56
57 /*
58 * This char array is used to concatenate path to binary to look for
59 * the binary.
60 */
61 tentative_binary_path = zmalloc(LTTNG_PATH_MAX * sizeof(char));
62 if (!tentative_binary_path) {
63 ret = -1;
64 goto alloc_error;
65 }
66
67 curr_search_dir = command_search_path;
68 do {
69 /*
70 * Split on ':'. The return value of this call points to the
71 * matching character.
72 */
73 curr_search_dir_end = strchr(curr_search_dir, ':');
74 if (curr_search_dir_end != NULL) {
75 /*
76 * Add a NULL byte to the end of the first token so it
77 * can be used as a string.
78 */
79 curr_search_dir_end[0] = '\0';
80 }
81
82 /* Empty the tentative path */
83 memset(tentative_binary_path, 0, LTTNG_PATH_MAX * sizeof(char));
84
85 /*
86 * Build the tentative path to the binary using the current
87 * search directory and the name of the binary.
88 */
89 ret = snprintf(tentative_binary_path, LTTNG_PATH_MAX, "%s/%s",
90 curr_search_dir, binary);
91 if (ret < 0) {
92 goto free_binary_path;
93 }
94 if (ret < LTTNG_PATH_MAX) {
95 /*
96 * Use STAT(2) to see if the file exists.
97 */
98 ret = stat(tentative_binary_path, &stat_output);
99 if (ret == 0) {
100 /*
101 * Verify that it is a regular file or a
102 * symlink and not a special file (e.g.
103 * device).
104 */
105 if (S_ISREG(stat_output.st_mode)
106 || S_ISLNK(stat_output.st_mode)) {
107 /*
108 * Found a match, set the out parameter
109 * and return success.
110 */
111 ret = lttng_strncpy(binary_full_path,
112 tentative_binary_path,
113 LTTNG_PATH_MAX);
114 if (ret == -1) {
115 ERR("Source path does not fit "
116 "in destination buffer.");
117 }
118 goto free_binary_path;
119 }
120 }
121 }
122 /* Go to the next entry in the $PATH variable. */
123 curr_search_dir = curr_search_dir_end + 1;
124 } while (curr_search_dir_end != NULL);
125
126 free_binary_path:
127 free(tentative_binary_path);
128 alloc_error:
129 free(command_search_path);
130 end:
131 return ret;
132 }
133
134 /*
135 * Check if the symbol field passed by the user is in fact an address or an
136 * offset from a symbol. Those two instrumentation types are not supported yet.
137 * It's expected to be a common mistake because of the existing --probe option
138 * that does support these formats.
139 *
140 * Here are examples of these unsupported formats for the --userspace-probe
141 * option:
142 * elf:/path/to/binary:0x400430
143 * elf:/path/to/binary:4194364
144 * elf:/path/to/binary:my_symbol+0x323
145 * elf:/path/to/binary:my_symbol+43
146 */
147 static
148 int warn_userspace_probe_syntax(const char *symbol)
149 {
150 int ret;
151
152 /* Check if the symbol field is an hex address. */
153 ret = sscanf(symbol, "0x%*x");
154 if (ret > 0) {
155 /* If there is a match, print a warning and return an error. */
156 ERR("Userspace probe on address not supported yet.");
157 ret = CMD_UNSUPPORTED;
158 goto error;
159 }
160
161 /* Check if the symbol field is an decimal address. */
162 ret = sscanf(symbol, "%*u");
163 if (ret > 0) {
164 /* If there is a match, print a warning and return an error. */
165 ERR("Userspace probe on address not supported yet.");
166 ret = CMD_UNSUPPORTED;
167 goto error;
168 }
169
170 /* Check if the symbol field is symbol+hex_offset. */
171 ret = sscanf(symbol, "%*[^+]+0x%*x");
172 if (ret > 0) {
173 /* If there is a match, print a warning and return an error. */
174 ERR("Userspace probe on symbol+offset not supported yet.");
175 ret = CMD_UNSUPPORTED;
176 goto error;
177 }
178
179 /* Check if the symbol field is symbol+decimal_offset. */
180 ret = sscanf(symbol, "%*[^+]+%*u");
181 if (ret > 0) {
182 /* If there is a match, print a warning and return an error. */
183 ERR("Userspace probe on symbol+offset not supported yet.");
184 ret = CMD_UNSUPPORTED;
185 goto error;
186 }
187
188 ret = 0;
189
190 error:
191 return ret;
192 }
193
194 /*
195 * Parse userspace probe options
196 * Set the userspace probe fields in the lttng_event struct and set the
197 * target_path to the path to the binary.
198 */
199 LTTNG_HIDDEN
200 int parse_userspace_probe_opts(const char *opt,
201 struct lttng_userspace_probe_location **probe_location)
202 {
203 int ret = CMD_SUCCESS;
204 size_t num_token = 0;
205 char *target_path = NULL;
206 char *unescaped_target_path = NULL;
207 char *real_target_path = NULL;
208 char *symbol_name = NULL, *probe_name = NULL, *provider_name = NULL;
209 struct lttng_userspace_probe_location *probe_location_local = NULL;
210 struct lttng_userspace_probe_location_lookup_method *lookup_method = NULL;
211 struct lttng_dynamic_pointer_array tokens;
212
213 assert(opt);
214
215 /*
216 * userspace probe fields are separated by ':'.
217 */
218 ret = strutils_split(opt, ':', true, &tokens);
219 if (ret == 0) {
220 num_token = lttng_dynamic_pointer_array_get_count(&tokens);
221 }
222
223 /*
224 * Early sanity check that the number of parameter is between 2 and 4
225 * inclusively.
226 * elf:PATH:SYMBOL
227 * std:PATH:PROVIDER_NAME:PROBE_NAME
228 * PATH:SYMBOL (same behavior as ELF)
229 */
230 if (ret < 0 || num_token < 2 || num_token > 4) {
231 ret = CMD_ERROR;
232 goto end;
233 }
234
235 /*
236 * Looking up the first parameter will tell the technique to use to
237 * interpret the userspace probe/function description.
238 */
239 switch (num_token) {
240 case 2:
241 /* When the probe type is omitted we assume ELF for now. */
242 case 3:
243 if (num_token == 3 && strcmp(lttng_dynamic_pointer_array_get_pointer(&tokens, 0), "elf") == 0) {
244 target_path = lttng_dynamic_pointer_array_get_pointer(&tokens, 1);
245 symbol_name = lttng_dynamic_pointer_array_get_pointer(&tokens, 2);
246 } else if (num_token == 2) {
247 target_path = lttng_dynamic_pointer_array_get_pointer(&tokens, 0);
248 symbol_name = lttng_dynamic_pointer_array_get_pointer(&tokens, 1);
249 } else {
250 ret = CMD_ERROR;
251 goto end;
252 }
253 lookup_method =
254 lttng_userspace_probe_location_lookup_method_function_elf_create();
255 if (!lookup_method) {
256 WARN("Failed to create ELF lookup method");
257 ret = CMD_ERROR;
258 goto end;
259 }
260 break;
261 case 4:
262 if (strcmp(lttng_dynamic_pointer_array_get_pointer(&tokens, 0), "sdt") == 0) {
263 target_path = lttng_dynamic_pointer_array_get_pointer(&tokens, 1);
264 provider_name = lttng_dynamic_pointer_array_get_pointer(&tokens, 2);
265 probe_name = lttng_dynamic_pointer_array_get_pointer(&tokens, 3);
266 } else {
267 ret = CMD_ERROR;
268 goto end;
269 }
270 lookup_method =
271 lttng_userspace_probe_location_lookup_method_tracepoint_sdt_create();
272 if (!lookup_method) {
273 WARN("Failed to create SDT lookup method");
274 ret = CMD_ERROR;
275 goto end;
276 }
277 break;
278 default:
279 ret = CMD_ERROR;
280 goto end;
281 }
282
283 /* strutils_unescape_string allocates a new char *. */
284 unescaped_target_path = strutils_unescape_string(target_path, 0);
285 if (!unescaped_target_path) {
286 ret = CMD_ERROR;
287 goto end;
288 }
289
290 /*
291 * If there is not forward slash in the path. Walk the $PATH else
292 * expand.
293 */
294 if (strchr(unescaped_target_path, '/') == NULL) {
295 /* Walk the $PATH variable to find the targeted binary. */
296 real_target_path = zmalloc(LTTNG_PATH_MAX * sizeof(char));
297 if (!real_target_path) {
298 PERROR("Error allocating path buffer");
299 ret = CMD_ERROR;
300 goto end;
301 }
302 ret = walk_command_search_path(unescaped_target_path, real_target_path);
303 if (ret) {
304 ERR("Binary not found.");
305 ret = CMD_ERROR;
306 goto end;
307 }
308 } else {
309 /*
310 * Expand references to `/./` and `/../`. This function does not check
311 * if the file exists. This call returns an allocated buffer on
312 * success.
313 */
314 real_target_path = utils_expand_path_keep_symlink(unescaped_target_path);
315 if (!real_target_path) {
316 ERR("Error expanding the path to binary.");
317 ret = CMD_ERROR;
318 goto end;
319 }
320
321 /*
322 * Check if the file exists using access(2), If it does not,
323 * return an error.
324 */
325 ret = access(real_target_path, F_OK);
326 if (ret) {
327 ERR("Cannot find binary at path: %s.", real_target_path);
328 ret = CMD_ERROR;
329 goto end;
330 }
331 }
332
333 switch (lttng_userspace_probe_location_lookup_method_get_type(lookup_method)) {
334 case LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_FUNCTION_ELF:
335 /*
336 * Check for common mistakes in userspace probe description syntax.
337 */
338 ret = warn_userspace_probe_syntax(symbol_name);
339 if (ret) {
340 goto end;
341 }
342
343 probe_location_local = lttng_userspace_probe_location_function_create(
344 real_target_path, symbol_name, lookup_method);
345 if (!probe_location_local) {
346 WARN("Failed to create function probe location");
347 ret = CMD_ERROR;
348 goto end;
349 }
350
351 /* Ownership transferred to probe_location. */
352 lookup_method = NULL;
353 break;
354 case LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_TRACEPOINT_SDT:
355 probe_location_local = lttng_userspace_probe_location_tracepoint_create(
356 real_target_path, provider_name, probe_name, lookup_method);
357 if (!probe_location_local) {
358 WARN("Failed to create function probe location");
359 ret = CMD_ERROR;
360 goto end;
361 }
362
363 /* Ownership transferred to probe_location. */
364 lookup_method = NULL;
365 break;
366 default:
367 ret = CMD_ERROR;
368 goto end;
369 }
370
371 /*
372 * Everything went fine, transfer ownership of probe location to
373 * caller.
374 */
375 *probe_location = probe_location_local;
376 probe_location_local = NULL;
377
378 end:
379 lttng_userspace_probe_location_destroy(probe_location_local);
380 lttng_userspace_probe_location_lookup_method_destroy(lookup_method);
381 lttng_dynamic_pointer_array_reset(&tokens);
382 /*
383 * Freeing both char * here makes the error handling simplier. free()
384 * performs not action if the pointer is NULL.
385 */
386 free(real_target_path);
387 free(unescaped_target_path);
388
389 return ret;
390 }
This page took 0.037858 seconds and 4 git commands to generate.