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