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