trackers: update list/track/untrack commands
[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 enum tracker_type_state {
42 STATE_NONE = 0,
43 STATE_PID,
44 STATE_VPID,
45 STATE_UID,
46 STATE_VUID,
47 STATE_GID,
48 STATE_VGID,
49 };
50
51 struct opt_type {
52 int used;
53 int all;
54 char *string;
55 };
56
57 struct id_list {
58 size_t nr;
59 struct lttng_tracker_id *array;
60 };
61
62 static char *opt_session_name;
63 static int opt_kernel;
64 static int opt_userspace;
65
66 static struct opt_type opt_pid, opt_vpid, opt_uid, opt_vuid, opt_gid, opt_vgid;
67
68 static enum tracker_type_state type_state;
69
70 enum {
71 OPT_HELP = 1,
72 OPT_LIST_OPTIONS,
73 OPT_SESSION,
74 OPT_PID,
75 OPT_VPID,
76 OPT_UID,
77 OPT_VUID,
78 OPT_GID,
79 OPT_VGID,
80 OPT_ALL,
81 };
82
83 static struct poptOption long_options[] = {
84 /* { longName, shortName, argInfo, argPtr, value, descrip, argDesc, } */
85 { "help", 'h', POPT_ARG_NONE, 0, OPT_HELP, 0, 0, },
86 { "session", 's', POPT_ARG_STRING, &opt_session_name, OPT_SESSION, 0, 0, },
87 { "kernel", 'k', POPT_ARG_VAL, &opt_kernel, 1, 0, 0, },
88 { "userspace", 'u', POPT_ARG_VAL, &opt_userspace, 1, 0, 0, },
89 { "pid", 'p', POPT_ARG_STRING | POPT_ARGFLAG_OPTIONAL, &opt_pid.string, OPT_PID, 0, 0, },
90 { "vpid", 0, POPT_ARG_STRING | POPT_ARGFLAG_OPTIONAL, &opt_vpid.string, OPT_VPID, 0, 0, },
91 { "uid", 0, POPT_ARG_STRING | POPT_ARGFLAG_OPTIONAL, &opt_uid.string, OPT_UID, 0, 0, },
92 { "vuid", 0, POPT_ARG_STRING | POPT_ARGFLAG_OPTIONAL, &opt_vuid.string, OPT_VUID, 0, 0, },
93 { "gid", 0, POPT_ARG_STRING | POPT_ARGFLAG_OPTIONAL, &opt_gid.string, OPT_GID, 0, 0, },
94 { "vgid", 0, POPT_ARG_STRING | POPT_ARGFLAG_OPTIONAL, &opt_vgid.string, OPT_VGID, 0, 0, },
95 { "all", 'a', POPT_ARG_NONE, 0, OPT_ALL, 0, 0, },
96 { "list-options", 0, POPT_ARG_NONE, NULL, OPT_LIST_OPTIONS, 0, 0, },
97 { 0, 0, 0, 0, 0, 0, 0, },
98 };
99
100 static struct id_list *alloc_id_list(size_t nr_items)
101 {
102 struct id_list *id_list;
103 struct lttng_tracker_id *items;
104
105 id_list = zmalloc(sizeof(*id_list));
106 if (!id_list) {
107 goto error;
108 }
109 items = zmalloc(nr_items * sizeof(*items));
110 if (!items) {
111 goto error;
112 }
113 id_list->nr = nr_items;
114 id_list->array = items;
115 return id_list;
116 error:
117 free(id_list);
118 return NULL;
119 }
120
121 static void free_id_list(struct id_list *list)
122 {
123 size_t nr_items;
124 int i;
125
126 if (!list) {
127 return;
128 }
129 nr_items = list->nr;
130 for (i = 0; i < nr_items; i++) {
131 struct lttng_tracker_id *item = &list->array[i];
132
133 free(item->string);
134 }
135 free(list);
136 }
137
138 static int parse_id_string(
139 const char *_id_string, int all, struct id_list **_id_list)
140 {
141 const char *one_id_str;
142 char *iter;
143 int retval = CMD_SUCCESS;
144 int count = 0;
145 struct id_list *id_list = NULL;
146 char *id_string = NULL;
147 char *endptr;
148
149 if (all && _id_string) {
150 ERR("An empty ID string is expected with --all");
151 retval = CMD_ERROR;
152 goto error;
153 }
154 if (!all && !_id_string) {
155 ERR("An ID string is expected");
156 retval = CMD_ERROR;
157 goto error;
158 }
159 if (all) {
160 /* Empty ID string means all IDs */
161 id_list = alloc_id_list(1);
162 if (!id_list) {
163 ERR("Out of memory");
164 retval = CMD_ERROR;
165 goto error;
166 }
167 id_list->array[0].type = LTTNG_ID_ALL;
168 goto assign;
169 }
170
171 id_string = strdup(_id_string);
172 if (!id_string) {
173 ERR("Out of memory");
174 retval = CMD_ERROR;
175 goto error;
176 }
177
178 /* Count */
179 one_id_str = strtok_r(id_string, ",", &iter);
180 while (one_id_str != NULL) {
181 unsigned long v;
182
183 if (isdigit(one_id_str[0])) {
184 errno = 0;
185 v = strtoul(one_id_str, &endptr, 10);
186 if ((v == 0 && errno == EINVAL) ||
187 (v == ULONG_MAX && errno == ERANGE) ||
188 (*one_id_str != '\0' &&
189 *endptr != '\0')) {
190 ERR("Error parsing ID %s", one_id_str);
191 retval = CMD_ERROR;
192 goto error;
193 }
194
195 if ((long) v > INT_MAX || (int) v < 0) {
196 ERR("Invalid ID value %ld", (long) v);
197 retval = CMD_ERROR;
198 goto error;
199 }
200 }
201 count++;
202
203 /* For next loop */
204 one_id_str = strtok_r(NULL, ",", &iter);
205 }
206 if (count == 0) {
207 ERR("Fatal error occurred when parsing pid string");
208 retval = CMD_ERROR;
209 goto error;
210 }
211
212 free(id_string);
213 /* Identity of delimiter has been lost in first pass. */
214 id_string = strdup(_id_string);
215 if (!id_string) {
216 ERR("Out of memory");
217 retval = CMD_ERROR;
218 goto error;
219 }
220
221 /* Allocate */
222 id_list = alloc_id_list(count);
223 if (!id_list) {
224 ERR("Out of memory");
225 retval = CMD_ERROR;
226 goto error;
227 }
228
229 /* Reparse string and populate the id list. */
230 count = 0;
231 one_id_str = strtok_r(id_string, ",", &iter);
232 while (one_id_str != NULL) {
233 struct lttng_tracker_id *item;
234
235 item = &id_list->array[count++];
236 if (isdigit(one_id_str[0])) {
237 unsigned long v;
238
239 v = strtoul(one_id_str, NULL, 10);
240 item->type = LTTNG_ID_VALUE;
241 item->value = (int) v;
242 } else {
243 item->type = LTTNG_ID_STRING;
244 item->string = strdup(one_id_str);
245 if (!item->string) {
246 PERROR("Failed to allocate ID string");
247 retval = CMD_ERROR;
248 goto error;
249 }
250 }
251
252 /* For next loop */
253 one_id_str = strtok_r(NULL, ",", &iter);
254 }
255
256 assign:
257 *_id_list = id_list;
258 goto end; /* SUCCESS */
259
260 /* ERROR */
261 error:
262 free_id_list(id_list);
263 end:
264 free(id_string);
265 return retval;
266 }
267
268 static const char *get_tracker_str(enum lttng_tracker_type tracker_type)
269 {
270 switch (tracker_type) {
271 case LTTNG_TRACKER_PID:
272 return "PID";
273 case LTTNG_TRACKER_VPID:
274 return "VPID";
275 case LTTNG_TRACKER_UID:
276 return "UID";
277 case LTTNG_TRACKER_VUID:
278 return "VUID";
279 case LTTNG_TRACKER_GID:
280 return "GID";
281 case LTTNG_TRACKER_VGID:
282 return "VGID";
283 default:
284 return NULL;
285 }
286 return NULL;
287 }
288
289 static enum cmd_error_code track_untrack_id(enum cmd_type cmd_type,
290 const char *cmd_str,
291 const char *session_name,
292 const char *id_string,
293 int all,
294 struct mi_writer *writer,
295 enum lttng_tracker_type tracker_type)
296 {
297 int ret, success = 1 , i;
298 enum cmd_error_code retval = CMD_SUCCESS;
299 struct id_list *id_list = NULL;
300 struct lttng_domain dom;
301 struct lttng_handle *handle = NULL;
302 int (*cmd_func)(struct lttng_handle *handle,
303 enum lttng_tracker_type tracker_type,
304 const struct lttng_tracker_id *id);
305 const char *tracker_str;
306
307 switch (cmd_type) {
308 case CMD_TRACK:
309 cmd_func = lttng_track_id;
310 break;
311 case CMD_UNTRACK:
312 cmd_func = lttng_untrack_id;
313 break;
314 default:
315 ERR("Unknown command");
316 retval = CMD_ERROR;
317 goto end;
318 }
319 memset(&dom, 0, sizeof(dom));
320 if (opt_kernel) {
321 dom.type = LTTNG_DOMAIN_KERNEL;
322 } else if (opt_userspace) {
323 dom.type = LTTNG_DOMAIN_UST;
324 if (tracker_type == LTTNG_TRACKER_PID) {
325 tracker_type = LTTNG_TRACKER_VPID;
326 }
327 } else {
328 /* Checked by the caller. */
329 assert(0);
330 }
331 tracker_str = get_tracker_str(tracker_type);
332 if (!tracker_str) {
333 ERR("Unknown tracker type");
334 retval = CMD_ERROR;
335 goto end;
336 }
337 ret = parse_id_string(id_string, all, &id_list);
338 if (ret != CMD_SUCCESS) {
339 ERR("Error parsing %s string", tracker_str);
340 retval = CMD_ERROR;
341 goto end;
342 }
343
344 handle = lttng_create_handle(session_name, &dom);
345 if (handle == NULL) {
346 retval = CMD_ERROR;
347 goto end;
348 }
349
350 if (writer) {
351 /* Open tracker_id and targets elements */
352 ret = mi_lttng_id_tracker_open(writer, tracker_type);
353 if (ret) {
354 goto end;
355 }
356 }
357
358 for (i = 0; i < id_list->nr; i++) {
359 struct lttng_tracker_id *item = &id_list->array[i];
360
361 switch (item->type) {
362 case LTTNG_ID_ALL:
363 DBG("%s all IDs", cmd_str);
364 break;
365 case LTTNG_ID_VALUE:
366 DBG("%s ID %d", cmd_str, item->value);
367 break;
368 case LTTNG_ID_STRING:
369 DBG("%s ID '%s'", cmd_str, item->string);
370 break;
371 default:
372 retval = CMD_ERROR;
373 goto end;
374 }
375 ret = cmd_func(handle, tracker_type, item);
376 if (ret) {
377 char *msg = NULL;
378
379 switch (-ret) {
380 case LTTNG_ERR_ID_TRACKED:
381 msg = "already tracked";
382 success = 1;
383 retval = CMD_SUCCESS;
384 break;
385 case LTTNG_ERR_ID_NOT_TRACKED:
386 msg = "already not tracked";
387 success = 1;
388 retval = CMD_SUCCESS;
389 break;
390 default:
391 ERR("%s", lttng_strerror(ret));
392 success = 0;
393 retval = CMD_ERROR;
394 break;
395 }
396 if (msg) {
397 switch (item->type) {
398 case LTTNG_ID_ALL:
399 WARN("All %ss %s in session %s",
400 tracker_str, msg,
401 session_name);
402 break;
403 case LTTNG_ID_VALUE:
404 WARN("%s %i %s in session %s",
405 tracker_str,
406 item->value, msg,
407 session_name);
408 break;
409 case LTTNG_ID_STRING:
410 WARN("%s '%s' %s in session %s",
411 tracker_str,
412 item->string, msg,
413 session_name);
414 break;
415 default:
416 retval = CMD_ERROR;
417 goto end;
418 }
419 }
420 } else {
421 switch (item->type) {
422 case LTTNG_ID_ALL:
423 MSG("All %ss %sed in session %s", tracker_str,
424 cmd_str, session_name);
425 break;
426 case LTTNG_ID_VALUE:
427 MSG("%s %i %sed in session %s", tracker_str,
428 item->value, cmd_str,
429 session_name);
430 break;
431 case LTTNG_ID_STRING:
432 MSG("%s '%s' %sed in session %s", tracker_str,
433 item->string, cmd_str,
434 session_name);
435 break;
436 default:
437 retval = CMD_ERROR;
438 goto end;
439 }
440 success = 1;
441 }
442
443 /* Mi */
444 if (writer) {
445 ret = mi_lttng_id_target(writer, tracker_type, item, 1);
446 if (ret) {
447 retval = CMD_ERROR;
448 goto end;
449 }
450
451 ret = mi_lttng_writer_write_element_bool(writer,
452 mi_lttng_element_success, success);
453 if (ret) {
454 retval = CMD_ERROR;
455 goto end;
456 }
457
458 ret = mi_lttng_writer_close_element(writer);
459 if (ret) {
460 retval = CMD_ERROR;
461 goto end;
462 }
463 }
464 }
465
466 if (writer) {
467 /* Close targets and tracker_id elements */
468 ret = mi_lttng_close_multi_element(writer, 2);
469 if (ret) {
470 retval = CMD_ERROR;
471 goto end;
472 }
473 }
474
475 end:
476 if (handle) {
477 lttng_destroy_handle(handle);
478 }
479 free_id_list(id_list);
480 return retval;
481 }
482
483 static
484 const char *get_mi_element_command(enum cmd_type cmd_type)
485 {
486 switch (cmd_type) {
487 case CMD_TRACK:
488 return mi_lttng_element_command_track;
489 case CMD_UNTRACK:
490 return mi_lttng_element_command_untrack;
491 default:
492 return NULL;
493 }
494 }
495
496 static void print_err_duplicate(const char *type)
497 {
498 ERR("The --%s option can only be used once. A list of comma-separated values can be specified.",
499 type);
500 }
501
502 /*
503 * Add/remove tracker to/from session.
504 */
505 static
506 int cmd_track_untrack(enum cmd_type cmd_type, const char *cmd_str,
507 int argc, const char **argv, const char *help_msg)
508 {
509 int opt, ret = 0;
510 enum cmd_error_code command_ret = CMD_SUCCESS;
511 int success = 1;
512 static poptContext pc;
513 char *session_name = NULL;
514 struct mi_writer *writer = NULL;
515
516 if (argc < 1) {
517 command_ret = CMD_ERROR;
518 goto end;
519 }
520
521 pc = poptGetContext(NULL, argc, argv, long_options, 0);
522 poptReadDefaultConfig(pc, 0);
523
524 while ((opt = poptGetNextOpt(pc)) != -1) {
525 switch (opt) {
526 case OPT_HELP:
527 SHOW_HELP();
528 goto end;
529 case OPT_LIST_OPTIONS:
530 list_cmd_options(stdout, long_options);
531 goto end;
532 case OPT_SESSION:
533 break;
534 case OPT_PID:
535 if (opt_pid.used) {
536 print_err_duplicate("pid");
537 command_ret = CMD_ERROR;
538 goto end;
539 }
540 opt_pid.used = 1;
541 type_state = STATE_PID;
542 break;
543 case OPT_VPID:
544 if (opt_vpid.used) {
545 print_err_duplicate("vpid");
546 command_ret = CMD_ERROR;
547 goto end;
548 }
549 opt_vpid.used = 1;
550 type_state = STATE_VPID;
551 break;
552 case OPT_UID:
553 if (opt_uid.used) {
554 print_err_duplicate("uid");
555 command_ret = CMD_ERROR;
556 goto end;
557 }
558 opt_uid.used = 1;
559 type_state = STATE_UID;
560 break;
561 case OPT_VUID:
562 if (opt_vuid.used) {
563 print_err_duplicate("vuid");
564 command_ret = CMD_ERROR;
565 goto end;
566 }
567 opt_vuid.used = 1;
568 type_state = STATE_VUID;
569 break;
570 case OPT_GID:
571 if (opt_gid.used) {
572 print_err_duplicate("gid");
573 command_ret = CMD_ERROR;
574 goto end;
575 }
576 opt_gid.used = 1;
577 type_state = STATE_GID;
578 break;
579 case OPT_VGID:
580 if (opt_vgid.used) {
581 print_err_duplicate("vgid");
582 command_ret = CMD_ERROR;
583 goto end;
584 }
585 opt_vgid.used = 1;
586 type_state = STATE_VGID;
587 break;
588 case OPT_ALL:
589 switch (type_state) {
590 case STATE_PID:
591 opt_pid.all = 1;
592 break;
593 case STATE_VPID:
594 opt_vpid.all = 1;
595 break;
596 case STATE_UID:
597 opt_uid.all = 1;
598 break;
599 case STATE_VUID:
600 opt_vuid.all = 1;
601 break;
602 case STATE_GID:
603 opt_gid.all = 1;
604 break;
605 case STATE_VGID:
606 opt_vgid.all = 1;
607 break;
608 default:
609 command_ret = CMD_ERROR;
610 goto end;
611 }
612 break;
613 default:
614 command_ret = CMD_UNDEFINED;
615 goto end;
616 }
617 }
618
619 ret = print_missing_or_multiple_domains(opt_kernel + opt_userspace);
620 if (ret) {
621 command_ret = CMD_ERROR;
622 goto end;
623 }
624
625 if (!opt_session_name) {
626 session_name = get_session_name();
627 if (session_name == NULL) {
628 command_ret = CMD_ERROR;
629 goto end;
630 }
631 } else {
632 session_name = opt_session_name;
633 }
634
635 /* Mi check */
636 if (lttng_opt_mi) {
637 writer = mi_lttng_writer_create(fileno(stdout), lttng_opt_mi);
638 if (!writer) {
639 command_ret = CMD_ERROR;
640 goto end;
641 }
642 }
643
644 if (writer) {
645 /* Open command element */
646 ret = mi_lttng_writer_command_open(writer,
647 get_mi_element_command(cmd_type));
648 if (ret) {
649 command_ret = CMD_ERROR;
650 goto end;
651 }
652
653 /* Open output element */
654 ret = mi_lttng_writer_open_element(writer,
655 mi_lttng_element_command_output);
656 if (ret) {
657 command_ret = CMD_ERROR;
658 goto end;
659 }
660
661 ret = mi_lttng_trackers_open(writer);
662 if (ret) {
663 goto end;
664 }
665 }
666
667 if (opt_pid.used) {
668 command_ret = track_untrack_id(cmd_type, cmd_str, session_name,
669 opt_pid.string, opt_pid.all, writer,
670 LTTNG_TRACKER_PID);
671 if (command_ret != CMD_SUCCESS) {
672 success = 0;
673 }
674 }
675 if (opt_vpid.used) {
676 command_ret = track_untrack_id(cmd_type, cmd_str, session_name,
677 opt_vpid.string, opt_vpid.all, writer,
678 LTTNG_TRACKER_VPID);
679 if (command_ret != CMD_SUCCESS) {
680 success = 0;
681 }
682 }
683 if (opt_uid.used) {
684 command_ret = track_untrack_id(cmd_type, cmd_str, session_name,
685 opt_uid.string, opt_uid.all, writer,
686 LTTNG_TRACKER_UID);
687 if (command_ret != CMD_SUCCESS) {
688 success = 0;
689 }
690 }
691 if (opt_vuid.used) {
692 command_ret = track_untrack_id(cmd_type, cmd_str, session_name,
693 opt_vuid.string, opt_vuid.all, writer,
694 LTTNG_TRACKER_VUID);
695 if (command_ret != CMD_SUCCESS) {
696 success = 0;
697 }
698 }
699 if (opt_gid.used) {
700 command_ret = track_untrack_id(cmd_type, cmd_str, session_name,
701 opt_gid.string, opt_gid.all, writer,
702 LTTNG_TRACKER_GID);
703 if (command_ret != CMD_SUCCESS) {
704 success = 0;
705 }
706 }
707 if (opt_vgid.used) {
708 command_ret = track_untrack_id(cmd_type, cmd_str, session_name,
709 opt_vgid.string, opt_vgid.all, writer,
710 LTTNG_TRACKER_VGID);
711 if (command_ret != CMD_SUCCESS) {
712 success = 0;
713 }
714 }
715
716 /* Mi closing */
717 if (writer) {
718 /* Close trackers and output elements */
719 ret = mi_lttng_close_multi_element(writer, 2);
720 if (ret) {
721 command_ret = CMD_ERROR;
722 goto end;
723 }
724
725 /* Success ? */
726 ret = mi_lttng_writer_write_element_bool(writer,
727 mi_lttng_element_command_success, success);
728 if (ret) {
729 command_ret = CMD_ERROR;
730 goto end;
731 }
732
733 /* Command element close */
734 ret = mi_lttng_writer_command_close(writer);
735 if (ret) {
736 command_ret = CMD_ERROR;
737 goto end;
738 }
739 }
740
741 end:
742 if (!opt_session_name) {
743 free(session_name);
744 }
745
746 /* Mi clean-up */
747 if (writer && mi_lttng_writer_destroy(writer)) {
748 /* Preserve original error code */
749 command_ret = CMD_ERROR;
750 }
751
752 poptFreeContext(pc);
753 return (int) command_ret;
754 }
755
756 int cmd_track(int argc, const char **argv)
757 {
758 static const char *help_msg =
759 #ifdef LTTNG_EMBED_HELP
760 #include <lttng-track.1.h>
761 #else
762 NULL
763 #endif
764 ;
765
766 return cmd_track_untrack(CMD_TRACK, "track", argc, argv, help_msg);
767 }
768
769 int cmd_untrack(int argc, const char **argv)
770 {
771 static const char *help_msg =
772 #ifdef LTTNG_EMBED_HELP
773 #include <lttng-untrack.1.h>
774 #else
775 NULL
776 #endif
777 ;
778
779 return cmd_track_untrack(CMD_UNTRACK, "untrack", argc, argv, help_msg);
780 }
This page took 0.07377 seconds and 4 git commands to generate.