2 * Copyright (C) 2011 EfficiOS Inc.
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>
21 #include <urcu/list.h>
23 #include <common/dynamic-array.h>
24 #include <common/mi-lttng.h>
25 #include <common/optional.h>
26 #include <common/dynamic-buffer.h>
27 #include <common/tracker.h>
29 #include <lttng/lttng.h>
31 #include "../command.h"
33 struct process_attr_command_args
{
34 enum lttng_process_attr process_attr
;
35 /* Present in the user's command. */
38 struct lttng_dynamic_pointer_array string_args
;
46 /* Offset OPT_ values by one since libpopt gives '0' a special meaning. */
48 OPT_PID
= LTTNG_PROCESS_ATTR_PROCESS_ID
+ 1,
49 OPT_VPID
= LTTNG_PROCESS_ATTR_VIRTUAL_PROCESS_ID
+ 1,
50 OPT_UID
= LTTNG_PROCESS_ATTR_USER_ID
+ 1,
51 OPT_VUID
= LTTNG_PROCESS_ATTR_VIRTUAL_USER_ID
+ 1,
52 OPT_GID
= LTTNG_PROCESS_ATTR_GROUP_ID
+ 1,
53 OPT_VGID
= LTTNG_PROCESS_ATTR_VIRTUAL_GROUP_ID
+ 1,
60 static char *opt_session_name
;
61 static int opt_kernel
;
62 static int opt_userspace
;
63 static char *opt_str_arg
;
65 static struct poptOption long_options
[] = {
66 /* { longName, shortName, argInfo, argPtr, value, descrip, argDesc, } */
67 { "help", 'h', POPT_ARG_NONE
, 0, OPT_HELP
, 0, 0, },
68 { "session", 's', POPT_ARG_STRING
, &opt_session_name
, OPT_SESSION
, 0, 0, },
69 { "kernel", 'k', POPT_ARG_VAL
, &opt_kernel
, 1, 0, 0, },
70 { "userspace", 'u', POPT_ARG_VAL
, &opt_userspace
, 1, 0, 0, },
71 { "pid", 'p', POPT_ARG_STRING
| POPT_ARGFLAG_OPTIONAL
, &opt_str_arg
, OPT_PID
, 0, 0, },
72 { "vpid", 0, POPT_ARG_STRING
| POPT_ARGFLAG_OPTIONAL
, &opt_str_arg
, OPT_VPID
, 0, 0, },
73 { "uid", 0, POPT_ARG_STRING
| POPT_ARGFLAG_OPTIONAL
, &opt_str_arg
, OPT_UID
, 0, 0, },
74 { "vuid", 0, POPT_ARG_STRING
| POPT_ARGFLAG_OPTIONAL
, &opt_str_arg
, OPT_VUID
, 0, 0, },
75 { "gid", 0, POPT_ARG_STRING
| POPT_ARGFLAG_OPTIONAL
, &opt_str_arg
, OPT_GID
, 0, 0, },
76 { "vgid", 0, POPT_ARG_STRING
| POPT_ARGFLAG_OPTIONAL
, &opt_str_arg
, OPT_VGID
, 0, 0, },
77 { "all", 'a', POPT_ARG_NONE
, 0, OPT_ALL
, 0, 0, },
78 { "list-options", 0, POPT_ARG_NONE
, NULL
, OPT_LIST_OPTIONS
, 0, 0, },
79 { 0, 0, 0, 0, 0, 0, 0, },
82 static struct process_attr_command_args
83 process_attr_commands
[LTTNG_PROCESS_ATTR_VIRTUAL_GROUP_ID
+ 1];
85 static void process_attr_command_init(struct process_attr_command_args
*cmd
,
86 enum lttng_process_attr process_attr
)
88 cmd
->process_attr
= process_attr
;
90 lttng_dynamic_pointer_array_init(&cmd
->string_args
, NULL
);
93 static void process_attr_command_fini(struct process_attr_command_args
*cmd
)
95 lttng_dynamic_pointer_array_reset(&cmd
->string_args
);
98 static const char *get_capitalized_process_attr_str(enum lttng_process_attr process_attr
)
100 switch (process_attr
) {
101 case LTTNG_PROCESS_ATTR_PROCESS_ID
:
103 case LTTNG_PROCESS_ATTR_VIRTUAL_PROCESS_ID
:
104 return "Virtual process ID";
105 case LTTNG_PROCESS_ATTR_USER_ID
:
107 case LTTNG_PROCESS_ATTR_VIRTUAL_USER_ID
:
108 return "Virtual user ID";
109 case LTTNG_PROCESS_ATTR_GROUP_ID
:
111 case LTTNG_PROCESS_ATTR_VIRTUAL_GROUP_ID
:
112 return "Virtual group ID";
119 static bool ust_process_attr_supported(enum lttng_process_attr
*process_attr
)
123 switch (*process_attr
) {
124 case LTTNG_PROCESS_ATTR_PROCESS_ID
:
125 *process_attr
= LTTNG_PROCESS_ATTR_VIRTUAL_PROCESS_ID
;
127 case LTTNG_PROCESS_ATTR_VIRTUAL_PROCESS_ID
:
128 case LTTNG_PROCESS_ATTR_VIRTUAL_USER_ID
:
129 case LTTNG_PROCESS_ATTR_VIRTUAL_GROUP_ID
:
133 ERR("The %s process attribute cannot be tracked in the user space domain.",
134 lttng_process_attr_to_string(*process_attr
));
141 static const char *get_mi_element_command(enum cmd_type cmd_type
)
145 return mi_lttng_element_command_track
;
147 return mi_lttng_element_command_untrack
;
153 static enum cmd_error_code
run_command_all(enum cmd_type cmd_type
,
154 const char *session_name
,
155 enum lttng_domain_type domain_type
,
156 enum lttng_process_attr process_attr
,
157 struct mi_writer
*writer
)
159 struct lttng_process_attr_tracker_handle
*tracker_handle
= NULL
;
160 const enum lttng_error_code handle_ret_code
=
161 lttng_session_get_tracker_handle(session_name
,
162 domain_type
, process_attr
,
164 enum cmd_error_code cmd_ret
= CMD_SUCCESS
;
165 enum lttng_process_attr_tracker_handle_status status
;
168 const int ret
= mi_lttng_all_process_attribute_value(
169 writer
, process_attr
, true);
176 if (handle_ret_code
!= LTTNG_OK
) {
177 ERR("Session `%s` does not exist", session_name
);
182 status
= lttng_process_attr_tracker_handle_set_tracking_policy(
184 cmd_type
== CMD_TRACK
?
185 LTTNG_TRACKING_POLICY_INCLUDE_ALL
:
186 LTTNG_TRACKING_POLICY_EXCLUDE_ALL
);
188 case LTTNG_PROCESS_ATTR_TRACKER_HANDLE_STATUS_OK
:
189 if (cmd_type
== CMD_TRACK
) {
190 MSG("%s tracking policy set to `include all`",
191 get_capitalized_process_attr_str(process_attr
));
193 MSG("%s tracking policy set to `exclude all`",
194 get_capitalized_process_attr_str(process_attr
));
197 case LTTNG_PROCESS_ATTR_TRACKER_HANDLE_STATUS_SESSION_DOES_NOT_EXIST
:
198 ERR("%s", lttng_strerror(-LTTNG_ERR_SESS_NOT_FOUND
));
201 ERR("Unknown error encountered while setting tracking policy of %s tracker to `%s`",
202 lttng_process_attr_to_string(process_attr
),
203 cmd_type
== CMD_TRACK
? "include all" :
210 int ret
= mi_lttng_writer_write_element_bool(writer
,
211 mi_lttng_element_success
,
212 cmd_ret
== CMD_SUCCESS
);
217 ret
= mi_lttng_writer_close_element(writer
);
218 cmd_ret
= ret
== 0 ? cmd_ret
: CMD_FATAL
;
221 lttng_process_attr_tracker_handle_destroy(tracker_handle
);
225 static enum cmd_error_code
run_command_string(enum cmd_type cmd_type
,
226 const char *session_name
,
227 enum lttng_domain_type domain_type
,
228 enum lttng_process_attr process_attr
,
230 struct mi_writer
*writer
)
232 struct lttng_process_attr_tracker_handle
*tracker_handle
= NULL
;
233 const enum lttng_error_code handle_ret_code
=
234 lttng_session_get_tracker_handle(session_name
,
235 domain_type
, process_attr
,
237 enum cmd_error_code cmd_ret
= CMD_SUCCESS
;
238 const char *one_value_str
;
239 char *args
= strdup(_args
);
241 bool policy_set
= false;
244 ERR("%s", lttng_strerror(-LTTNG_ERR_NOMEM
));
249 if (handle_ret_code
!= LTTNG_OK
) {
250 ERR("%s", lttng_strerror(-handle_ret_code
));
255 while ((one_value_str
= strtok_r(iter
, ",", &iter
)) != NULL
) {
256 const bool is_numerical_argument
= isdigit(one_value_str
[0]);
257 enum lttng_process_attr_tracker_handle_status status
;
258 enum lttng_tracking_policy policy
;
260 char *prettified_arg
;
263 status
= lttng_process_attr_tracker_handle_get_tracking_policy(
264 tracker_handle
, &policy
);
265 if (status
!= LTTNG_PROCESS_ATTR_TRACKER_HANDLE_STATUS_OK
) {
269 if (policy
!= LTTNG_TRACKING_POLICY_INCLUDE_SET
) {
270 status
= lttng_process_attr_tracker_handle_set_tracking_policy(
272 LTTNG_TRACKING_POLICY_INCLUDE_SET
);
273 if (status
!= LTTNG_PROCESS_ATTR_TRACKER_HANDLE_STATUS_OK
) {
280 if (is_numerical_argument
) {
281 const unsigned long one_value_int
=
282 strtoul(one_value_str
, NULL
, 10);
285 ret
= mi_lttng_integral_process_attribute_value(
286 writer
, process_attr
,
287 (int64_t) one_value_int
, true);
294 switch (process_attr
) {
295 case LTTNG_PROCESS_ATTR_PROCESS_ID
:
296 status
= cmd_type
== CMD_TRACK
?
297 lttng_process_attr_process_id_tracker_handle_add_pid(
299 (pid_t
) one_value_int
) :
300 lttng_process_attr_process_id_tracker_handle_remove_pid(
302 (pid_t
) one_value_int
);
304 case LTTNG_PROCESS_ATTR_VIRTUAL_PROCESS_ID
:
305 status
= cmd_type
== CMD_TRACK
?
306 lttng_process_attr_virtual_process_id_tracker_handle_add_pid(
308 (pid_t
) one_value_int
) :
309 lttng_process_attr_virtual_process_id_tracker_handle_remove_pid(
311 (pid_t
) one_value_int
);
313 case LTTNG_PROCESS_ATTR_USER_ID
:
314 status
= cmd_type
== CMD_TRACK
?
315 lttng_process_attr_user_id_tracker_handle_add_uid(
317 (uid_t
) one_value_int
) :
318 lttng_process_attr_user_id_tracker_handle_remove_uid(
320 (uid_t
) one_value_int
);
322 case LTTNG_PROCESS_ATTR_VIRTUAL_USER_ID
:
323 status
= cmd_type
== CMD_TRACK
?
324 lttng_process_attr_virtual_user_id_tracker_handle_add_uid(
326 (uid_t
) one_value_int
) :
327 lttng_process_attr_virtual_user_id_tracker_handle_remove_uid(
329 (uid_t
) one_value_int
);
331 case LTTNG_PROCESS_ATTR_GROUP_ID
:
332 status
= cmd_type
== CMD_TRACK
?
333 lttng_process_attr_group_id_tracker_handle_add_gid(
335 (gid_t
) one_value_int
) :
336 lttng_process_attr_group_id_tracker_handle_remove_gid(
338 (gid_t
) one_value_int
);
340 case LTTNG_PROCESS_ATTR_VIRTUAL_GROUP_ID
:
341 status
= cmd_type
== CMD_TRACK
?
342 lttng_process_attr_virtual_group_id_tracker_handle_add_gid(
344 (gid_t
) one_value_int
) :
345 lttng_process_attr_virtual_group_id_tracker_handle_remove_gid(
347 (gid_t
) one_value_int
);
355 ret
= mi_lttng_string_process_attribute_value(
356 writer
, process_attr
,
357 one_value_str
, true);
364 switch (process_attr
) {
365 case LTTNG_PROCESS_ATTR_USER_ID
:
366 status
= cmd_type
== CMD_TRACK
?
367 lttng_process_attr_user_id_tracker_handle_add_user_name(
370 lttng_process_attr_user_id_tracker_handle_remove_user_name(
374 case LTTNG_PROCESS_ATTR_VIRTUAL_USER_ID
:
375 status
= cmd_type
== CMD_TRACK
?
376 lttng_process_attr_virtual_user_id_tracker_handle_add_user_name(
379 lttng_process_attr_virtual_user_id_tracker_handle_remove_user_name(
383 case LTTNG_PROCESS_ATTR_GROUP_ID
:
384 status
= cmd_type
== CMD_TRACK
?
385 lttng_process_attr_group_id_tracker_handle_add_group_name(
388 lttng_process_attr_group_id_tracker_handle_remove_group_name(
392 case LTTNG_PROCESS_ATTR_VIRTUAL_GROUP_ID
:
393 status
= cmd_type
== CMD_TRACK
?
394 lttng_process_attr_virtual_group_id_tracker_handle_add_group_name(
397 lttng_process_attr_virtual_group_id_tracker_handle_remove_group_name(
402 ERR("%s is not a valid %s value; expected an integer",
404 lttng_process_attr_to_string(
411 ret
= asprintf(&prettified_arg
,
412 is_numerical_argument
? "%s" : "`%s`",
415 PERROR("Failed to format argument `%s`", one_value_str
);
421 case LTTNG_PROCESS_ATTR_TRACKER_HANDLE_STATUS_OK
:
422 if (cmd_type
== CMD_TRACK
) {
423 MSG("Added %s to the %s tracker inclusion set",
425 lttng_process_attr_to_string(
428 MSG("Removed %s from the %s tracker inclusion set",
430 lttng_process_attr_to_string(
434 case LTTNG_PROCESS_ATTR_TRACKER_HANDLE_STATUS_SESSION_DOES_NOT_EXIST
:
435 ERR("Session `%s` not found", session_name
);
437 case LTTNG_PROCESS_ATTR_TRACKER_HANDLE_STATUS_EXISTS
:
438 WARN("%s is already in the %s inclusion set",
440 lttng_process_attr_to_string(
443 case LTTNG_PROCESS_ATTR_TRACKER_HANDLE_STATUS_MISSING
:
444 WARN("%s is not in the %s the inclusion set",
446 lttng_process_attr_to_string(
449 case LTTNG_PROCESS_ATTR_TRACKER_HANDLE_STATUS_USER_NOT_FOUND
:
450 ERR("User %s was not found", prettified_arg
);
453 case LTTNG_PROCESS_ATTR_TRACKER_HANDLE_STATUS_GROUP_NOT_FOUND
:
454 ERR("Group %s was not found", prettified_arg
);
458 ERR("Unknown error encountered while %s %s %s %s tracker's inclusion set",
459 cmd_type
== CMD_TRACK
? "adding" :
461 lttng_process_attr_to_string(
464 cmd_type
== CMD_TRACK
? "to" : "from");
468 free(prettified_arg
);
471 ret
= mi_lttng_writer_write_element_bool(writer
,
472 mi_lttng_element_success
,
473 cmd_ret
== CMD_SUCCESS
);
478 ret
= mi_lttng_writer_close_element(writer
);
479 cmd_ret
= ret
== 0 ? cmd_ret
: CMD_FATAL
;
485 lttng_process_attr_tracker_handle_destroy(tracker_handle
);
489 static enum cmd_error_code
run_command(enum cmd_type cmd_type
,
490 const char *session_name
,
491 const struct process_attr_command_args
*command_args
,
492 struct mi_writer
*writer
)
494 const enum lttng_domain_type domain_type
=
495 opt_kernel
? LTTNG_DOMAIN_KERNEL
: LTTNG_DOMAIN_UST
;
496 enum cmd_error_code cmd_ret
= CMD_SUCCESS
;
498 const unsigned int string_arg_count
=
499 lttng_dynamic_pointer_array_get_count(
500 &command_args
->string_args
);
501 enum lttng_process_attr process_attr
= command_args
->process_attr
;
505 * Check that this process attribute can be tracked
506 * in the user space domain. Backward-compatibility
507 * changes are be applied to process_attr as needed.
509 if (!ust_process_attr_supported(&process_attr
)) {
516 /* Open tracker and trackers elements */
517 const int ret
= mi_lttng_process_attribute_tracker_open(
518 writer
, process_attr
);
525 if (command_args
->all
) {
526 cmd_ret
= run_command_all(cmd_type
, session_name
, domain_type
,
527 process_attr
, writer
);
529 bool error_occurred
= false;
531 for (i
= 0; i
< string_arg_count
; i
++) {
532 const char *arg
= (const char *) lttng_dynamic_pointer_array_get_pointer(
533 &command_args
->string_args
, i
);
535 cmd_ret
= run_command_string(cmd_type
, session_name
,
536 domain_type
, process_attr
, arg
, writer
);
537 if (cmd_ret
!= CMD_SUCCESS
) {
538 error_occurred
= true;
539 if (cmd_ret
== CMD_FATAL
) {
545 if (error_occurred
) {
551 /* Close tracker and trackers elements */
552 const int ret
= mi_lttng_close_multi_element(
564 * Add/remove tracker to/from session.
566 static int cmd_track_untrack(enum cmd_type cmd_type
,
569 const char *help_msg
)
572 bool sub_command_failed
= false;
573 bool opt_all
= false;
574 unsigned int selected_process_attr_tracker_count
= 0;
575 const unsigned int command_count
=
576 sizeof(process_attr_commands
) /
577 sizeof(struct process_attr_command_args
);
578 enum cmd_error_code command_ret
= CMD_SUCCESS
;
579 static poptContext pc
;
580 char *session_name
= NULL
;
581 const char *leftover
= NULL
;
582 struct mi_writer
*writer
= NULL
;
585 for (i
= 0; i
< command_count
; i
++) {
586 process_attr_command_init(&process_attr_commands
[i
], (lttng_process_attr
) i
);
590 command_ret
= CMD_ERROR
;
594 pc
= poptGetContext(NULL
, argc
, argv
, long_options
, 0);
595 poptReadDefaultConfig(pc
, 0);
597 while ((opt
= poptGetNextOpt(pc
)) != -1) {
602 case OPT_LIST_OPTIONS
:
603 list_cmd_options(stdout
, long_options
);
613 /* See OPT_ enum declaration comment. */
615 selected_process_attr_tracker_count
++;
616 process_attr_commands
[opt
].requested
= true;
620 ret
= lttng_dynamic_pointer_array_add_pointer(
621 &process_attr_commands
[opt
].string_args
,
624 ERR("Allocation failed while parsing command arguments");
625 command_ret
= CMD_ERROR
;
633 command_ret
= CMD_UNDEFINED
;
638 ret
= print_missing_or_multiple_domains(
639 opt_kernel
+ opt_userspace
, false);
641 command_ret
= CMD_ERROR
;
645 if (selected_process_attr_tracker_count
== 0) {
646 ERR("At least one process attribute must be specified");
647 command_ret
= CMD_ERROR
;
652 * Only one process attribute tracker was specified; find it
653 * and set it in 'all' mode.
655 for (i
= 0; i
< command_count
; i
++) {
656 if (!process_attr_commands
[i
].requested
) {
659 process_attr_commands
[i
].all
= true;
660 if (lttng_dynamic_pointer_array_get_count(
661 &process_attr_commands
[i
]
663 ERR("The --all option cannot be used with a list of process attribute values");
664 command_ret
= CMD_ERROR
;
669 for (i
= 0; i
< command_count
; i
++) {
670 if (!process_attr_commands
[i
].requested
) {
673 if (lttng_dynamic_pointer_array_get_count(
674 &process_attr_commands
[i
]
675 .string_args
) == 0) {
676 ERR("No process attribute value specified for %s tracker",
677 get_capitalized_process_attr_str(
678 process_attr_commands
[i
]
680 command_ret
= CMD_ERROR
;
686 if (!opt_session_name
) {
687 session_name
= get_session_name();
688 if (session_name
== NULL
) {
689 command_ret
= CMD_ERROR
;
693 session_name
= opt_session_name
;
696 leftover
= poptGetArg(pc
);
698 ERR("Unknown argument: %s", leftover
);
699 command_ret
= CMD_ERROR
;
705 writer
= mi_lttng_writer_create(fileno(stdout
), lttng_opt_mi
);
707 command_ret
= CMD_ERROR
;
713 /* Open command element */
714 ret
= mi_lttng_writer_command_open(writer
,
715 get_mi_element_command(cmd_type
));
717 command_ret
= CMD_ERROR
;
721 /* Open output element */
722 ret
= mi_lttng_writer_open_element(writer
,
723 mi_lttng_element_command_output
);
725 command_ret
= CMD_ERROR
;
729 ret
= mi_lttng_trackers_open(writer
);
735 /* Execute sub-commands. */
736 for (i
= 0; i
< command_count
; i
++) {
737 if (!process_attr_commands
[i
].requested
) {
740 command_ret
= run_command(cmd_type
, session_name
,
741 &process_attr_commands
[i
], writer
);
742 if (command_ret
!= CMD_SUCCESS
) {
743 sub_command_failed
= true;
744 if (command_ret
== CMD_FATAL
) {
752 /* Close trackers and output elements */
753 ret
= mi_lttng_close_multi_element(writer
, 2);
755 command_ret
= CMD_ERROR
;
760 ret
= mi_lttng_writer_write_element_bool(writer
,
761 mi_lttng_element_command_success
,
762 !sub_command_failed
);
764 command_ret
= CMD_ERROR
;
768 /* Command element close */
769 ret
= mi_lttng_writer_command_close(writer
);
771 command_ret
= CMD_ERROR
;
777 if (!opt_session_name
) {
782 if (writer
&& mi_lttng_writer_destroy(writer
)) {
783 /* Preserve original error code */
784 command_ret
= CMD_ERROR
;
787 for (i
= 0; i
< command_count
; i
++) {
788 process_attr_command_fini(&process_attr_commands
[i
]);
792 return (int) command_ret
;
795 int cmd_track(int argc
, const char **argv
)
797 static const char *help_msg
=
798 #ifdef LTTNG_EMBED_HELP
799 #include <lttng-track.1.h>
805 return cmd_track_untrack(CMD_TRACK
, argc
, argv
, help_msg
);
808 int cmd_untrack(int argc
, const char **argv
)
810 static const char *help_msg
=
811 #ifdef LTTNG_EMBED_HELP
812 #include <lttng-untrack.1.h>
818 return cmd_track_untrack(CMD_UNTRACK
, argc
, argv
, help_msg
);