2 * Copyright (C) 2011 - David Goulet <david.goulet@polymtl.ca>
3 * Copyright (C) 2015 - Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
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.
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.
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.
27 #include <sys/types.h>
31 #include <urcu/list.h>
33 #include <common/mi-lttng.h>
35 #include "../command.h"
42 enum tracker_type_state
{
60 struct lttng_tracker_id
**array
;
63 static char *opt_session_name
;
64 static int opt_kernel
;
65 static int opt_userspace
;
67 static struct opt_type opt_pid
, opt_vpid
, opt_uid
, opt_vuid
, opt_gid
, opt_vgid
;
69 static enum tracker_type_state type_state
;
84 static struct poptOption long_options
[] = {
85 /* { longName, shortName, argInfo, argPtr, value, descrip, argDesc, } */
86 { "help", 'h', POPT_ARG_NONE
, 0, OPT_HELP
, 0, 0, },
87 { "session", 's', POPT_ARG_STRING
, &opt_session_name
, OPT_SESSION
, 0, 0, },
88 { "kernel", 'k', POPT_ARG_VAL
, &opt_kernel
, 1, 0, 0, },
89 { "userspace", 'u', POPT_ARG_VAL
, &opt_userspace
, 1, 0, 0, },
90 { "pid", 'p', POPT_ARG_STRING
| POPT_ARGFLAG_OPTIONAL
, &opt_pid
.string
, OPT_PID
, 0, 0, },
91 { "vpid", 0, POPT_ARG_STRING
| POPT_ARGFLAG_OPTIONAL
, &opt_vpid
.string
, OPT_VPID
, 0, 0, },
92 { "uid", 0, POPT_ARG_STRING
| POPT_ARGFLAG_OPTIONAL
, &opt_uid
.string
, OPT_UID
, 0, 0, },
93 { "vuid", 0, POPT_ARG_STRING
| POPT_ARGFLAG_OPTIONAL
, &opt_vuid
.string
, OPT_VUID
, 0, 0, },
94 { "gid", 0, POPT_ARG_STRING
| POPT_ARGFLAG_OPTIONAL
, &opt_gid
.string
, OPT_GID
, 0, 0, },
95 { "vgid", 0, POPT_ARG_STRING
| POPT_ARGFLAG_OPTIONAL
, &opt_vgid
.string
, OPT_VGID
, 0, 0, },
96 { "all", 'a', POPT_ARG_NONE
, 0, OPT_ALL
, 0, 0, },
97 { "list-options", 0, POPT_ARG_NONE
, NULL
, OPT_LIST_OPTIONS
, 0, 0, },
98 { 0, 0, 0, 0, 0, 0, 0, },
101 static struct id_list
*alloc_id_list(size_t nr_items
)
103 struct id_list
*id_list
;
104 struct lttng_tracker_id
**items
;
106 id_list
= zmalloc(sizeof(*id_list
));
110 items
= zmalloc(nr_items
* sizeof(*items
));
114 id_list
->nr
= nr_items
;
115 id_list
->array
= items
;
122 static void free_id_list(struct id_list
*list
)
131 for (i
= 0; i
< nr_items
; i
++) {
132 struct lttng_tracker_id
*item
= list
->array
[i
];
133 lttng_tracker_id_destroy(item
);
138 static int parse_id_string(const char *_id_string
,
140 struct id_list
**_id_list
,
141 enum lttng_tracker_type tracker_type
)
143 const char *one_id_str
;
145 int retval
= CMD_SUCCESS
;
147 struct id_list
*id_list
= NULL
;
148 char *id_string
= NULL
;
151 if (all
&& _id_string
) {
152 ERR("An empty ID string is expected with --all");
156 if (!all
&& !_id_string
) {
157 ERR("An ID string is expected");
162 enum lttng_tracker_id_status status
;
163 /* Empty `ID string means all IDs */
164 id_list
= alloc_id_list(1);
166 ERR("Out of memory");
171 id_list
->array
[0] = lttng_tracker_id_create();
172 if (id_list
->array
[0] == NULL
) {
173 ERR("Out of memory");
178 status
= lttng_tracker_id_set_all(id_list
->array
[0]);
179 if (status
!= LTTNG_TRACKER_ID_STATUS_OK
) {
180 ERR("Invalid value for tracker id");
187 id_string
= strdup(_id_string
);
189 ERR("Out of memory");
195 one_id_str
= strtok_r(id_string
, ",", &iter
);
196 while (one_id_str
!= NULL
) {
199 if (isdigit(one_id_str
[0])) {
201 v
= strtoul(one_id_str
, &endptr
, 10);
202 if ((v
== 0 && errno
== EINVAL
) ||
203 (v
== ULONG_MAX
&& errno
== ERANGE
) ||
204 (*one_id_str
!= '\0' &&
206 ERR("Error parsing ID %s", one_id_str
);
211 if ((long) v
> INT_MAX
|| (int) v
< 0) {
212 ERR("Invalid ID value %ld", (long) v
);
220 one_id_str
= strtok_r(NULL
, ",", &iter
);
223 ERR("Fatal error occurred when parsing pid string");
229 /* Identity of delimiter has been lost in first pass. */
230 id_string
= strdup(_id_string
);
232 ERR("Out of memory");
238 id_list
= alloc_id_list(count
);
240 ERR("Out of memory");
245 /* Reparse string and populate the id list. */
247 one_id_str
= strtok_r(id_string
, ",", &iter
);
248 while (one_id_str
!= NULL
) {
249 enum lttng_tracker_id_status status
;
250 struct lttng_tracker_id
*item
;
251 item
= lttng_tracker_id_create();
253 ERR("Out of memory");
258 id_list
->array
[count
++] = item
;
259 if (isdigit(one_id_str
[0])) {
262 v
= strtoul(one_id_str
, NULL
, 10);
263 status
= lttng_tracker_id_set_value(item
, (int) v
);
264 if (status
== LTTNG_TRACKER_ID_STATUS_INVALID
) {
265 ERR("Invalid value");
270 status
= lttng_tracker_id_set_string(item
, one_id_str
);
271 if (status
== LTTNG_TRACKER_ID_STATUS_INVALID
) {
272 ERR("Failed to set ID string");
279 one_id_str
= strtok_r(NULL
, ",", &iter
);
289 free_id_list(id_list
);
295 static const char *get_tracker_str(enum lttng_tracker_type tracker_type
)
297 switch (tracker_type
) {
298 case LTTNG_TRACKER_PID
:
300 case LTTNG_TRACKER_VPID
:
302 case LTTNG_TRACKER_UID
:
304 case LTTNG_TRACKER_VUID
:
306 case LTTNG_TRACKER_GID
:
308 case LTTNG_TRACKER_VGID
:
316 static int ust_tracker_type_support(enum lttng_tracker_type
*tracker_type
)
320 switch (*tracker_type
) {
321 case LTTNG_TRACKER_PID
:
322 *tracker_type
= LTTNG_TRACKER_VPID
;
325 case LTTNG_TRACKER_VPID
:
326 case LTTNG_TRACKER_VUID
:
327 case LTTNG_TRACKER_VGID
:
330 case LTTNG_TRACKER_UID
:
331 case LTTNG_TRACKER_GID
:
332 ERR("The %s tracker is invalid for UST domain.",
333 get_tracker_str(*tracker_type
));
344 static enum cmd_error_code
track_untrack_id(enum cmd_type cmd_type
,
346 const char *session_name
,
347 const char *id_string
,
349 struct mi_writer
*writer
,
350 enum lttng_tracker_type tracker_type
)
352 int ret
, success
= 1 , i
;
353 enum cmd_error_code retval
= CMD_SUCCESS
;
354 struct id_list
*id_list
= NULL
;
355 struct lttng_domain dom
;
356 struct lttng_handle
*handle
= NULL
;
357 int (*cmd_func
)(struct lttng_handle
*handle
,
358 enum lttng_tracker_type tracker_type
,
359 const struct lttng_tracker_id
*id
);
360 const char *tracker_str
;
364 cmd_func
= lttng_track_id
;
367 cmd_func
= lttng_untrack_id
;
370 ERR("Unknown command");
374 memset(&dom
, 0, sizeof(dom
));
376 dom
.type
= LTTNG_DOMAIN_KERNEL
;
377 } else if (opt_userspace
) {
378 dom
.type
= LTTNG_DOMAIN_UST
;
379 ret
= ust_tracker_type_support(&tracker_type
);
381 ERR("Invalid parameter");
386 /* Checked by the caller. */
389 tracker_str
= get_tracker_str(tracker_type
);
391 ERR("Unknown tracker type");
395 ret
= parse_id_string(id_string
, all
, &id_list
, tracker_type
);
396 if (ret
!= CMD_SUCCESS
) {
397 ERR("Error parsing %s string", tracker_str
);
402 handle
= lttng_create_handle(session_name
, &dom
);
403 if (handle
== NULL
) {
409 /* Open tracker_id and targets elements */
410 ret
= mi_lttng_id_tracker_open(writer
, tracker_type
);
416 for (i
= 0; i
< id_list
->nr
; i
++) {
417 struct lttng_tracker_id
*item
= id_list
->array
[i
];
418 enum lttng_tracker_id_type type
=
419 lttng_tracker_id_get_type(item
);
420 enum lttng_tracker_id_status status
=
421 LTTNG_TRACKER_ID_STATUS_OK
;
423 const char *value_string
;
427 /* Nothing to check */
430 status
= lttng_tracker_id_get_value(item
, &value
);
432 case LTTNG_ID_STRING
:
433 status
= lttng_tracker_id_get_string(
434 item
, &value_string
);
441 if (status
!= LTTNG_TRACKER_ID_STATUS_OK
) {
442 ERR("Tracker id object is in an invalid state");
449 DBG("%s all IDs", cmd_str
);
452 DBG("%s ID %d", cmd_str
, value
);
454 case LTTNG_ID_STRING
:
455 DBG("%s ID '%s'", cmd_str
, value_string
);
462 ret
= cmd_func(handle
, tracker_type
, item
);
467 case LTTNG_ERR_ID_TRACKED
:
468 msg
= "already tracked";
470 retval
= CMD_SUCCESS
;
472 case LTTNG_ERR_ID_NOT_TRACKED
:
473 msg
= "already not tracked";
475 retval
= CMD_SUCCESS
;
478 ERR("%s", lttng_strerror(ret
));
486 WARN("All %ss %s in session %s",
491 WARN("%s %i %s in session %s",
492 tracker_str
, value
, msg
,
495 case LTTNG_ID_STRING
:
496 WARN("%s '%s' %s in session %s",
509 MSG("All %ss %sed in session %s", tracker_str
,
510 cmd_str
, session_name
);
513 MSG("%s %i %sed in session %s", tracker_str
,
514 value
, cmd_str
, session_name
);
516 case LTTNG_ID_STRING
:
517 MSG("%s '%s' %sed in session %s", tracker_str
,
518 value_string
, cmd_str
,
530 ret
= mi_lttng_id_target(writer
, tracker_type
, item
, 1);
536 ret
= mi_lttng_writer_write_element_bool(writer
,
537 mi_lttng_element_success
, success
);
543 ret
= mi_lttng_writer_close_element(writer
);
552 /* Close targets and tracker_id elements */
553 ret
= mi_lttng_close_multi_element(writer
, 2);
562 lttng_destroy_handle(handle
);
564 free_id_list(id_list
);
569 const char *get_mi_element_command(enum cmd_type cmd_type
)
573 return mi_lttng_element_command_track
;
575 return mi_lttng_element_command_untrack
;
581 static void print_err_duplicate(const char *type
)
583 ERR("The --%s option can only be used once. A list of comma-separated values can be specified.",
588 * Add/remove tracker to/from session.
591 int cmd_track_untrack(enum cmd_type cmd_type
, const char *cmd_str
,
592 int argc
, const char **argv
, const char *help_msg
)
594 int opt
, ret
= 0, success
= 1;
595 bool opt_all_present
= false;
596 enum cmd_error_code command_ret
= CMD_SUCCESS
;
597 static poptContext pc
;
598 char *session_name
= NULL
;
599 const char *leftover
= NULL
;
600 struct mi_writer
*writer
= NULL
;
603 command_ret
= CMD_ERROR
;
607 pc
= poptGetContext(NULL
, argc
, argv
, long_options
, 0);
608 poptReadDefaultConfig(pc
, 0);
610 while ((opt
= poptGetNextOpt(pc
)) != -1) {
615 case OPT_LIST_OPTIONS
:
616 list_cmd_options(stdout
, long_options
);
622 print_err_duplicate("pid");
623 command_ret
= CMD_ERROR
;
627 type_state
= STATE_PID
;
631 print_err_duplicate("vpid");
632 command_ret
= CMD_ERROR
;
636 type_state
= STATE_VPID
;
640 print_err_duplicate("uid");
641 command_ret
= CMD_ERROR
;
645 type_state
= STATE_UID
;
649 print_err_duplicate("vuid");
650 command_ret
= CMD_ERROR
;
654 type_state
= STATE_VUID
;
658 print_err_duplicate("gid");
659 command_ret
= CMD_ERROR
;
663 type_state
= STATE_GID
;
667 print_err_duplicate("vgid");
668 command_ret
= CMD_ERROR
;
672 type_state
= STATE_VGID
;
675 opt_all_present
= true;
678 command_ret
= CMD_UNDEFINED
;
683 ret
= print_missing_or_multiple_domains(opt_kernel
+ opt_userspace
);
685 command_ret
= CMD_ERROR
;
690 * If the `--all` option is present set the appropriate tracker's `all`
693 if (opt_all_present
) {
694 switch (type_state
) {
714 command_ret
= CMD_ERROR
;
719 if (!opt_session_name
) {
720 session_name
= get_session_name();
721 if (session_name
== NULL
) {
722 command_ret
= CMD_ERROR
;
726 session_name
= opt_session_name
;
729 leftover
= poptGetArg(pc
);
731 ERR("Unknown argument: %s", leftover
);
738 writer
= mi_lttng_writer_create(fileno(stdout
), lttng_opt_mi
);
740 command_ret
= CMD_ERROR
;
746 /* Open command element */
747 ret
= mi_lttng_writer_command_open(writer
,
748 get_mi_element_command(cmd_type
));
750 command_ret
= CMD_ERROR
;
754 /* Open output element */
755 ret
= mi_lttng_writer_open_element(writer
,
756 mi_lttng_element_command_output
);
758 command_ret
= CMD_ERROR
;
762 ret
= mi_lttng_trackers_open(writer
);
769 command_ret
= track_untrack_id(cmd_type
, cmd_str
, session_name
,
770 opt_pid
.string
, opt_pid
.all
, writer
,
772 if (command_ret
!= CMD_SUCCESS
) {
777 command_ret
= track_untrack_id(cmd_type
, cmd_str
, session_name
,
778 opt_vpid
.string
, opt_vpid
.all
, writer
,
780 if (command_ret
!= CMD_SUCCESS
) {
785 command_ret
= track_untrack_id(cmd_type
, cmd_str
, session_name
,
786 opt_uid
.string
, opt_uid
.all
, writer
,
788 if (command_ret
!= CMD_SUCCESS
) {
793 command_ret
= track_untrack_id(cmd_type
, cmd_str
, session_name
,
794 opt_vuid
.string
, opt_vuid
.all
, writer
,
796 if (command_ret
!= CMD_SUCCESS
) {
801 command_ret
= track_untrack_id(cmd_type
, cmd_str
, session_name
,
802 opt_gid
.string
, opt_gid
.all
, writer
,
804 if (command_ret
!= CMD_SUCCESS
) {
809 command_ret
= track_untrack_id(cmd_type
, cmd_str
, session_name
,
810 opt_vgid
.string
, opt_vgid
.all
, writer
,
812 if (command_ret
!= CMD_SUCCESS
) {
819 /* Close trackers and output elements */
820 ret
= mi_lttng_close_multi_element(writer
, 2);
822 command_ret
= CMD_ERROR
;
827 ret
= mi_lttng_writer_write_element_bool(writer
,
828 mi_lttng_element_command_success
, success
);
830 command_ret
= CMD_ERROR
;
834 /* Command element close */
835 ret
= mi_lttng_writer_command_close(writer
);
837 command_ret
= CMD_ERROR
;
843 if (!opt_session_name
) {
848 if (writer
&& mi_lttng_writer_destroy(writer
)) {
849 /* Preserve original error code */
850 command_ret
= CMD_ERROR
;
854 return (int) command_ret
;
857 int cmd_track(int argc
, const char **argv
)
859 static const char *help_msg
=
860 #ifdef LTTNG_EMBED_HELP
861 #include <lttng-track.1.h>
867 return cmd_track_untrack(CMD_TRACK
, "track", argc
, argv
, help_msg
);
870 int cmd_untrack(int argc
, const char **argv
)
872 static const char *help_msg
=
873 #ifdef LTTNG_EMBED_HELP
874 #include <lttng-untrack.1.h>
880 return cmd_track_untrack(CMD_UNTRACK
, "untrack", argc
, argv
, help_msg
);