common: Add index allocator for error counters
[lttng-tools.git] / src / common / spawn-viewer.c
1 /*
2 * Copyright (C) 2011 David Goulet <david.goulet@polymtl.ca>
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: GPL-2.0-only
7 *
8 */
9
10 #include <assert.h>
11 #include <stdbool.h>
12 #include <sys/stat.h>
13 #include <sys/types.h>
14 #include <unistd.h>
15
16 #include <lttng/constant.h>
17
18 #include <common/compat/errno.h>
19 #include "error.h"
20 #include "macros.h"
21 #include "spawn-viewer.h"
22
23
24 static const char *babeltrace_bin = CONFIG_BABELTRACE_BIN;
25 static const char *babeltrace2_bin = CONFIG_BABELTRACE2_BIN;
26
27 /*
28 * This is needed for each viewer since we are using execvp().
29 */
30 static const char *babeltrace_opts[] = { "babeltrace" };
31 static const char *babeltrace2_opts[] = { "babeltrace2" };
32
33 /*
34 * Type is also use as the index in the viewers array. So please, make sure
35 * your enum value is in the right order in the array below.
36 */
37 enum viewer_type {
38 VIEWER_BABELTRACE = 0,
39 VIEWER_BABELTRACE2 = 1,
40 VIEWER_USER_DEFINED = 2,
41 };
42
43 static const struct viewer {
44 const char *exec_name;
45 enum viewer_type type;
46 } viewers[] = {
47 { "babeltrace", VIEWER_BABELTRACE },
48 { "babeltrace2", VIEWER_BABELTRACE2 },
49 { NULL, VIEWER_USER_DEFINED },
50 };
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 = zmalloc(sizeof(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 size, 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 size = sizeof(char *) * mem_len;
143
144 /* Add two here for the trace_path and the NULL terminating element. */
145 argv = zmalloc(size);
146 if (argv == NULL) {
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;
156 argv[opts_len + 3] = NULL;
157 } else {
158 argv[opts_len] = (char *) trace_path;
159 argv[opts_len + 1] = NULL;
160 }
161
162 error:
163 return argv;
164 }
165
166
167 /*
168 * Spawn viewer with the trace directory path.
169 */
170 int spawn_viewer(const char *trace_path, char *opt_viewer, bool opt_live_mode)
171 {
172 int ret = 0;
173 struct stat status;
174 const char *viewer_bin = NULL;
175 const struct viewer *viewer;
176 char **argv = NULL;
177
178 /* Check for --viewer option. */
179 viewer = parse_viewer_option(opt_viewer);
180 if (viewer == NULL) {
181 ret = -1;
182 goto error;
183 }
184
185 retry_viewer:
186 switch (viewer->type) {
187 case VIEWER_BABELTRACE2:
188 if (stat(babeltrace2_bin, &status) == 0) {
189 viewer_bin = babeltrace2_bin;
190 } else {
191 viewer_bin = viewer->exec_name;
192 }
193 argv = alloc_argv_from_local_opts(babeltrace2_opts,
194 ARRAY_SIZE(babeltrace2_opts), trace_path,
195 opt_live_mode);
196 break;
197 case VIEWER_BABELTRACE:
198 if (stat(babeltrace_bin, &status) == 0) {
199 viewer_bin = babeltrace_bin;
200 } else {
201 viewer_bin = viewer->exec_name;
202 }
203 argv = alloc_argv_from_local_opts(babeltrace_opts,
204 ARRAY_SIZE(babeltrace_opts), trace_path,
205 opt_live_mode);
206 break;
207 case VIEWER_USER_DEFINED:
208 argv = alloc_argv_from_user_opts(opt_viewer, trace_path);
209 if (argv) {
210 viewer_bin = argv[0];
211 }
212 break;
213 default:
214 abort();
215 }
216
217 if (argv == NULL || !viewer_bin) {
218 ret = -1;
219 goto error;
220 }
221
222 DBG("Using %s viewer", viewer_bin);
223
224 ret = execvp(viewer_bin, argv);
225 if (ret) {
226 if (errno == ENOENT && viewer->exec_name) {
227 if (viewer->type == VIEWER_BABELTRACE2) {
228 /* Fallback to legacy babeltrace. */
229 DBG("Default viewer \"%s\" not installed on the system, falling back to \"%s\"",
230 viewers[VIEWER_BABELTRACE2].exec_name,
231 viewers[VIEWER_BABELTRACE].exec_name);
232 viewer = &viewers[VIEWER_BABELTRACE];
233 free(argv);
234 argv = NULL;
235 goto retry_viewer;
236 } else {
237 ERR("Default viewer \"%s\" (and fallback \"%s\") not found on the system",
238 viewers[VIEWER_BABELTRACE2].exec_name,
239 viewers[VIEWER_BABELTRACE].exec_name);
240 }
241 } else {
242 PERROR("Failed to launch \"%s\" viewer", viewer_bin);
243 }
244 ret = -1;
245 goto error;
246 }
247
248 /*
249 * This function should never return if successfull because `execvp(3)`
250 * onle returns if an error has occurred.
251 */
252 assert(ret != 0);
253 error:
254 free(argv);
255 return ret;
256 }
This page took 0.045378 seconds and 4 git commands to generate.