Move utils_expand_path and utils_expand_path_keep_symlink to libpath.la
[lttng-tools.git] / src / bin / lttng / uprobe.cpp
CommitLineData
0c4e727b
JR
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"
4971b7f0 17#include "common/path.h"
0c4e727b
JR
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 */
31static
32int 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 */
48a40005 61 tentative_binary_path = (char *) zmalloc(LTTNG_PATH_MAX * sizeof(char));
0c4e727b
JR
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
126free_binary_path:
127 free(tentative_binary_path);
128alloc_error:
129 free(command_search_path);
130end:
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 */
147static
148int 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
190error:
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 */
0c4e727b
JR
199int parse_userspace_probe_opts(const char *opt,
200 struct lttng_userspace_probe_location **probe_location)
201{
202 int ret = CMD_SUCCESS;
e358ddd5 203 size_t num_token = 0;
0c4e727b
JR
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;
e358ddd5 210 struct lttng_dynamic_pointer_array tokens;
0c4e727b 211
a0377dfe 212 LTTNG_ASSERT(opt);
0c4e727b
JR
213
214 /*
215 * userspace probe fields are separated by ':'.
216 */
e358ddd5
JG
217 ret = strutils_split(opt, ':', true, &tokens);
218 if (ret == 0) {
219 num_token = lttng_dynamic_pointer_array_get_count(&tokens);
220 }
0c4e727b
JR
221
222 /*
223 * Early sanity check that the number of parameter is between 2 and 4
224 * inclusively.
225 * elf:PATH:SYMBOL
226 * std:PATH:PROVIDER_NAME:PROBE_NAME
227 * PATH:SYMBOL (same behavior as ELF)
228 */
e358ddd5 229 if (ret < 0 || num_token < 2 || num_token > 4) {
0c4e727b
JR
230 ret = CMD_ERROR;
231 goto end;
232 }
233
234 /*
235 * Looking up the first parameter will tell the technique to use to
236 * interpret the userspace probe/function description.
237 */
238 switch (num_token) {
239 case 2:
240 /* When the probe type is omitted we assume ELF for now. */
241 case 3:
48a40005
SM
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);
0c4e727b 245 } else if (num_token == 2) {
48a40005
SM
246 target_path = (char *) lttng_dynamic_pointer_array_get_pointer(&tokens, 0);
247 symbol_name = (char *) lttng_dynamic_pointer_array_get_pointer(&tokens, 1);
0c4e727b
JR
248 } else {
249 ret = CMD_ERROR;
250 goto end;
251 }
252 lookup_method =
253 lttng_userspace_probe_location_lookup_method_function_elf_create();
254 if (!lookup_method) {
255 WARN("Failed to create ELF lookup method");
256 ret = CMD_ERROR;
257 goto end;
258 }
259 break;
260 case 4:
48a40005
SM
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);
0c4e727b
JR
265 } else {
266 ret = CMD_ERROR;
267 goto end;
268 }
269 lookup_method =
270 lttng_userspace_probe_location_lookup_method_tracepoint_sdt_create();
271 if (!lookup_method) {
272 WARN("Failed to create SDT lookup method");
273 ret = CMD_ERROR;
274 goto end;
275 }
276 break;
277 default:
278 ret = CMD_ERROR;
279 goto end;
280 }
281
282 /* strutils_unescape_string allocates a new char *. */
283 unescaped_target_path = strutils_unescape_string(target_path, 0);
284 if (!unescaped_target_path) {
285 ret = CMD_ERROR;
286 goto end;
287 }
288
289 /*
290 * If there is not forward slash in the path. Walk the $PATH else
291 * expand.
292 */
293 if (strchr(unescaped_target_path, '/') == NULL) {
294 /* Walk the $PATH variable to find the targeted binary. */
48a40005 295 real_target_path = (char *) zmalloc(LTTNG_PATH_MAX * sizeof(char));
0c4e727b
JR
296 if (!real_target_path) {
297 PERROR("Error allocating path buffer");
298 ret = CMD_ERROR;
299 goto end;
300 }
301 ret = walk_command_search_path(unescaped_target_path, real_target_path);
302 if (ret) {
303 ERR("Binary not found.");
304 ret = CMD_ERROR;
305 goto end;
306 }
307 } else {
308 /*
309 * Expand references to `/./` and `/../`. This function does not check
310 * if the file exists. This call returns an allocated buffer on
311 * success.
312 */
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.");
316 ret = CMD_ERROR;
317 goto end;
318 }
319
320 /*
321 * Check if the file exists using access(2), If it does not,
322 * return an error.
323 */
324 ret = access(real_target_path, F_OK);
325 if (ret) {
326 ERR("Cannot find binary at path: %s.", real_target_path);
327 ret = CMD_ERROR;
328 goto end;
329 }
330 }
331
332 switch (lttng_userspace_probe_location_lookup_method_get_type(lookup_method)) {
333 case LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_FUNCTION_ELF:
334 /*
335 * Check for common mistakes in userspace probe description syntax.
336 */
337 ret = warn_userspace_probe_syntax(symbol_name);
338 if (ret) {
339 goto end;
340 }
341
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");
346 ret = CMD_ERROR;
347 goto end;
348 }
349
350 /* Ownership transferred to probe_location. */
351 lookup_method = NULL;
352 break;
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");
358 ret = CMD_ERROR;
359 goto end;
360 }
361
362 /* Ownership transferred to probe_location. */
363 lookup_method = NULL;
364 break;
365 default:
366 ret = CMD_ERROR;
367 goto end;
368 }
369
370 /*
371 * Everything went fine, transfer ownership of probe location to
372 * caller.
373 */
374 *probe_location = probe_location_local;
375 probe_location_local = NULL;
376
377end:
378 lttng_userspace_probe_location_destroy(probe_location_local);
379 lttng_userspace_probe_location_lookup_method_destroy(lookup_method);
e358ddd5 380 lttng_dynamic_pointer_array_reset(&tokens);
0c4e727b
JR
381 /*
382 * Freeing both char * here makes the error handling simplier. free()
383 * performs not action if the pointer is NULL.
384 */
385 free(real_target_path);
386 free(unescaped_target_path);
387
388 return ret;
389}
This page took 0.042193 seconds and 4 git commands to generate.