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 "lttng/constant.h"
22 * Walk the directories in the PATH environment variable to find the target
23 * binary passed as parameter.
25 * On success, the full path of the binary is copied in binary_full_path out
26 * parameter. This buffer is allocated by the caller and must be at least
27 * LTTNG_PATH_MAX bytes long.
28 * On failure, returns -1;
31 int walk_command_search_path(const char *binary
, char *binary_full_path
)
33 char *tentative_binary_path
= NULL
;
34 char *command_search_path
= NULL
;
35 char *curr_search_dir_end
= NULL
;
36 char *curr_search_dir
= NULL
;
37 struct stat stat_output
;
40 command_search_path
= lttng_secure_getenv("PATH");
41 if (!command_search_path
) {
47 * Duplicate the $PATH string as the char pointer returned by getenv() should
50 command_search_path
= strdup(command_search_path
);
51 if (!command_search_path
) {
57 * This char array is used to concatenate path to binary to look for
60 tentative_binary_path
= zmalloc(LTTNG_PATH_MAX
* sizeof(char));
61 if (!tentative_binary_path
) {
66 curr_search_dir
= command_search_path
;
69 * Split on ':'. The return value of this call points to the
72 curr_search_dir_end
= strchr(curr_search_dir
, ':');
73 if (curr_search_dir_end
!= NULL
) {
75 * Add a NULL byte to the end of the first token so it
76 * can be used as a string.
78 curr_search_dir_end
[0] = '\0';
81 /* Empty the tentative path */
82 memset(tentative_binary_path
, 0, LTTNG_PATH_MAX
* sizeof(char));
85 * Build the tentative path to the binary using the current
86 * search directory and the name of the binary.
88 ret
= snprintf(tentative_binary_path
, LTTNG_PATH_MAX
, "%s/%s",
89 curr_search_dir
, binary
);
91 goto free_binary_path
;
93 if (ret
< LTTNG_PATH_MAX
) {
95 * Use STAT(2) to see if the file exists.
97 ret
= stat(tentative_binary_path
, &stat_output
);
100 * Verify that it is a regular file or a
101 * symlink and not a special file (e.g.
104 if (S_ISREG(stat_output
.st_mode
)
105 || S_ISLNK(stat_output
.st_mode
)) {
107 * Found a match, set the out parameter
108 * and return success.
110 ret
= lttng_strncpy(binary_full_path
,
111 tentative_binary_path
,
114 ERR("Source path does not fit "
115 "in destination buffer.");
117 goto free_binary_path
;
121 /* Go to the next entry in the $PATH variable. */
122 curr_search_dir
= curr_search_dir_end
+ 1;
123 } while (curr_search_dir_end
!= NULL
);
126 free(tentative_binary_path
);
128 free(command_search_path
);
134 * Check if the symbol field passed by the user is in fact an address or an
135 * offset from a symbol. Those two instrumentation types are not supported yet.
136 * It's expected to be a common mistake because of the existing --probe option
137 * that does support these formats.
139 * Here are examples of these unsupported formats for the --userspace-probe
141 * elf:/path/to/binary:0x400430
142 * elf:/path/to/binary:4194364
143 * elf:/path/to/binary:my_symbol+0x323
144 * elf:/path/to/binary:my_symbol+43
147 int warn_userspace_probe_syntax(const char *symbol
)
151 /* Check if the symbol field is an hex address. */
152 ret
= sscanf(symbol
, "0x%*x");
154 /* If there is a match, print a warning and return an error. */
155 ERR("Userspace probe on address not supported yet.");
156 ret
= CMD_UNSUPPORTED
;
160 /* Check if the symbol field is an decimal address. */
161 ret
= sscanf(symbol
, "%*u");
163 /* If there is a match, print a warning and return an error. */
164 ERR("Userspace probe on address not supported yet.");
165 ret
= CMD_UNSUPPORTED
;
169 /* Check if the symbol field is symbol+hex_offset. */
170 ret
= sscanf(symbol
, "%*[^+]+0x%*x");
172 /* If there is a match, print a warning and return an error. */
173 ERR("Userspace probe on symbol+offset not supported yet.");
174 ret
= CMD_UNSUPPORTED
;
178 /* Check if the symbol field is symbol+decimal_offset. */
179 ret
= sscanf(symbol
, "%*[^+]+%*u");
181 /* If there is a match, print a warning and return an error. */
182 ERR("Userspace probe on symbol+offset not supported yet.");
183 ret
= CMD_UNSUPPORTED
;
194 * Parse userspace probe options
195 * Set the userspace probe fields in the lttng_event struct and set the
196 * target_path to the path to the binary.
199 int parse_userspace_probe_opts(const char *opt
,
200 struct lttng_userspace_probe_location
**probe_location
)
202 int ret
= CMD_SUCCESS
;
203 size_t num_token
= 0;
204 char *target_path
= NULL
;
205 char *unescaped_target_path
= NULL
;
206 char *real_target_path
= NULL
;
207 char *symbol_name
= NULL
, *probe_name
= NULL
, *provider_name
= NULL
;
208 struct lttng_userspace_probe_location
*probe_location_local
= NULL
;
209 struct lttng_userspace_probe_location_lookup_method
*lookup_method
= NULL
;
210 struct lttng_dynamic_pointer_array tokens
;
215 * userspace probe fields are separated by ':'.
217 ret
= strutils_split(opt
, ':', true, &tokens
);
219 num_token
= lttng_dynamic_pointer_array_get_count(&tokens
);
223 * Early sanity check that the number of parameter is between 2 and 4
226 * std:PATH:PROVIDER_NAME:PROBE_NAME
227 * PATH:SYMBOL (same behavior as ELF)
229 if (ret
< 0 || num_token
< 2 || num_token
> 4) {
235 * Looking up the first parameter will tell the technique to use to
236 * interpret the userspace probe/function description.
240 /* When the probe type is omitted we assume ELF for now. */
242 if (num_token
== 3 && strcmp(lttng_dynamic_pointer_array_get_pointer(&tokens
, 0), "elf") == 0) {
243 target_path
= lttng_dynamic_pointer_array_get_pointer(&tokens
, 1);
244 symbol_name
= lttng_dynamic_pointer_array_get_pointer(&tokens
, 2);
245 } else if (num_token
== 2) {
246 target_path
= lttng_dynamic_pointer_array_get_pointer(&tokens
, 0);
247 symbol_name
= lttng_dynamic_pointer_array_get_pointer(&tokens
, 1);
253 lttng_userspace_probe_location_lookup_method_function_elf_create();
254 if (!lookup_method
) {
255 WARN("Failed to create ELF lookup method");
261 if (strcmp(lttng_dynamic_pointer_array_get_pointer(&tokens
, 0), "sdt") == 0) {
262 target_path
= lttng_dynamic_pointer_array_get_pointer(&tokens
, 1);
263 provider_name
= lttng_dynamic_pointer_array_get_pointer(&tokens
, 2);
264 probe_name
= lttng_dynamic_pointer_array_get_pointer(&tokens
, 3);
270 lttng_userspace_probe_location_lookup_method_tracepoint_sdt_create();
271 if (!lookup_method
) {
272 WARN("Failed to create SDT lookup method");
282 /* strutils_unescape_string allocates a new char *. */
283 unescaped_target_path
= strutils_unescape_string(target_path
, 0);
284 if (!unescaped_target_path
) {
290 * If there is not forward slash in the path. Walk the $PATH else
293 if (strchr(unescaped_target_path
, '/') == NULL
) {
294 /* Walk the $PATH variable to find the targeted binary. */
295 real_target_path
= zmalloc(LTTNG_PATH_MAX
* sizeof(char));
296 if (!real_target_path
) {
297 PERROR("Error allocating path buffer");
301 ret
= walk_command_search_path(unescaped_target_path
, real_target_path
);
303 ERR("Binary not found.");
309 * Expand references to `/./` and `/../`. This function does not check
310 * if the file exists. This call returns an allocated buffer on
313 real_target_path
= utils_expand_path_keep_symlink(unescaped_target_path
);
314 if (!real_target_path
) {
315 ERR("Error expanding the path to binary.");
321 * Check if the file exists using access(2), If it does not,
324 ret
= access(real_target_path
, F_OK
);
326 ERR("Cannot find binary at path: %s.", real_target_path
);
332 switch (lttng_userspace_probe_location_lookup_method_get_type(lookup_method
)) {
333 case LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_FUNCTION_ELF
:
335 * Check for common mistakes in userspace probe description syntax.
337 ret
= warn_userspace_probe_syntax(symbol_name
);
342 probe_location_local
= lttng_userspace_probe_location_function_create(
343 real_target_path
, symbol_name
, lookup_method
);
344 if (!probe_location_local
) {
345 WARN("Failed to create function probe location");
350 /* Ownership transferred to probe_location. */
351 lookup_method
= NULL
;
353 case LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_TRACEPOINT_SDT
:
354 probe_location_local
= lttng_userspace_probe_location_tracepoint_create(
355 real_target_path
, provider_name
, probe_name
, lookup_method
);
356 if (!probe_location_local
) {
357 WARN("Failed to create function probe location");
362 /* Ownership transferred to probe_location. */
363 lookup_method
= NULL
;
371 * Everything went fine, transfer ownership of probe location to
374 *probe_location
= probe_location_local
;
375 probe_location_local
= NULL
;
378 lttng_userspace_probe_location_destroy(probe_location_local
);
379 lttng_userspace_probe_location_lookup_method_destroy(lookup_method
);
380 lttng_dynamic_pointer_array_reset(&tokens
);
382 * Freeing both char * here makes the error handling simplier. free()
383 * performs not action if the pointer is NULL.
385 free(real_target_path
);
386 free(unescaped_target_path
);