2 * Copyright (C) 2011 David Goulet <david.goulet@polymtl.ca>
3 * Copyright (C) 2015 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
4 * Copyright (C) 2020 Jérémie Galarneau <jeremie.galarneau@efficios.com>
6 * SPDX-License-Identifier: GPL-2.0-only
18 #include <sys/types.h>
22 #include <urcu/list.h>
24 #include <common/dynamic-array.h>
25 #include <common/mi-lttng.h>
26 #include <common/optional.h>
27 #include <common/dynamic-buffer.h>
28 #include <common/tracker.h>
30 #include <lttng/lttng.h>
32 #include "../command.h"
34 struct process_attr_command_args
{
35 enum lttng_process_attr process_attr
;
36 /* Present in the user's command. */
39 struct lttng_dynamic_pointer_array string_args
;
47 /* Offset OPT_ values by one since libpopt gives '0' a special meaning. */
49 OPT_PID
= LTTNG_PROCESS_ATTR_PROCESS_ID
+ 1,
50 OPT_VPID
= LTTNG_PROCESS_ATTR_VIRTUAL_PROCESS_ID
+ 1,
51 OPT_UID
= LTTNG_PROCESS_ATTR_USER_ID
+ 1,
52 OPT_VUID
= LTTNG_PROCESS_ATTR_VIRTUAL_USER_ID
+ 1,
53 OPT_GID
= LTTNG_PROCESS_ATTR_GROUP_ID
+ 1,
54 OPT_VGID
= LTTNG_PROCESS_ATTR_VIRTUAL_GROUP_ID
+ 1,
61 static char *opt_session_name
;
62 static int opt_kernel
;
63 static int opt_userspace
;
64 static char *opt_str_arg
;
66 static struct poptOption long_options
[] = {
67 /* { longName, shortName, argInfo, argPtr, value, descrip, argDesc, } */
68 { "help", 'h', POPT_ARG_NONE
, 0, OPT_HELP
, 0, 0, },
69 { "session", 's', POPT_ARG_STRING
, &opt_session_name
, OPT_SESSION
, 0, 0, },
70 { "kernel", 'k', POPT_ARG_VAL
, &opt_kernel
, 1, 0, 0, },
71 { "userspace", 'u', POPT_ARG_VAL
, &opt_userspace
, 1, 0, 0, },
72 { "pid", 'p', POPT_ARG_STRING
| POPT_ARGFLAG_OPTIONAL
, &opt_str_arg
, OPT_PID
, 0, 0, },
73 { "vpid", 0, POPT_ARG_STRING
| POPT_ARGFLAG_OPTIONAL
, &opt_str_arg
, OPT_VPID
, 0, 0, },
74 { "uid", 0, POPT_ARG_STRING
| POPT_ARGFLAG_OPTIONAL
, &opt_str_arg
, OPT_UID
, 0, 0, },
75 { "vuid", 0, POPT_ARG_STRING
| POPT_ARGFLAG_OPTIONAL
, &opt_str_arg
, OPT_VUID
, 0, 0, },
76 { "gid", 0, POPT_ARG_STRING
| POPT_ARGFLAG_OPTIONAL
, &opt_str_arg
, OPT_GID
, 0, 0, },
77 { "vgid", 0, POPT_ARG_STRING
| POPT_ARGFLAG_OPTIONAL
, &opt_str_arg
, OPT_VGID
, 0, 0, },
78 { "all", 'a', POPT_ARG_NONE
, 0, OPT_ALL
, 0, 0, },
79 { "list-options", 0, POPT_ARG_NONE
, NULL
, OPT_LIST_OPTIONS
, 0, 0, },
80 { 0, 0, 0, 0, 0, 0, 0, },
83 static struct process_attr_command_args
84 process_attr_commands
[LTTNG_PROCESS_ATTR_VIRTUAL_GROUP_ID
+ 1];
86 static void process_attr_command_init(struct process_attr_command_args
*cmd
,
87 enum lttng_process_attr process_attr
)
89 cmd
->process_attr
= process_attr
;
91 lttng_dynamic_pointer_array_init(&cmd
->string_args
, NULL
);
94 static void process_attr_command_fini(struct process_attr_command_args
*cmd
)
96 lttng_dynamic_pointer_array_reset(&cmd
->string_args
);
99 static const char *get_capitalized_process_attr_str(enum lttng_process_attr process_attr
)
101 switch (process_attr
) {
102 case LTTNG_PROCESS_ATTR_PROCESS_ID
:
104 case LTTNG_PROCESS_ATTR_VIRTUAL_PROCESS_ID
:
105 return "Virtual process ID";
106 case LTTNG_PROCESS_ATTR_USER_ID
:
108 case LTTNG_PROCESS_ATTR_VIRTUAL_USER_ID
:
109 return "Virtual user ID";
110 case LTTNG_PROCESS_ATTR_GROUP_ID
:
112 case LTTNG_PROCESS_ATTR_VIRTUAL_GROUP_ID
:
113 return "Virtual group ID";
120 static bool ust_process_attr_supported(enum lttng_process_attr
*process_attr
)
124 switch (*process_attr
) {
125 case LTTNG_PROCESS_ATTR_PROCESS_ID
:
126 *process_attr
= LTTNG_PROCESS_ATTR_VIRTUAL_PROCESS_ID
;
128 case LTTNG_PROCESS_ATTR_VIRTUAL_PROCESS_ID
:
129 case LTTNG_PROCESS_ATTR_VIRTUAL_USER_ID
:
130 case LTTNG_PROCESS_ATTR_VIRTUAL_GROUP_ID
:
134 ERR("The %s process attribute cannot be tracked in the user space domain.",
135 lttng_process_attr_to_string(*process_attr
));
142 static const char *get_mi_element_command(enum cmd_type cmd_type
)
146 return mi_lttng_element_command_track
;
148 return mi_lttng_element_command_untrack
;
154 static enum cmd_error_code
run_command_all(enum cmd_type cmd_type
,
155 const char *session_name
,
156 enum lttng_domain_type domain_type
,
157 enum lttng_process_attr process_attr
,
158 struct mi_writer
*writer
)
160 struct lttng_process_attr_tracker_handle
*tracker_handle
= NULL
;
161 const enum lttng_error_code handle_ret_code
=
162 lttng_session_get_tracker_handle(session_name
,
163 domain_type
, process_attr
,
165 enum cmd_error_code cmd_ret
= CMD_SUCCESS
;
166 enum lttng_process_attr_tracker_handle_status status
;
169 const int ret
= mi_lttng_all_process_attribute_value(
170 writer
, process_attr
, true);
177 if (handle_ret_code
!= LTTNG_OK
) {
178 ERR("Session `%s` does not exist", session_name
);
183 status
= lttng_process_attr_tracker_handle_set_tracking_policy(
185 cmd_type
== CMD_TRACK
?
186 LTTNG_TRACKING_POLICY_INCLUDE_ALL
:
187 LTTNG_TRACKING_POLICY_EXCLUDE_ALL
);
189 case LTTNG_PROCESS_ATTR_TRACKER_HANDLE_STATUS_OK
:
190 if (cmd_type
== CMD_TRACK
) {
191 MSG("%s tracking policy set to `include all`",
192 get_capitalized_process_attr_str(process_attr
));
194 MSG("%s tracking policy set to `exclude all`",
195 get_capitalized_process_attr_str(process_attr
));
198 case LTTNG_PROCESS_ATTR_TRACKER_HANDLE_STATUS_SESSION_DOES_NOT_EXIST
:
199 ERR("%s", lttng_strerror(-LTTNG_ERR_SESS_NOT_FOUND
));
202 ERR("Unknown error encountered while setting tracking policy of %s tracker to `%s`",
203 lttng_process_attr_to_string(process_attr
),
204 cmd_type
== CMD_TRACK
? "include all" :
211 int ret
= mi_lttng_writer_write_element_bool(writer
,
212 mi_lttng_element_success
,
213 cmd_ret
== CMD_SUCCESS
);
218 ret
= mi_lttng_writer_close_element(writer
);
219 cmd_ret
= ret
== 0 ? cmd_ret
: CMD_FATAL
;
222 lttng_process_attr_tracker_handle_destroy(tracker_handle
);
226 static enum cmd_error_code
run_command_string(enum cmd_type cmd_type
,
227 const char *session_name
,
228 enum lttng_domain_type domain_type
,
229 enum lttng_process_attr process_attr
,
231 struct mi_writer
*writer
)
233 struct lttng_process_attr_tracker_handle
*tracker_handle
;
234 const enum lttng_error_code handle_ret_code
=
235 lttng_session_get_tracker_handle(session_name
,
236 domain_type
, process_attr
,
238 enum cmd_error_code cmd_ret
= CMD_SUCCESS
;
239 const char *one_value_str
;
240 char *args
= strdup(_args
);
242 bool policy_set
= false;
245 ERR("%s", lttng_strerror(-LTTNG_ERR_NOMEM
));
250 if (handle_ret_code
!= LTTNG_OK
) {
251 ERR("%s", lttng_strerror(-handle_ret_code
));
256 while ((one_value_str
= strtok_r(iter
, ",", &iter
)) != NULL
) {
257 const bool is_numerical_argument
= isdigit(one_value_str
[0]);
258 enum lttng_process_attr_tracker_handle_status status
;
259 enum lttng_tracking_policy policy
;
261 char *prettified_arg
;
264 status
= lttng_process_attr_tracker_handle_get_tracking_policy(
265 tracker_handle
, &policy
);
266 if (status
!= LTTNG_PROCESS_ATTR_TRACKER_HANDLE_STATUS_OK
) {
270 if (policy
!= LTTNG_TRACKING_POLICY_INCLUDE_SET
) {
271 status
= lttng_process_attr_tracker_handle_set_tracking_policy(
273 LTTNG_TRACKING_POLICY_INCLUDE_SET
);
274 if (status
!= LTTNG_PROCESS_ATTR_TRACKER_HANDLE_STATUS_OK
) {
281 if (is_numerical_argument
) {
282 const unsigned long one_value_int
=
283 strtoul(one_value_str
, NULL
, 10);
286 const int ret
= mi_lttng_integral_process_attribute_value(
287 writer
, process_attr
,
288 (int64_t) one_value_int
, true);
295 switch (process_attr
) {
296 case LTTNG_PROCESS_ATTR_PROCESS_ID
:
297 status
= cmd_type
== CMD_TRACK
?
298 lttng_process_attr_process_id_tracker_handle_add_pid(
300 (pid_t
) one_value_int
) :
301 lttng_process_attr_process_id_tracker_handle_remove_pid(
303 (pid_t
) one_value_int
);
305 case LTTNG_PROCESS_ATTR_VIRTUAL_PROCESS_ID
:
306 status
= cmd_type
== CMD_TRACK
?
307 lttng_process_attr_virtual_process_id_tracker_handle_add_pid(
309 (pid_t
) one_value_int
) :
310 lttng_process_attr_virtual_process_id_tracker_handle_remove_pid(
312 (pid_t
) one_value_int
);
314 case LTTNG_PROCESS_ATTR_USER_ID
:
315 status
= cmd_type
== CMD_TRACK
?
316 lttng_process_attr_user_id_tracker_handle_add_uid(
318 (uid_t
) one_value_int
) :
319 lttng_process_attr_user_id_tracker_handle_remove_uid(
321 (uid_t
) one_value_int
);
323 case LTTNG_PROCESS_ATTR_VIRTUAL_USER_ID
:
324 status
= cmd_type
== CMD_TRACK
?
325 lttng_process_attr_virtual_user_id_tracker_handle_add_uid(
327 (uid_t
) one_value_int
) :
328 lttng_process_attr_virtual_user_id_tracker_handle_remove_uid(
330 (uid_t
) one_value_int
);
332 case LTTNG_PROCESS_ATTR_GROUP_ID
:
333 status
= cmd_type
== CMD_TRACK
?
334 lttng_process_attr_group_id_tracker_handle_add_gid(
336 (gid_t
) one_value_int
) :
337 lttng_process_attr_group_id_tracker_handle_remove_gid(
339 (gid_t
) one_value_int
);
341 case LTTNG_PROCESS_ATTR_VIRTUAL_GROUP_ID
:
342 status
= cmd_type
== CMD_TRACK
?
343 lttng_process_attr_virtual_group_id_tracker_handle_add_gid(
345 (gid_t
) one_value_int
) :
346 lttng_process_attr_virtual_group_id_tracker_handle_remove_gid(
348 (gid_t
) one_value_int
);
356 const int ret
= mi_lttng_string_process_attribute_value(
357 writer
, process_attr
,
358 one_value_str
, true);
365 switch (process_attr
) {
366 case LTTNG_PROCESS_ATTR_USER_ID
:
367 status
= cmd_type
== CMD_TRACK
?
368 lttng_process_attr_user_id_tracker_handle_add_user_name(
371 lttng_process_attr_user_id_tracker_handle_remove_user_name(
375 case LTTNG_PROCESS_ATTR_VIRTUAL_USER_ID
:
376 status
= cmd_type
== CMD_TRACK
?
377 lttng_process_attr_virtual_user_id_tracker_handle_add_user_name(
380 lttng_process_attr_virtual_user_id_tracker_handle_remove_user_name(
384 case LTTNG_PROCESS_ATTR_GROUP_ID
:
385 status
= cmd_type
== CMD_TRACK
?
386 lttng_process_attr_group_id_tracker_handle_add_group_name(
389 lttng_process_attr_group_id_tracker_handle_remove_group_name(
393 case LTTNG_PROCESS_ATTR_VIRTUAL_GROUP_ID
:
394 status
= cmd_type
== CMD_TRACK
?
395 lttng_process_attr_virtual_group_id_tracker_handle_add_group_name(
398 lttng_process_attr_virtual_group_id_tracker_handle_remove_group_name(
403 ERR("%s is not a valid %s value; expected an integer",
405 lttng_process_attr_to_string(
412 ret
= asprintf(&prettified_arg
,
413 is_numerical_argument
? "%s" : "`%s`",
416 PERROR("Failed to format argument `%s`", one_value_str
);
422 case LTTNG_PROCESS_ATTR_TRACKER_HANDLE_STATUS_OK
:
423 if (cmd_type
== CMD_TRACK
) {
424 MSG("Added %s to the %s tracker inclusion set",
426 lttng_process_attr_to_string(
429 MSG("Removed %s from the %s tracker inclusion set",
431 lttng_process_attr_to_string(
435 case LTTNG_PROCESS_ATTR_TRACKER_HANDLE_STATUS_SESSION_DOES_NOT_EXIST
:
436 ERR("Session `%s` not found", session_name
);
438 case LTTNG_PROCESS_ATTR_TRACKER_HANDLE_STATUS_EXISTS
:
439 WARN("%s is already in the %s inclusion set",
441 lttng_process_attr_to_string(
444 case LTTNG_PROCESS_ATTR_TRACKER_HANDLE_STATUS_MISSING
:
445 WARN("%s is not in the %s the inclusion set",
447 lttng_process_attr_to_string(
450 case LTTNG_PROCESS_ATTR_TRACKER_HANDLE_STATUS_USER_NOT_FOUND
:
451 ERR("User %s was not found", prettified_arg
);
454 case LTTNG_PROCESS_ATTR_TRACKER_HANDLE_STATUS_GROUP_NOT_FOUND
:
455 ERR("Group %s was not found", prettified_arg
);
459 ERR("Unknown error encountered while %s %s %s %s tracker's inclusion set",
460 cmd_type
== CMD_TRACK
? "adding" :
462 lttng_process_attr_to_string(
465 cmd_type
== CMD_TRACK
? "to" : "from");
469 free(prettified_arg
);
472 int ret
= mi_lttng_writer_write_element_bool(writer
,
473 mi_lttng_element_success
,
474 cmd_ret
== CMD_SUCCESS
);
479 ret
= mi_lttng_writer_close_element(writer
);
480 cmd_ret
= ret
== 0 ? cmd_ret
: CMD_FATAL
;
486 lttng_process_attr_tracker_handle_destroy(tracker_handle
);
490 static enum cmd_error_code
run_command(enum cmd_type cmd_type
,
491 const char *session_name
,
492 const struct process_attr_command_args
*command_args
,
493 struct mi_writer
*writer
)
495 const enum lttng_domain_type domain_type
=
496 opt_kernel
? LTTNG_DOMAIN_KERNEL
: LTTNG_DOMAIN_UST
;
497 enum cmd_error_code cmd_ret
= CMD_SUCCESS
;
499 const unsigned int string_arg_count
=
500 lttng_dynamic_pointer_array_get_count(
501 &command_args
->string_args
);
502 enum lttng_process_attr process_attr
= command_args
->process_attr
;
506 * Check that this process attribute can be tracked
507 * in the user space domain. Backward-compatibility
508 * changes are be applied to process_attr as needed.
510 if (!ust_process_attr_supported(&process_attr
)) {
517 /* Open tracker and trackers elements */
518 const int ret
= mi_lttng_process_attribute_tracker_open(
519 writer
, process_attr
);
526 if (command_args
->all
) {
527 cmd_ret
= run_command_all(cmd_type
, session_name
, domain_type
,
528 process_attr
, writer
);
530 bool error_occurred
= false;
532 for (i
= 0; i
< string_arg_count
; i
++) {
533 const char *arg
= lttng_dynamic_pointer_array_get_pointer(
534 &command_args
->string_args
, i
);
536 cmd_ret
= run_command_string(cmd_type
, session_name
,
537 domain_type
, process_attr
, arg
, writer
);
538 if (cmd_ret
!= CMD_SUCCESS
) {
539 error_occurred
= true;
540 if (cmd_ret
== CMD_FATAL
) {
546 if (error_occurred
) {
552 /* Close tracker and trackers elements */
553 const int ret
= mi_lttng_close_multi_element(
565 * Add/remove tracker to/from session.
567 static int cmd_track_untrack(enum cmd_type cmd_type
,
570 const char *help_msg
)
573 bool sub_command_failed
= false;
574 bool opt_all
= false;
575 unsigned int selected_process_attr_tracker_count
= 0;
576 const unsigned int command_count
=
577 sizeof(process_attr_commands
) /
578 sizeof(struct process_attr_command_args
);
579 enum cmd_error_code command_ret
= CMD_SUCCESS
;
580 static poptContext pc
;
581 char *session_name
= NULL
;
582 const char *leftover
= NULL
;
583 struct mi_writer
*writer
= NULL
;
586 for (i
= 0; i
< command_count
; i
++) {
587 process_attr_command_init(&process_attr_commands
[i
], i
);
591 command_ret
= CMD_ERROR
;
595 pc
= poptGetContext(NULL
, argc
, argv
, long_options
, 0);
596 poptReadDefaultConfig(pc
, 0);
598 while ((opt
= poptGetNextOpt(pc
)) != -1) {
603 case OPT_LIST_OPTIONS
:
604 list_cmd_options(stdout
, long_options
);
614 /* See OPT_ enum declaration comment. */
616 selected_process_attr_tracker_count
++;
617 process_attr_commands
[opt
].requested
= true;
621 ret
= lttng_dynamic_pointer_array_add_pointer(
622 &process_attr_commands
[opt
].string_args
,
625 ERR("Allocation failed while parsing command arguments");
626 command_ret
= CMD_ERROR
;
634 command_ret
= CMD_UNDEFINED
;
639 ret
= print_missing_or_multiple_domains(
640 opt_kernel
+ opt_userspace
, false);
642 command_ret
= CMD_ERROR
;
646 if (selected_process_attr_tracker_count
== 0) {
647 ERR("At least one process attribute must be specified");
648 command_ret
= CMD_ERROR
;
653 * Only one process attribute tracker was specified; find it
654 * and set it in 'all' mode.
656 for (i
= 0; i
< command_count
; i
++) {
657 if (!process_attr_commands
[i
].requested
) {
660 process_attr_commands
[i
].all
= true;
661 if (lttng_dynamic_pointer_array_get_count(
662 &process_attr_commands
[i
]
664 ERR("The --all option cannot be used with a list of process attribute values");
665 command_ret
= CMD_ERROR
;
670 for (i
= 0; i
< command_count
; i
++) {
671 if (!process_attr_commands
[i
].requested
) {
674 if (lttng_dynamic_pointer_array_get_count(
675 &process_attr_commands
[i
]
676 .string_args
) == 0) {
677 ERR("No process attribute value specified for %s tracker",
678 get_capitalized_process_attr_str(
679 process_attr_commands
[i
]
681 command_ret
= CMD_ERROR
;
687 if (!opt_session_name
) {
688 session_name
= get_session_name();
689 if (session_name
== NULL
) {
690 command_ret
= CMD_ERROR
;
694 session_name
= opt_session_name
;
697 leftover
= poptGetArg(pc
);
699 ERR("Unknown argument: %s", leftover
);
700 command_ret
= CMD_ERROR
;
706 writer
= mi_lttng_writer_create(fileno(stdout
), lttng_opt_mi
);
708 command_ret
= CMD_ERROR
;
714 /* Open command element */
715 ret
= mi_lttng_writer_command_open(writer
,
716 get_mi_element_command(cmd_type
));
718 command_ret
= CMD_ERROR
;
722 /* Open output element */
723 ret
= mi_lttng_writer_open_element(writer
,
724 mi_lttng_element_command_output
);
726 command_ret
= CMD_ERROR
;
730 ret
= mi_lttng_trackers_open(writer
);
736 /* Execute sub-commands. */
737 for (i
= 0; i
< command_count
; i
++) {
738 if (!process_attr_commands
[i
].requested
) {
741 command_ret
= run_command(cmd_type
, session_name
,
742 &process_attr_commands
[i
], writer
);
743 if (command_ret
!= CMD_SUCCESS
) {
744 sub_command_failed
= true;
745 if (command_ret
== CMD_FATAL
) {
753 /* Close trackers and output elements */
754 ret
= mi_lttng_close_multi_element(writer
, 2);
756 command_ret
= CMD_ERROR
;
761 ret
= mi_lttng_writer_write_element_bool(writer
,
762 mi_lttng_element_command_success
,
763 !sub_command_failed
);
765 command_ret
= CMD_ERROR
;
769 /* Command element close */
770 ret
= mi_lttng_writer_command_close(writer
);
772 command_ret
= CMD_ERROR
;
778 if (!opt_session_name
) {
783 if (writer
&& mi_lttng_writer_destroy(writer
)) {
784 /* Preserve original error code */
785 command_ret
= CMD_ERROR
;
788 for (i
= 0; i
< command_count
; i
++) {
789 process_attr_command_fini(&process_attr_commands
[i
]);
793 return (int) command_ret
;
796 int cmd_track(int argc
, const char **argv
)
798 static const char *help_msg
=
799 #ifdef LTTNG_EMBED_HELP
800 #include <lttng-track.1.h>
806 return cmd_track_untrack(CMD_TRACK
, argc
, argv
, help_msg
);
809 int cmd_untrack(int argc
, const char **argv
)
811 static const char *help_msg
=
812 #ifdef LTTNG_EMBED_HELP
813 #include <lttng-untrack.1.h>
819 return cmd_track_untrack(CMD_UNTRACK
, argc
, argv
, help_msg
);