Commit | Line | Data |
---|---|---|
dd392e94 | 1 | /* |
21cf9b6b | 2 | * Copyright (C) 2011 EfficiOS Inc. |
dd392e94 FD |
3 | * Copyright (C) 2014 Mathieu Desnoyers <mathieu.desnoyers@efficios.com> |
4 | * Copyright (C) 2020 Francis Deslauriers <francis.deslauriers@efficios.com> | |
5 | * | |
c922647d | 6 | * SPDX-License-Identifier: LGPL-2.1-only |
dd392e94 FD |
7 | * |
8 | */ | |
9 | ||
28ab034a JG |
10 | #include "error.hpp" |
11 | #include "macros.hpp" | |
12 | #include "spawn-viewer.hpp" | |
13 | ||
14 | #include <common/compat/errno.hpp> | |
15 | ||
16 | #include <lttng/constant.h> | |
17 | ||
dd392e94 FD |
18 | #include <stdbool.h> |
19 | #include <sys/stat.h> | |
20 | #include <sys/types.h> | |
21 | #include <unistd.h> | |
dd392e94 | 22 | |
dd392e94 FD |
23 | /* |
24 | * Type is also use as the index in the viewers array. So please, make sure | |
25 | * your enum value is in the right order in the array below. | |
26 | */ | |
27 | enum viewer_type { | |
28ab034a JG |
28 | VIEWER_BABELTRACE = 0, |
29 | VIEWER_BABELTRACE2 = 1, | |
30 | VIEWER_USER_DEFINED = 2, | |
dd392e94 FD |
31 | }; |
32 | ||
f1494934 JG |
33 | namespace { |
34 | const char *babeltrace_bin = CONFIG_BABELTRACE_BIN; | |
35 | const char *babeltrace2_bin = CONFIG_BABELTRACE2_BIN; | |
36 | ||
37 | /* | |
38 | * This is needed for each viewer since we are using execvp(). | |
39 | */ | |
40 | const char *babeltrace_opts[] = { "babeltrace" }; | |
41 | const char *babeltrace2_opts[] = { "babeltrace2" }; | |
42 | ||
43 | const struct viewer { | |
dd392e94 FD |
44 | const char *exec_name; |
45 | enum viewer_type type; | |
46 | } viewers[] = { | |
47 | { "babeltrace", VIEWER_BABELTRACE }, | |
48 | { "babeltrace2", VIEWER_BABELTRACE2 }, | |
cd9adb8b | 49 | { nullptr, VIEWER_USER_DEFINED }, |
dd392e94 | 50 | }; |
f1494934 | 51 | } /* namespace */ |
dd392e94 FD |
52 | |
53 | static const struct viewer *parse_viewer_option(const char *opt_viewer) | |
54 | { | |
cd9adb8b | 55 | if (opt_viewer == nullptr) { |
dd392e94 FD |
56 | /* Default is babeltrace2 */ |
57 | return &(viewers[VIEWER_BABELTRACE2]); | |
58 | } | |
59 | ||
60 | return &(viewers[VIEWER_USER_DEFINED]); | |
61 | } | |
62 | ||
63 | /* | |
64 | * Alloc an array of string pointer from a simple string having all options | |
65 | * seperated by spaces. Also adds the trace path to the arguments. | |
66 | * | |
67 | * The returning pointer is ready to be passed to execvp(). | |
68 | */ | |
69 | static char **alloc_argv_from_user_opts(char *opts, const char *trace_path) | |
70 | { | |
71 | int i = 0, ignore_space = 0; | |
72 | unsigned int num_opts = 1; | |
cd9adb8b | 73 | char **argv, *token = opts, *saveptr = nullptr; |
dd392e94 FD |
74 | |
75 | /* Count number of arguments. */ | |
76 | do { | |
77 | if (*token == ' ') { | |
78 | /* Use to ignore consecutive spaces */ | |
79 | if (!ignore_space) { | |
80 | num_opts++; | |
81 | } | |
82 | ignore_space = 1; | |
83 | } else { | |
84 | ignore_space = 0; | |
85 | } | |
86 | token++; | |
87 | } while (*token != '\0'); | |
88 | ||
89 | /* Add two here for the NULL terminating element and trace path */ | |
64803277 | 90 | argv = calloc<char *>(num_opts + 2); |
cd9adb8b | 91 | if (argv == nullptr) { |
dd392e94 FD |
92 | goto error; |
93 | } | |
94 | ||
77368158 | 95 | token = strtok_r(opts, " ", &saveptr); |
cd9adb8b | 96 | while (token != nullptr) { |
dd392e94 | 97 | argv[i] = strdup(token); |
cd9adb8b | 98 | if (argv[i] == nullptr) { |
dd392e94 FD |
99 | goto error; |
100 | } | |
cd9adb8b | 101 | token = strtok_r(nullptr, " ", &saveptr); |
dd392e94 FD |
102 | i++; |
103 | } | |
104 | ||
105 | argv[num_opts] = (char *) trace_path; | |
cd9adb8b | 106 | argv[num_opts + 1] = nullptr; |
dd392e94 FD |
107 | |
108 | return argv; | |
109 | ||
110 | error: | |
111 | if (argv) { | |
112 | for (i = 0; i < num_opts + 2; i++) { | |
113 | free(argv[i]); | |
114 | } | |
115 | free(argv); | |
116 | } | |
117 | ||
cd9adb8b | 118 | return nullptr; |
dd392e94 FD |
119 | } |
120 | ||
121 | /* | |
122 | * Alloc an array of string pointer from an array of strings. It also adds | |
123 | * the trace path to the argv. | |
124 | * | |
125 | * The returning pointer is ready to be passed to execvp(). | |
126 | */ | |
28ab034a JG |
127 | static char **alloc_argv_from_local_opts(const char **opts, |
128 | size_t opts_len, | |
129 | const char *trace_path, | |
130 | bool opt_live_mode) | |
dd392e94 FD |
131 | { |
132 | char **argv; | |
64803277 | 133 | size_t mem_len; |
dd392e94 FD |
134 | |
135 | /* Add one for the NULL terminating element. */ | |
136 | mem_len = opts_len + 1; | |
137 | if (opt_live_mode) { | |
138 | /* Add 3 option for the live mode being "-i lttng-live URL". */ | |
139 | mem_len += 3; | |
140 | } else { | |
141 | /* Add option for the trace path. */ | |
142 | mem_len += 1; | |
143 | } | |
144 | ||
64803277 | 145 | argv = calloc<char *>(mem_len); |
cd9adb8b | 146 | if (argv == nullptr) { |
dd392e94 FD |
147 | goto error; |
148 | } | |
149 | ||
150 | memcpy(argv, opts, sizeof(char *) * opts_len); | |
151 | ||
152 | if (opt_live_mode) { | |
153 | argv[opts_len] = (char *) "-i"; | |
154 | argv[opts_len + 1] = (char *) "lttng-live"; | |
155 | argv[opts_len + 2] = (char *) trace_path; | |
cd9adb8b | 156 | argv[opts_len + 3] = nullptr; |
dd392e94 FD |
157 | } else { |
158 | argv[opts_len] = (char *) trace_path; | |
cd9adb8b | 159 | argv[opts_len + 1] = nullptr; |
dd392e94 FD |
160 | } |
161 | ||
162 | error: | |
163 | return argv; | |
164 | } | |
165 | ||
dd392e94 FD |
166 | /* |
167 | * Spawn viewer with the trace directory path. | |
168 | */ | |
169 | int spawn_viewer(const char *trace_path, char *opt_viewer, bool opt_live_mode) | |
170 | { | |
171 | int ret = 0; | |
172 | struct stat status; | |
cd9adb8b | 173 | const char *viewer_bin = nullptr; |
dd392e94 | 174 | const struct viewer *viewer; |
cd9adb8b | 175 | char **argv = nullptr; |
dd392e94 FD |
176 | |
177 | /* Check for --viewer option. */ | |
178 | viewer = parse_viewer_option(opt_viewer); | |
cd9adb8b | 179 | if (viewer == nullptr) { |
dd392e94 FD |
180 | ret = -1; |
181 | goto error; | |
182 | } | |
183 | ||
184 | retry_viewer: | |
185 | switch (viewer->type) { | |
186 | case VIEWER_BABELTRACE2: | |
187 | if (stat(babeltrace2_bin, &status) == 0) { | |
188 | viewer_bin = babeltrace2_bin; | |
189 | } else { | |
190 | viewer_bin = viewer->exec_name; | |
191 | } | |
28ab034a JG |
192 | argv = alloc_argv_from_local_opts( |
193 | babeltrace2_opts, ARRAY_SIZE(babeltrace2_opts), trace_path, opt_live_mode); | |
dd392e94 FD |
194 | break; |
195 | case VIEWER_BABELTRACE: | |
196 | if (stat(babeltrace_bin, &status) == 0) { | |
197 | viewer_bin = babeltrace_bin; | |
198 | } else { | |
199 | viewer_bin = viewer->exec_name; | |
200 | } | |
28ab034a JG |
201 | argv = alloc_argv_from_local_opts( |
202 | babeltrace_opts, ARRAY_SIZE(babeltrace_opts), trace_path, opt_live_mode); | |
dd392e94 FD |
203 | break; |
204 | case VIEWER_USER_DEFINED: | |
205 | argv = alloc_argv_from_user_opts(opt_viewer, trace_path); | |
206 | if (argv) { | |
207 | viewer_bin = argv[0]; | |
208 | } | |
209 | break; | |
210 | default: | |
211 | abort(); | |
212 | } | |
213 | ||
cd9adb8b | 214 | if (argv == nullptr || !viewer_bin) { |
dd392e94 FD |
215 | ret = -1; |
216 | goto error; | |
217 | } | |
218 | ||
219 | DBG("Using %s viewer", viewer_bin); | |
220 | ||
221 | ret = execvp(viewer_bin, argv); | |
222 | if (ret) { | |
223 | if (errno == ENOENT && viewer->exec_name) { | |
224 | if (viewer->type == VIEWER_BABELTRACE2) { | |
225 | /* Fallback to legacy babeltrace. */ | |
226 | DBG("Default viewer \"%s\" not installed on the system, falling back to \"%s\"", | |
28ab034a JG |
227 | viewers[VIEWER_BABELTRACE2].exec_name, |
228 | viewers[VIEWER_BABELTRACE].exec_name); | |
dd392e94 FD |
229 | viewer = &viewers[VIEWER_BABELTRACE]; |
230 | free(argv); | |
cd9adb8b | 231 | argv = nullptr; |
dd392e94 FD |
232 | goto retry_viewer; |
233 | } else { | |
234 | ERR("Default viewer \"%s\" (and fallback \"%s\") not found on the system", | |
28ab034a JG |
235 | viewers[VIEWER_BABELTRACE2].exec_name, |
236 | viewers[VIEWER_BABELTRACE].exec_name); | |
dd392e94 FD |
237 | } |
238 | } else { | |
239 | PERROR("Failed to launch \"%s\" viewer", viewer_bin); | |
240 | } | |
241 | ret = -1; | |
242 | goto error; | |
243 | } | |
244 | ||
245 | /* | |
246 | * This function should never return if successfull because `execvp(3)` | |
247 | * onle returns if an error has occurred. | |
248 | */ | |
a0377dfe | 249 | LTTNG_ASSERT(ret != 0); |
dd392e94 FD |
250 | error: |
251 | free(argv); | |
252 | return ret; | |
253 | } |