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
= (char *) 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.
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((const char *) lttng_dynamic_pointer_array_get_pointer(&tokens
, 0), "elf") == 0) {
243 target_path
= (char *) lttng_dynamic_pointer_array_get_pointer(&tokens
, 1);
244 symbol_name
= (char *) lttng_dynamic_pointer_array_get_pointer(&tokens
, 2);
245 } else if (num_token
== 2) {
246 target_path
= (char *) lttng_dynamic_pointer_array_get_pointer(&tokens
, 0);
247 symbol_name
= (char *) 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((const char *) lttng_dynamic_pointer_array_get_pointer(&tokens
, 0), "sdt") == 0) {
262 target_path
= (char *) lttng_dynamic_pointer_array_get_pointer(&tokens
, 1);
263 provider_name
= (char *) lttng_dynamic_pointer_array_get_pointer(&tokens
, 2);
264 probe_name
= (char *) 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
= (char *) 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
);