lttng: fix: potential 0-length allocation in pid list parsing
[lttng-tools.git] / src / bin / lttng / commands / track-untrack.c
1 /*
2 * Copyright (C) 2011 - David Goulet <david.goulet@polymtl.ca>
3 * Copyright (C) 2015 - Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License, version 2 only,
7 * as published by the Free Software Foundation.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License along
15 * with this program; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17 */
18
19 #define _LGPL_SOURCE
20 #include <ctype.h>
21 #include <popt.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <sys/stat.h>
26 #include <sys/types.h>
27 #include <unistd.h>
28 #include <assert.h>
29
30 #include <urcu/list.h>
31
32 #include <common/mi-lttng.h>
33
34 #include "../command.h"
35
36 enum cmd_type {
37 CMD_TRACK,
38 CMD_UNTRACK,
39 };
40
41 static char *opt_session_name;
42 static int opt_kernel;
43 static int opt_userspace;
44 static int opt_all;
45 static char *opt_pid_string;
46 static int opt_pid;
47
48 enum {
49 OPT_HELP = 1,
50 OPT_LIST_OPTIONS,
51 OPT_SESSION,
52 OPT_PID,
53 };
54
55 static struct poptOption long_options[] = {
56 /* { longName, shortName, argInfo, argPtr, value, descrip, argDesc, } */
57 { "help", 'h', POPT_ARG_NONE, 0, OPT_HELP, 0, 0, },
58 { "session", 's', POPT_ARG_STRING, &opt_session_name, OPT_SESSION, 0, 0, },
59 { "kernel", 'k', POPT_ARG_VAL, &opt_kernel, 1, 0, 0, },
60 { "userspace", 'u', POPT_ARG_VAL, &opt_userspace, 1, 0, 0, },
61 { "pid", 'p', POPT_ARG_STRING | POPT_ARGFLAG_OPTIONAL, &opt_pid_string, OPT_PID, 0, 0, },
62 { "all", 'a', POPT_ARG_VAL, &opt_all, 1, 0, 0, },
63 { "list-options", 0, POPT_ARG_NONE, NULL, OPT_LIST_OPTIONS, 0, 0, },
64 { 0, 0, 0, 0, 0, 0, 0, },
65 };
66
67 static
68 int parse_pid_string(const char *_pid_string,
69 int all, int **_pid_list, int *nr_pids)
70 {
71 const char *one_pid_str;
72 char *iter;
73 int retval = CMD_SUCCESS;
74 int count = 0;
75 int *pid_list = NULL;
76 char *pid_string = NULL;
77 char *endptr;
78
79 if (all && _pid_string) {
80 ERR("An empty PID string is expected with --all");
81 retval = CMD_ERROR;
82 goto error;
83 }
84 if (!all && !_pid_string) {
85 ERR("Please specify --all with an empty PID string");
86 retval = CMD_ERROR;
87 goto error;
88 }
89 if (all) {
90 pid_list = zmalloc(sizeof(*pid_list));
91 if (!pid_list) {
92 ERR("Out of memory");
93 retval = CMD_ERROR;
94 goto error;
95 }
96 /* Empty PID string means all PIDs */
97 count = 1;
98 pid_list[0] = -1;
99 goto assign;
100 }
101
102 pid_string = strdup(_pid_string);
103 if (!pid_string) {
104 ERR("Out of memory");
105 retval = CMD_ERROR;
106 goto error;
107 }
108
109 /* Count */
110 one_pid_str = strtok_r(pid_string, ",", &iter);
111 while (one_pid_str != NULL) {
112 unsigned long v;
113
114 errno = 0;
115 v = strtoul(one_pid_str, &endptr, 10);
116 if ((v == 0 && errno == EINVAL)
117 || (v == ULONG_MAX && errno == ERANGE)
118 || (*one_pid_str != '\0' && *endptr != '\0')){
119 ERR("Error parsing PID %s", one_pid_str);
120 retval = CMD_ERROR;
121 goto error;
122 }
123
124 if ((long) v > INT_MAX || (int) v < 0) {
125 ERR("Invalid PID value %ld", (long) v);
126 retval = CMD_ERROR;
127 goto error;
128 }
129 count++;
130
131 /* For next loop */
132 one_pid_str = strtok_r(NULL, ",", &iter);
133 }
134 if (count == 0) {
135 ERR("Fatal error occurred when parsing pid string");
136 retval = CMD_ERROR;
137 goto error;
138 }
139
140 free(pid_string);
141 /* Identity of delimiter has been lost in first pass. */
142 pid_string = strdup(_pid_string);
143 if (!pid_string) {
144 ERR("Out of memory");
145 retval = CMD_ERROR;
146 goto error;
147 }
148
149 /* Allocate */
150 pid_list = zmalloc(count * sizeof(*pid_list));
151 if (!pid_list) {
152 ERR("Out of memory");
153 retval = CMD_ERROR;
154 goto error;
155 }
156
157 /* Reparse string and populate the pid list. */
158 count = 0;
159 one_pid_str = strtok_r(pid_string, ",", &iter);
160 while (one_pid_str != NULL) {
161 unsigned long v;
162
163 v = strtoul(one_pid_str, NULL, 10);
164 pid_list[count++] = (int) v;
165
166 /* For next loop */
167 one_pid_str = strtok_r(NULL, ",", &iter);
168 }
169
170 assign:
171 *nr_pids = count;
172 *_pid_list = pid_list;
173 goto end; /* SUCCESS */
174
175 /* ERROR */
176 error:
177 free(pid_list);
178 end:
179 free(pid_string);
180 return retval;
181 }
182
183 static
184 enum cmd_error_code track_untrack_pid(enum cmd_type cmd_type, const char *cmd_str,
185 const char *session_name, const char *pid_string,
186 int all, struct mi_writer *writer)
187 {
188 int ret, success = 1 , i;
189 enum cmd_error_code retval = CMD_SUCCESS;
190 int *pid_list = NULL;
191 int nr_pids;
192 struct lttng_domain dom;
193 struct lttng_handle *handle = NULL;
194 int (*cmd_func)(struct lttng_handle *handle, int pid);
195
196 switch (cmd_type) {
197 case CMD_TRACK:
198 cmd_func = lttng_track_pid;
199 break;
200 case CMD_UNTRACK:
201 cmd_func = lttng_untrack_pid;
202 break;
203 default:
204 ERR("Unknown command");
205 retval = CMD_ERROR;
206 goto end;
207 }
208
209 memset(&dom, 0, sizeof(dom));
210 if (opt_kernel) {
211 dom.type = LTTNG_DOMAIN_KERNEL;
212 } else if (opt_userspace) {
213 dom.type = LTTNG_DOMAIN_UST;
214 } else {
215 /* Checked by the caller. */
216 assert(0);
217 }
218
219 ret = parse_pid_string(pid_string, all, &pid_list, &nr_pids);
220 if (ret != CMD_SUCCESS) {
221 ERR("Error parsing PID string");
222 retval = CMD_ERROR;
223 goto end;
224 }
225
226 handle = lttng_create_handle(session_name, &dom);
227 if (handle == NULL) {
228 retval = CMD_ERROR;
229 goto end;
230 }
231
232 if (writer) {
233 /* Open process element */
234 ret = mi_lttng_targets_open(writer);
235 if (ret) {
236 retval = CMD_ERROR;
237 goto end;
238 }
239 }
240
241 for (i = 0; i < nr_pids; i++) {
242 DBG("%s PID %d", cmd_str, pid_list[i]);
243 ret = cmd_func(handle, pid_list[i]);
244 if (ret) {
245 switch (-ret) {
246 case LTTNG_ERR_PID_TRACKED:
247 WARN("PID %i already tracked in session %s",
248 pid_list[i], session_name);
249 success = 1;
250 retval = CMD_SUCCESS;
251 break;
252 case LTTNG_ERR_PID_NOT_TRACKED:
253 WARN("PID %i not tracked in session %s",
254 pid_list[i], session_name);
255 success = 1;
256 retval = CMD_SUCCESS;
257 break;
258 default:
259 ERR("%s", lttng_strerror(ret));
260 success = 0;
261 retval = CMD_ERROR;
262 break;
263 }
264 } else {
265 if (pid_list[i] != -1) {
266 MSG("PID %i %sed in session %s",
267 pid_list[i], cmd_str,
268 session_name);
269 } else {
270 MSG("All PIDs %sed in session %s",
271 cmd_str, session_name);
272 }
273 success = 1;
274 }
275
276 /* Mi */
277 if (writer) {
278 ret = mi_lttng_pid_target(writer, pid_list[i], 1);
279 if (ret) {
280 retval = CMD_ERROR;
281 goto end;
282 }
283
284 ret = mi_lttng_writer_write_element_bool(writer,
285 mi_lttng_element_success, success);
286 if (ret) {
287 retval = CMD_ERROR;
288 goto end;
289 }
290
291 ret = mi_lttng_writer_close_element(writer);
292 if (ret) {
293 retval = CMD_ERROR;
294 goto end;
295 }
296 }
297 }
298
299 if (writer) {
300 /* Close targets element */
301 ret = mi_lttng_writer_close_element(writer);
302 if (ret) {
303 retval = CMD_ERROR;
304 goto end;
305 }
306 }
307
308 end:
309 if (handle) {
310 lttng_destroy_handle(handle);
311 }
312 free(pid_list);
313 return retval;
314 }
315
316 static
317 const char *get_mi_element_command(enum cmd_type cmd_type)
318 {
319 switch (cmd_type) {
320 case CMD_TRACK:
321 return mi_lttng_element_command_track;
322 case CMD_UNTRACK:
323 return mi_lttng_element_command_untrack;
324 default:
325 return NULL;
326 }
327 }
328
329 /*
330 * Add/remove tracker to/from session.
331 */
332 static
333 int cmd_track_untrack(enum cmd_type cmd_type, const char *cmd_str,
334 int argc, const char **argv, const char *help_msg)
335 {
336 int opt, ret = 0;
337 enum cmd_error_code command_ret = CMD_SUCCESS;
338 int success = 1;
339 static poptContext pc;
340 char *session_name = NULL;
341 struct mi_writer *writer = NULL;
342
343 if (argc < 1) {
344 command_ret = CMD_ERROR;
345 goto end;
346 }
347
348 pc = poptGetContext(NULL, argc, argv, long_options, 0);
349 poptReadDefaultConfig(pc, 0);
350
351 while ((opt = poptGetNextOpt(pc)) != -1) {
352 switch (opt) {
353 case OPT_HELP:
354 SHOW_HELP();
355 goto end;
356 case OPT_LIST_OPTIONS:
357 list_cmd_options(stdout, long_options);
358 goto end;
359 case OPT_SESSION:
360 case OPT_PID:
361 opt_pid = 1;
362 break;
363 default:
364 command_ret = CMD_UNDEFINED;
365 goto end;
366 }
367 }
368
369 ret = print_missing_or_multiple_domains(opt_kernel + opt_userspace);
370 if (ret) {
371 command_ret = CMD_ERROR;
372 goto end;
373 }
374
375 if (!opt_session_name) {
376 session_name = get_session_name();
377 if (session_name == NULL) {
378 command_ret = CMD_ERROR;
379 goto end;
380 }
381 } else {
382 session_name = opt_session_name;
383 }
384
385 /* Currently only PID tracker is supported */
386 if (!opt_pid) {
387 ERR("Please specify at least one tracker with its expected arguments");
388 command_ret = CMD_ERROR;
389 goto end;
390 }
391
392 /* Mi check */
393 if (lttng_opt_mi) {
394 writer = mi_lttng_writer_create(fileno(stdout), lttng_opt_mi);
395 if (!writer) {
396 command_ret = CMD_ERROR;
397 goto end;
398 }
399 }
400
401 if (writer) {
402 /* Open command element */
403 ret = mi_lttng_writer_command_open(writer,
404 get_mi_element_command(cmd_type));
405 if (ret) {
406 command_ret = CMD_ERROR;
407 goto end;
408 }
409
410 /* Open output element */
411 ret = mi_lttng_writer_open_element(writer,
412 mi_lttng_element_command_output);
413 if (ret) {
414 command_ret = CMD_ERROR;
415 goto end;
416 }
417 }
418
419 command_ret = track_untrack_pid(cmd_type,
420 cmd_str, session_name, opt_pid_string,
421 opt_all, writer);
422 if (command_ret != CMD_SUCCESS) {
423 success = 0;
424 }
425
426 /* Mi closing */
427 if (writer) {
428 /* Close output element */
429 ret = mi_lttng_writer_close_element(writer);
430 if (ret) {
431 command_ret = CMD_ERROR;
432 goto end;
433 }
434
435 /* Success ? */
436 ret = mi_lttng_writer_write_element_bool(writer,
437 mi_lttng_element_command_success, success);
438 if (ret) {
439 command_ret = CMD_ERROR;
440 goto end;
441 }
442
443 /* Command element close */
444 ret = mi_lttng_writer_command_close(writer);
445 if (ret) {
446 command_ret = CMD_ERROR;
447 goto end;
448 }
449 }
450
451 end:
452 if (!opt_session_name) {
453 free(session_name);
454 }
455
456 /* Mi clean-up */
457 if (writer && mi_lttng_writer_destroy(writer)) {
458 /* Preserve original error code */
459 command_ret = CMD_ERROR;
460 }
461
462 poptFreeContext(pc);
463 return (int) command_ret;
464 }
465
466 int cmd_track(int argc, const char **argv)
467 {
468 static const char *help_msg =
469 #ifdef LTTNG_EMBED_HELP
470 #include <lttng-track.1.h>
471 #else
472 NULL
473 #endif
474 ;
475
476 return cmd_track_untrack(CMD_TRACK, "track", argc, argv, help_msg);
477 }
478
479 int cmd_untrack(int argc, const char **argv)
480 {
481 static const char *help_msg =
482 #ifdef LTTNG_EMBED_HELP
483 #include <lttng-untrack.1.h>
484 #else
485 NULL
486 #endif
487 ;
488
489 return cmd_track_untrack(CMD_UNTRACK, "untrack", argc, argv, help_msg);
490 }
This page took 0.038531 seconds and 4 git commands to generate.