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.hpp>
24 #include <common/mi-lttng.hpp>
25 #include <common/optional.hpp>
26 #include <common/dynamic-buffer.hpp>
27 #include <common/tracker.hpp>
29 #include <lttng/lttng.h>
31 #include "../command.hpp"
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
;
48 /* Offset OPT_ values by one since libpopt gives '0' a special meaning. */
50 OPT_PID
= LTTNG_PROCESS_ATTR_PROCESS_ID
+ 1,
51 OPT_VPID
= LTTNG_PROCESS_ATTR_VIRTUAL_PROCESS_ID
+ 1,
52 OPT_UID
= LTTNG_PROCESS_ATTR_USER_ID
+ 1,
53 OPT_VUID
= LTTNG_PROCESS_ATTR_VIRTUAL_USER_ID
+ 1,
54 OPT_GID
= LTTNG_PROCESS_ATTR_GROUP_ID
+ 1,
55 OPT_VGID
= LTTNG_PROCESS_ATTR_VIRTUAL_GROUP_ID
+ 1,
62 static char *opt_session_name
;
63 static int opt_kernel
;
64 static int opt_userspace
;
65 static char *opt_str_arg
;
67 static struct poptOption long_options
[] = {
68 /* { longName, shortName, argInfo, argPtr, value, descrip, argDesc, } */
69 { "help", 'h', POPT_ARG_NONE
, 0, OPT_HELP
, 0, 0, },
70 { "session", 's', POPT_ARG_STRING
, &opt_session_name
, OPT_SESSION
, 0, 0, },
71 { "kernel", 'k', POPT_ARG_VAL
, &opt_kernel
, 1, 0, 0, },
72 { "userspace", 'u', POPT_ARG_VAL
, &opt_userspace
, 1, 0, 0, },
73 { "pid", 'p', POPT_ARG_STRING
| POPT_ARGFLAG_OPTIONAL
, &opt_str_arg
, OPT_PID
, 0, 0, },
74 { "vpid", 0, POPT_ARG_STRING
| POPT_ARGFLAG_OPTIONAL
, &opt_str_arg
, OPT_VPID
, 0, 0, },
75 { "uid", 0, POPT_ARG_STRING
| POPT_ARGFLAG_OPTIONAL
, &opt_str_arg
, OPT_UID
, 0, 0, },
76 { "vuid", 0, POPT_ARG_STRING
| POPT_ARGFLAG_OPTIONAL
, &opt_str_arg
, OPT_VUID
, 0, 0, },
77 { "gid", 0, POPT_ARG_STRING
| POPT_ARGFLAG_OPTIONAL
, &opt_str_arg
, OPT_GID
, 0, 0, },
78 { "vgid", 0, POPT_ARG_STRING
| POPT_ARGFLAG_OPTIONAL
, &opt_str_arg
, OPT_VGID
, 0, 0, },
79 { "all", 'a', POPT_ARG_NONE
, 0, OPT_ALL
, 0, 0, },
80 { "list-options", 0, POPT_ARG_NONE
, NULL
, OPT_LIST_OPTIONS
, 0, 0, },
81 { 0, 0, 0, 0, 0, 0, 0, },
84 static struct process_attr_command_args
85 process_attr_commands
[LTTNG_PROCESS_ATTR_VIRTUAL_GROUP_ID
+ 1];
87 static void process_attr_command_init(struct process_attr_command_args
*cmd
,
88 enum lttng_process_attr process_attr
)
90 cmd
->process_attr
= process_attr
;
92 lttng_dynamic_pointer_array_init(&cmd
->string_args
, free
);
95 static void process_attr_command_fini(struct process_attr_command_args
*cmd
)
97 lttng_dynamic_pointer_array_reset(&cmd
->string_args
);
100 static const char *get_capitalized_process_attr_str(enum lttng_process_attr process_attr
)
102 switch (process_attr
) {
103 case LTTNG_PROCESS_ATTR_PROCESS_ID
:
105 case LTTNG_PROCESS_ATTR_VIRTUAL_PROCESS_ID
:
106 return "Virtual process ID";
107 case LTTNG_PROCESS_ATTR_USER_ID
:
109 case LTTNG_PROCESS_ATTR_VIRTUAL_USER_ID
:
110 return "Virtual user ID";
111 case LTTNG_PROCESS_ATTR_GROUP_ID
:
113 case LTTNG_PROCESS_ATTR_VIRTUAL_GROUP_ID
:
114 return "Virtual group ID";
121 static bool ust_process_attr_supported(enum lttng_process_attr
*process_attr
)
125 switch (*process_attr
) {
126 case LTTNG_PROCESS_ATTR_PROCESS_ID
:
127 *process_attr
= LTTNG_PROCESS_ATTR_VIRTUAL_PROCESS_ID
;
129 case LTTNG_PROCESS_ATTR_VIRTUAL_PROCESS_ID
:
130 case LTTNG_PROCESS_ATTR_VIRTUAL_USER_ID
:
131 case LTTNG_PROCESS_ATTR_VIRTUAL_GROUP_ID
:
135 ERR("The %s process attribute cannot be tracked in the user space domain.",
136 lttng_process_attr_to_string(*process_attr
));
143 static const char *get_mi_element_command(enum cmd_type cmd_type
)
147 return mi_lttng_element_command_track
;
149 return mi_lttng_element_command_untrack
;
155 static enum cmd_error_code
run_command_all(enum cmd_type cmd_type
,
156 const char *session_name
,
157 enum lttng_domain_type domain_type
,
158 enum lttng_process_attr process_attr
,
159 struct mi_writer
*writer
)
161 struct lttng_process_attr_tracker_handle
*tracker_handle
= NULL
;
162 const enum lttng_error_code handle_ret_code
=
163 lttng_session_get_tracker_handle(session_name
,
164 domain_type
, process_attr
,
166 enum cmd_error_code cmd_ret
= CMD_SUCCESS
;
167 enum lttng_process_attr_tracker_handle_status status
;
170 const int ret
= mi_lttng_all_process_attribute_value(
171 writer
, process_attr
, true);
178 if (handle_ret_code
!= LTTNG_OK
) {
179 ERR("Session `%s` does not exist", session_name
);
184 status
= lttng_process_attr_tracker_handle_set_tracking_policy(
186 cmd_type
== CMD_TRACK
?
187 LTTNG_TRACKING_POLICY_INCLUDE_ALL
:
188 LTTNG_TRACKING_POLICY_EXCLUDE_ALL
);
190 case LTTNG_PROCESS_ATTR_TRACKER_HANDLE_STATUS_OK
:
191 if (cmd_type
== CMD_TRACK
) {
192 MSG("%s tracking policy set to `include all`",
193 get_capitalized_process_attr_str(process_attr
));
195 MSG("%s tracking policy set to `exclude all`",
196 get_capitalized_process_attr_str(process_attr
));
199 case LTTNG_PROCESS_ATTR_TRACKER_HANDLE_STATUS_SESSION_DOES_NOT_EXIST
:
200 ERR("%s", lttng_strerror(-LTTNG_ERR_SESS_NOT_FOUND
));
203 ERR("Unknown error encountered while setting tracking policy of %s tracker to `%s`",
204 lttng_process_attr_to_string(process_attr
),
205 cmd_type
== CMD_TRACK
? "include all" :
212 int ret
= mi_lttng_writer_write_element_bool(writer
,
213 mi_lttng_element_success
,
214 cmd_ret
== CMD_SUCCESS
);
219 ret
= mi_lttng_writer_close_element(writer
);
220 cmd_ret
= ret
== 0 ? cmd_ret
: CMD_FATAL
;
223 lttng_process_attr_tracker_handle_destroy(tracker_handle
);
227 static enum cmd_error_code
run_command_string(enum cmd_type cmd_type
,
228 const char *session_name
,
229 enum lttng_domain_type domain_type
,
230 enum lttng_process_attr process_attr
,
232 struct mi_writer
*writer
)
234 struct lttng_process_attr_tracker_handle
*tracker_handle
= NULL
;
235 const enum lttng_error_code handle_ret_code
=
236 lttng_session_get_tracker_handle(session_name
,
237 domain_type
, process_attr
,
239 enum cmd_error_code cmd_ret
= CMD_SUCCESS
;
240 const char *one_value_str
;
241 char *args
= strdup(_args
);
243 bool policy_set
= false;
246 ERR("%s", lttng_strerror(-LTTNG_ERR_NOMEM
));
251 if (handle_ret_code
!= LTTNG_OK
) {
252 ERR("%s", lttng_strerror(-handle_ret_code
));
257 while ((one_value_str
= strtok_r(iter
, ",", &iter
)) != NULL
) {
258 const bool is_numerical_argument
= isdigit(one_value_str
[0]);
259 enum lttng_process_attr_tracker_handle_status status
;
260 enum lttng_tracking_policy policy
;
262 char *prettified_arg
;
265 status
= lttng_process_attr_tracker_handle_get_tracking_policy(
266 tracker_handle
, &policy
);
267 if (status
!= LTTNG_PROCESS_ATTR_TRACKER_HANDLE_STATUS_OK
) {
271 if (policy
!= LTTNG_TRACKING_POLICY_INCLUDE_SET
) {
272 status
= lttng_process_attr_tracker_handle_set_tracking_policy(
274 LTTNG_TRACKING_POLICY_INCLUDE_SET
);
275 if (status
!= LTTNG_PROCESS_ATTR_TRACKER_HANDLE_STATUS_OK
) {
282 if (is_numerical_argument
) {
283 const unsigned long one_value_int
=
284 strtoul(one_value_str
, NULL
, 10);
287 ret
= mi_lttng_integral_process_attribute_value(
288 writer
, process_attr
,
289 (int64_t) one_value_int
, true);
296 switch (process_attr
) {
297 case LTTNG_PROCESS_ATTR_PROCESS_ID
:
298 status
= cmd_type
== CMD_TRACK
?
299 lttng_process_attr_process_id_tracker_handle_add_pid(
301 (pid_t
) one_value_int
) :
302 lttng_process_attr_process_id_tracker_handle_remove_pid(
304 (pid_t
) one_value_int
);
306 case LTTNG_PROCESS_ATTR_VIRTUAL_PROCESS_ID
:
307 status
= cmd_type
== CMD_TRACK
?
308 lttng_process_attr_virtual_process_id_tracker_handle_add_pid(
310 (pid_t
) one_value_int
) :
311 lttng_process_attr_virtual_process_id_tracker_handle_remove_pid(
313 (pid_t
) one_value_int
);
315 case LTTNG_PROCESS_ATTR_USER_ID
:
316 status
= cmd_type
== CMD_TRACK
?
317 lttng_process_attr_user_id_tracker_handle_add_uid(
319 (uid_t
) one_value_int
) :
320 lttng_process_attr_user_id_tracker_handle_remove_uid(
322 (uid_t
) one_value_int
);
324 case LTTNG_PROCESS_ATTR_VIRTUAL_USER_ID
:
325 status
= cmd_type
== CMD_TRACK
?
326 lttng_process_attr_virtual_user_id_tracker_handle_add_uid(
328 (uid_t
) one_value_int
) :
329 lttng_process_attr_virtual_user_id_tracker_handle_remove_uid(
331 (uid_t
) one_value_int
);
333 case LTTNG_PROCESS_ATTR_GROUP_ID
:
334 status
= cmd_type
== CMD_TRACK
?
335 lttng_process_attr_group_id_tracker_handle_add_gid(
337 (gid_t
) one_value_int
) :
338 lttng_process_attr_group_id_tracker_handle_remove_gid(
340 (gid_t
) one_value_int
);
342 case LTTNG_PROCESS_ATTR_VIRTUAL_GROUP_ID
:
343 status
= cmd_type
== CMD_TRACK
?
344 lttng_process_attr_virtual_group_id_tracker_handle_add_gid(
346 (gid_t
) one_value_int
) :
347 lttng_process_attr_virtual_group_id_tracker_handle_remove_gid(
349 (gid_t
) one_value_int
);
357 ret
= mi_lttng_string_process_attribute_value(
358 writer
, process_attr
,
359 one_value_str
, true);
366 switch (process_attr
) {
367 case LTTNG_PROCESS_ATTR_USER_ID
:
368 status
= cmd_type
== CMD_TRACK
?
369 lttng_process_attr_user_id_tracker_handle_add_user_name(
372 lttng_process_attr_user_id_tracker_handle_remove_user_name(
376 case LTTNG_PROCESS_ATTR_VIRTUAL_USER_ID
:
377 status
= cmd_type
== CMD_TRACK
?
378 lttng_process_attr_virtual_user_id_tracker_handle_add_user_name(
381 lttng_process_attr_virtual_user_id_tracker_handle_remove_user_name(
385 case LTTNG_PROCESS_ATTR_GROUP_ID
:
386 status
= cmd_type
== CMD_TRACK
?
387 lttng_process_attr_group_id_tracker_handle_add_group_name(
390 lttng_process_attr_group_id_tracker_handle_remove_group_name(
394 case LTTNG_PROCESS_ATTR_VIRTUAL_GROUP_ID
:
395 status
= cmd_type
== CMD_TRACK
?
396 lttng_process_attr_virtual_group_id_tracker_handle_add_group_name(
399 lttng_process_attr_virtual_group_id_tracker_handle_remove_group_name(
404 ERR("%s is not a valid %s value; expected an integer",
406 lttng_process_attr_to_string(
413 ret
= asprintf(&prettified_arg
,
414 is_numerical_argument
? "%s" : "`%s`",
417 PERROR("Failed to format argument `%s`", one_value_str
);
423 case LTTNG_PROCESS_ATTR_TRACKER_HANDLE_STATUS_OK
:
424 if (cmd_type
== CMD_TRACK
) {
425 MSG("Added %s to the %s tracker inclusion set",
427 lttng_process_attr_to_string(
430 MSG("Removed %s from the %s tracker inclusion set",
432 lttng_process_attr_to_string(
436 case LTTNG_PROCESS_ATTR_TRACKER_HANDLE_STATUS_SESSION_DOES_NOT_EXIST
:
437 ERR("Session `%s` not found", session_name
);
439 case LTTNG_PROCESS_ATTR_TRACKER_HANDLE_STATUS_EXISTS
:
440 WARN("%s is already in the %s inclusion set",
442 lttng_process_attr_to_string(
445 case LTTNG_PROCESS_ATTR_TRACKER_HANDLE_STATUS_MISSING
:
446 WARN("%s is not in the %s the inclusion set",
448 lttng_process_attr_to_string(
451 case LTTNG_PROCESS_ATTR_TRACKER_HANDLE_STATUS_USER_NOT_FOUND
:
452 ERR("User %s was not found", prettified_arg
);
455 case LTTNG_PROCESS_ATTR_TRACKER_HANDLE_STATUS_GROUP_NOT_FOUND
:
456 ERR("Group %s was not found", prettified_arg
);
460 ERR("Unknown error encountered while %s %s %s %s tracker's inclusion set",
461 cmd_type
== CMD_TRACK
? "adding" :
463 lttng_process_attr_to_string(
466 cmd_type
== CMD_TRACK
? "to" : "from");
470 free(prettified_arg
);
473 ret
= mi_lttng_writer_write_element_bool(writer
,
474 mi_lttng_element_success
,
475 cmd_ret
== CMD_SUCCESS
);
480 ret
= mi_lttng_writer_close_element(writer
);
481 cmd_ret
= ret
== 0 ? cmd_ret
: CMD_FATAL
;
487 lttng_process_attr_tracker_handle_destroy(tracker_handle
);
491 static enum cmd_error_code
run_command(enum cmd_type cmd_type
,
492 const char *session_name
,
493 const struct process_attr_command_args
*command_args
,
494 struct mi_writer
*writer
)
496 const enum lttng_domain_type domain_type
=
497 opt_kernel
? LTTNG_DOMAIN_KERNEL
: LTTNG_DOMAIN_UST
;
498 enum cmd_error_code cmd_ret
= CMD_SUCCESS
;
500 const unsigned int string_arg_count
=
501 lttng_dynamic_pointer_array_get_count(
502 &command_args
->string_args
);
503 enum lttng_process_attr process_attr
= command_args
->process_attr
;
507 * Check that this process attribute can be tracked
508 * in the user space domain. Backward-compatibility
509 * changes are be applied to process_attr as needed.
511 if (!ust_process_attr_supported(&process_attr
)) {
518 /* Open tracker and trackers elements */
519 const int ret
= mi_lttng_process_attribute_tracker_open(
520 writer
, process_attr
);
527 if (command_args
->all
) {
528 cmd_ret
= run_command_all(cmd_type
, session_name
, domain_type
,
529 process_attr
, writer
);
531 bool error_occurred
= false;
533 for (i
= 0; i
< string_arg_count
; i
++) {
534 const char *arg
= (const char *) lttng_dynamic_pointer_array_get_pointer(
535 &command_args
->string_args
, i
);
537 cmd_ret
= run_command_string(cmd_type
, session_name
,
538 domain_type
, process_attr
, arg
, writer
);
539 if (cmd_ret
!= CMD_SUCCESS
) {
540 error_occurred
= true;
541 if (cmd_ret
== CMD_FATAL
) {
547 if (error_occurred
) {
553 /* Close tracker and trackers elements */
554 const int ret
= mi_lttng_close_multi_element(
566 * Add/remove tracker to/from session.
568 static int cmd_track_untrack(enum cmd_type cmd_type
,
571 const char *help_msg
__attribute__((unused
)))
574 bool sub_command_failed
= false;
575 bool opt_all
= false;
576 unsigned int selected_process_attr_tracker_count
= 0;
577 const unsigned int command_count
=
578 sizeof(process_attr_commands
) /
579 sizeof(struct process_attr_command_args
);
580 enum cmd_error_code command_ret
= CMD_SUCCESS
;
581 static poptContext pc
;
582 char *session_name
= NULL
;
583 const char *leftover
= NULL
;
584 struct mi_writer
*writer
= NULL
;
587 for (i
= 0; i
< command_count
; i
++) {
588 process_attr_command_init(&process_attr_commands
[i
], (lttng_process_attr
) i
);
592 command_ret
= CMD_ERROR
;
596 pc
= poptGetContext(NULL
, argc
, argv
, long_options
, 0);
597 poptReadDefaultConfig(pc
, 0);
599 while ((opt
= poptGetNextOpt(pc
)) != -1) {
604 case OPT_LIST_OPTIONS
:
605 list_cmd_options(stdout
, long_options
);
615 /* See OPT_ enum declaration comment. */
617 selected_process_attr_tracker_count
++;
618 process_attr_commands
[opt
].requested
= true;
622 ret
= lttng_dynamic_pointer_array_add_pointer(
623 &process_attr_commands
[opt
].string_args
,
626 ERR("Allocation failed while parsing command arguments");
627 command_ret
= CMD_ERROR
;
635 command_ret
= CMD_UNDEFINED
;
640 ret
= print_missing_or_multiple_domains(
641 opt_kernel
+ opt_userspace
, false);
643 command_ret
= CMD_ERROR
;
647 if (selected_process_attr_tracker_count
== 0) {
648 ERR("At least one process attribute must be specified");
649 command_ret
= CMD_ERROR
;
654 * Only one process attribute tracker was specified; find it
655 * and set it in 'all' mode.
657 for (i
= 0; i
< command_count
; i
++) {
658 if (!process_attr_commands
[i
].requested
) {
661 process_attr_commands
[i
].all
= true;
662 if (lttng_dynamic_pointer_array_get_count(
663 &process_attr_commands
[i
]
665 ERR("The --all option cannot be used with a list of process attribute values");
666 command_ret
= CMD_ERROR
;
671 for (i
= 0; i
< command_count
; i
++) {
672 if (!process_attr_commands
[i
].requested
) {
675 if (lttng_dynamic_pointer_array_get_count(
676 &process_attr_commands
[i
]
677 .string_args
) == 0) {
678 ERR("No process attribute value specified for %s tracker",
679 get_capitalized_process_attr_str(
680 process_attr_commands
[i
]
682 command_ret
= CMD_ERROR
;
688 if (!opt_session_name
) {
689 session_name
= get_session_name();
690 if (session_name
== NULL
) {
691 command_ret
= CMD_ERROR
;
695 session_name
= opt_session_name
;
698 leftover
= poptGetArg(pc
);
700 ERR("Unknown argument: %s", leftover
);
701 command_ret
= CMD_ERROR
;
707 writer
= mi_lttng_writer_create(fileno(stdout
), lttng_opt_mi
);
709 command_ret
= CMD_ERROR
;
715 /* Open command element */
716 ret
= mi_lttng_writer_command_open(writer
,
717 get_mi_element_command(cmd_type
));
719 command_ret
= CMD_ERROR
;
723 /* Open output element */
724 ret
= mi_lttng_writer_open_element(writer
,
725 mi_lttng_element_command_output
);
727 command_ret
= CMD_ERROR
;
731 ret
= mi_lttng_trackers_open(writer
);
737 /* Execute sub-commands. */
738 for (i
= 0; i
< command_count
; i
++) {
739 if (!process_attr_commands
[i
].requested
) {
742 command_ret
= run_command(cmd_type
, session_name
,
743 &process_attr_commands
[i
], writer
);
744 if (command_ret
!= CMD_SUCCESS
) {
745 sub_command_failed
= true;
746 if (command_ret
== CMD_FATAL
) {
754 /* Close trackers and output elements */
755 ret
= mi_lttng_close_multi_element(writer
, 2);
757 command_ret
= CMD_ERROR
;
762 ret
= mi_lttng_writer_write_element_bool(writer
,
763 mi_lttng_element_command_success
,
764 !sub_command_failed
);
766 command_ret
= CMD_ERROR
;
770 /* Command element close */
771 ret
= mi_lttng_writer_command_close(writer
);
773 command_ret
= CMD_ERROR
;
779 if (!opt_session_name
) {
784 if (writer
&& mi_lttng_writer_destroy(writer
)) {
785 /* Preserve original error code */
786 command_ret
= CMD_ERROR
;
789 for (i
= 0; i
< command_count
; i
++) {
790 process_attr_command_fini(&process_attr_commands
[i
]);
794 return (int) command_ret
;
797 int cmd_track(int argc
, const char **argv
)
799 static const char *help_msg
=
800 #ifdef LTTNG_EMBED_HELP
801 #include <lttng-track.1.h>
807 return cmd_track_untrack(CMD_TRACK
, argc
, argv
, help_msg
);
810 int cmd_untrack(int argc
, const char **argv
)
812 static const char *help_msg
=
813 #ifdef LTTNG_EMBED_HELP
814 #include <lttng-untrack.1.h>
820 return cmd_track_untrack(CMD_UNTRACK
, argc
, argv
, help_msg
);