2 * Copyright (C) 2020 EfficiOS, Inc.
4 * SPDX-License-Identifier: GPL-2.0-only
10 #include <sys/types.h>
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"
23 * Walk the directories in the PATH environment variable to find the target
24 * binary passed as parameter.
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;
32 int walk_command_search_path(const char *binary
, char *binary_full_path
)
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
;
41 command_search_path
= lttng_secure_getenv("PATH");
42 if (!command_search_path
) {
48 * Duplicate the $PATH string as the char pointer returned by getenv() should
51 command_search_path
= strdup(command_search_path
);
52 if (!command_search_path
) {
58 * This char array is used to concatenate path to binary to look for
61 tentative_binary_path
= zmalloc(LTTNG_PATH_MAX
* sizeof(char));
62 if (!tentative_binary_path
) {
67 curr_search_dir
= command_search_path
;
70 * Split on ':'. The return value of this call points to the
73 curr_search_dir_end
= strchr(curr_search_dir
, ':');
74 if (curr_search_dir_end
!= NULL
) {
76 * Add a NULL byte to the end of the first token so it
77 * can be used as a string.
79 curr_search_dir_end
[0] = '\0';
82 /* Empty the tentative path */
83 memset(tentative_binary_path
, 0, LTTNG_PATH_MAX
* sizeof(char));
86 * Build the tentative path to the binary using the current
87 * search directory and the name of the binary.
89 ret
= snprintf(tentative_binary_path
, LTTNG_PATH_MAX
, "%s/%s",
90 curr_search_dir
, binary
);
92 goto free_binary_path
;
94 if (ret
< LTTNG_PATH_MAX
) {
96 * Use STAT(2) to see if the file exists.
98 ret
= stat(tentative_binary_path
, &stat_output
);
101 * Verify that it is a regular file or a
102 * symlink and not a special file (e.g.
105 if (S_ISREG(stat_output
.st_mode
)
106 || S_ISLNK(stat_output
.st_mode
)) {
108 * Found a match, set the out parameter
109 * and return success.
111 ret
= lttng_strncpy(binary_full_path
,
112 tentative_binary_path
,
115 ERR("Source path does not fit "
116 "in destination buffer.");
118 goto free_binary_path
;
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
);
127 free(tentative_binary_path
);
129 free(command_search_path
);
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.
140 * Here are examples of these unsupported formats for the --userspace-probe
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
148 int warn_userspace_probe_syntax(const char *symbol
)
152 /* Check if the symbol field is an hex address. */
153 ret
= sscanf(symbol
, "0x%*x");
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
;
161 /* Check if the symbol field is an decimal address. */
162 ret
= sscanf(symbol
, "%*u");
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
;
170 /* Check if the symbol field is symbol+hex_offset. */
171 ret
= sscanf(symbol
, "%*[^+]+0x%*x");
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
;
179 /* Check if the symbol field is symbol+decimal_offset. */
180 ret
= sscanf(symbol
, "%*[^+]+%*u");
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
;
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.
200 int parse_userspace_probe_opts(const char *opt
,
201 struct lttng_userspace_probe_location
**probe_location
)
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
;
216 * userspace probe fields are separated by ':'.
218 ret
= strutils_split(opt
, ':', true, &tokens
);
220 num_token
= lttng_dynamic_pointer_array_get_count(&tokens
);
224 * Early sanity check that the number of parameter is between 2 and 4
227 * std:PATH:PROVIDER_NAME:PROBE_NAME
228 * PATH:SYMBOL (same behavior as ELF)
230 if (ret
< 0 || num_token
< 2 || num_token
> 4) {
236 * Looking up the first parameter will tell the technique to use to
237 * interpret the userspace probe/function description.
241 /* When the probe type is omitted we assume ELF for now. */
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);
254 lttng_userspace_probe_location_lookup_method_function_elf_create();
255 if (!lookup_method
) {
256 WARN("Failed to create ELF lookup method");
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);
271 lttng_userspace_probe_location_lookup_method_tracepoint_sdt_create();
272 if (!lookup_method
) {
273 WARN("Failed to create SDT lookup method");
283 /* strutils_unescape_string allocates a new char *. */
284 unescaped_target_path
= strutils_unescape_string(target_path
, 0);
285 if (!unescaped_target_path
) {
291 * If there is not forward slash in the path. Walk the $PATH else
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");
302 ret
= walk_command_search_path(unescaped_target_path
, real_target_path
);
304 ERR("Binary not found.");
310 * Expand references to `/./` and `/../`. This function does not check
311 * if the file exists. This call returns an allocated buffer on
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.");
322 * Check if the file exists using access(2), If it does not,
325 ret
= access(real_target_path
, F_OK
);
327 ERR("Cannot find binary at path: %s.", real_target_path
);
333 switch (lttng_userspace_probe_location_lookup_method_get_type(lookup_method
)) {
334 case LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_FUNCTION_ELF
:
336 * Check for common mistakes in userspace probe description syntax.
338 ret
= warn_userspace_probe_syntax(symbol_name
);
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");
351 /* Ownership transferred to probe_location. */
352 lookup_method
= NULL
;
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");
363 /* Ownership transferred to probe_location. */
364 lookup_method
= NULL
;
372 * Everything went fine, transfer ownership of probe location to
375 *probe_location
= probe_location_local
;
376 probe_location_local
= NULL
;
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
);
383 * Freeing both char * here makes the error handling simplier. free()
384 * performs not action if the pointer is NULL.
386 free(real_target_path
);
387 free(unescaped_target_path
);