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
11 #include "../command.hpp"
13 #include <common/dynamic-array.hpp>
14 #include <common/dynamic-buffer.hpp>
15 #include <common/mi-lttng.hpp>
16 #include <common/optional.hpp>
17 #include <common/tracker.hpp>
19 #include <lttng/lttng.h>
28 #include <sys/types.h>
30 #include <urcu/list.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
;
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, } */
107 POPT_ARG_STRING
| POPT_ARGFLAG_OPTIONAL
,
116 POPT_ARG_STRING
| POPT_ARGFLAG_OPTIONAL
,
125 POPT_ARG_STRING
| POPT_ARGFLAG_OPTIONAL
,
134 POPT_ARG_STRING
| POPT_ARGFLAG_OPTIONAL
,
143 POPT_ARG_STRING
| POPT_ARGFLAG_OPTIONAL
,
152 POPT_ARG_STRING
| POPT_ARGFLAG_OPTIONAL
,
187 static struct process_attr_command_args
188 process_attr_commands
[LTTNG_PROCESS_ATTR_VIRTUAL_GROUP_ID
+ 1];
190 static void process_attr_command_init(struct process_attr_command_args
*cmd
,
191 enum lttng_process_attr process_attr
)
193 cmd
->process_attr
= process_attr
;
195 lttng_dynamic_pointer_array_init(&cmd
->string_args
, free
);
198 static void process_attr_command_fini(struct process_attr_command_args
*cmd
)
200 lttng_dynamic_pointer_array_reset(&cmd
->string_args
);
203 static const char *get_capitalized_process_attr_str(enum lttng_process_attr process_attr
)
205 switch (process_attr
) {
206 case LTTNG_PROCESS_ATTR_PROCESS_ID
:
208 case LTTNG_PROCESS_ATTR_VIRTUAL_PROCESS_ID
:
209 return "Virtual process ID";
210 case LTTNG_PROCESS_ATTR_USER_ID
:
212 case LTTNG_PROCESS_ATTR_VIRTUAL_USER_ID
:
213 return "Virtual user ID";
214 case LTTNG_PROCESS_ATTR_GROUP_ID
:
216 case LTTNG_PROCESS_ATTR_VIRTUAL_GROUP_ID
:
217 return "Virtual group ID";
224 static bool ust_process_attr_supported(enum lttng_process_attr
*process_attr
)
228 switch (*process_attr
) {
229 case LTTNG_PROCESS_ATTR_PROCESS_ID
:
230 *process_attr
= LTTNG_PROCESS_ATTR_VIRTUAL_PROCESS_ID
;
232 case LTTNG_PROCESS_ATTR_VIRTUAL_PROCESS_ID
:
233 case LTTNG_PROCESS_ATTR_VIRTUAL_USER_ID
:
234 case LTTNG_PROCESS_ATTR_VIRTUAL_GROUP_ID
:
238 ERR("The %s process attribute cannot be tracked in the user space domain.",
239 lttng_process_attr_to_string(*process_attr
));
246 static const char *get_mi_element_command(enum cmd_type cmd_type
)
250 return mi_lttng_element_command_track
;
252 return mi_lttng_element_command_untrack
;
258 static enum cmd_error_code
run_command_all(enum cmd_type cmd_type
,
259 const char *session_name
,
260 enum lttng_domain_type domain_type
,
261 enum lttng_process_attr process_attr
,
262 struct mi_writer
*writer
)
264 struct lttng_process_attr_tracker_handle
*tracker_handle
= nullptr;
265 const enum lttng_error_code handle_ret_code
= lttng_session_get_tracker_handle(
266 session_name
, domain_type
, process_attr
, &tracker_handle
);
267 enum cmd_error_code cmd_ret
= CMD_SUCCESS
;
268 enum lttng_process_attr_tracker_handle_status status
;
271 const int ret
= mi_lttng_all_process_attribute_value(writer
, process_attr
, true);
278 if (handle_ret_code
!= LTTNG_OK
) {
279 ERR("Session `%s` does not exist", session_name
);
284 status
= lttng_process_attr_tracker_handle_set_tracking_policy(
286 cmd_type
== CMD_TRACK
? LTTNG_TRACKING_POLICY_INCLUDE_ALL
:
287 LTTNG_TRACKING_POLICY_EXCLUDE_ALL
);
289 case LTTNG_PROCESS_ATTR_TRACKER_HANDLE_STATUS_OK
:
290 if (cmd_type
== CMD_TRACK
) {
291 MSG("%s tracking policy set to `include all`",
292 get_capitalized_process_attr_str(process_attr
));
294 MSG("%s tracking policy set to `exclude all`",
295 get_capitalized_process_attr_str(process_attr
));
298 case LTTNG_PROCESS_ATTR_TRACKER_HANDLE_STATUS_SESSION_DOES_NOT_EXIST
:
299 ERR("%s", lttng_strerror(-LTTNG_ERR_SESS_NOT_FOUND
));
302 ERR("Unknown error encountered while setting tracking policy of %s tracker to `%s`",
303 lttng_process_attr_to_string(process_attr
),
304 cmd_type
== CMD_TRACK
? "include all" : "exclude all");
310 int ret
= mi_lttng_writer_write_element_bool(
311 writer
, mi_lttng_element_success
, cmd_ret
== CMD_SUCCESS
);
316 ret
= mi_lttng_writer_close_element(writer
);
317 cmd_ret
= ret
== 0 ? cmd_ret
: CMD_FATAL
;
320 lttng_process_attr_tracker_handle_destroy(tracker_handle
);
324 static enum cmd_error_code
run_command_string(enum cmd_type cmd_type
,
325 const char *session_name
,
326 enum lttng_domain_type domain_type
,
327 enum lttng_process_attr process_attr
,
329 struct mi_writer
*writer
)
331 struct lttng_process_attr_tracker_handle
*tracker_handle
= nullptr;
332 const enum lttng_error_code handle_ret_code
= lttng_session_get_tracker_handle(
333 session_name
, domain_type
, process_attr
, &tracker_handle
);
334 enum cmd_error_code cmd_ret
= CMD_SUCCESS
;
335 const char *one_value_str
;
336 char *args
= strdup(_args
);
338 bool policy_set
= false;
341 ERR("%s", lttng_strerror(-LTTNG_ERR_NOMEM
));
346 if (handle_ret_code
!= LTTNG_OK
) {
347 ERR("%s", lttng_strerror(-handle_ret_code
));
352 while ((one_value_str
= strtok_r(iter
, ",", &iter
)) != nullptr) {
353 const bool is_numerical_argument
= isdigit(one_value_str
[0]);
354 enum lttng_process_attr_tracker_handle_status status
;
355 enum lttng_tracking_policy policy
;
357 char *prettified_arg
;
360 status
= lttng_process_attr_tracker_handle_get_tracking_policy(
361 tracker_handle
, &policy
);
362 if (status
!= LTTNG_PROCESS_ATTR_TRACKER_HANDLE_STATUS_OK
) {
366 if (policy
!= LTTNG_TRACKING_POLICY_INCLUDE_SET
) {
367 status
= lttng_process_attr_tracker_handle_set_tracking_policy(
368 tracker_handle
, LTTNG_TRACKING_POLICY_INCLUDE_SET
);
369 if (status
!= LTTNG_PROCESS_ATTR_TRACKER_HANDLE_STATUS_OK
) {
376 if (is_numerical_argument
) {
377 const unsigned long one_value_int
= strtoul(one_value_str
, nullptr, 10);
380 ret
= mi_lttng_integral_process_attribute_value(
381 writer
, process_attr
, (int64_t) one_value_int
, true);
388 switch (process_attr
) {
389 case LTTNG_PROCESS_ATTR_PROCESS_ID
:
390 status
= cmd_type
== CMD_TRACK
?
391 lttng_process_attr_process_id_tracker_handle_add_pid(
392 tracker_handle
, (pid_t
) one_value_int
) :
393 lttng_process_attr_process_id_tracker_handle_remove_pid(
394 tracker_handle
, (pid_t
) one_value_int
);
396 case LTTNG_PROCESS_ATTR_VIRTUAL_PROCESS_ID
:
397 status
= cmd_type
== CMD_TRACK
?
398 lttng_process_attr_virtual_process_id_tracker_handle_add_pid(
399 tracker_handle
, (pid_t
) one_value_int
) :
400 lttng_process_attr_virtual_process_id_tracker_handle_remove_pid(
401 tracker_handle
, (pid_t
) one_value_int
);
403 case LTTNG_PROCESS_ATTR_USER_ID
:
404 status
= cmd_type
== CMD_TRACK
?
405 lttng_process_attr_user_id_tracker_handle_add_uid(
406 tracker_handle
, (uid_t
) one_value_int
) :
407 lttng_process_attr_user_id_tracker_handle_remove_uid(
408 tracker_handle
, (uid_t
) one_value_int
);
410 case LTTNG_PROCESS_ATTR_VIRTUAL_USER_ID
:
411 status
= cmd_type
== CMD_TRACK
?
412 lttng_process_attr_virtual_user_id_tracker_handle_add_uid(
413 tracker_handle
, (uid_t
) one_value_int
) :
414 lttng_process_attr_virtual_user_id_tracker_handle_remove_uid(
415 tracker_handle
, (uid_t
) one_value_int
);
417 case LTTNG_PROCESS_ATTR_GROUP_ID
:
418 status
= cmd_type
== CMD_TRACK
?
419 lttng_process_attr_group_id_tracker_handle_add_gid(
420 tracker_handle
, (gid_t
) one_value_int
) :
421 lttng_process_attr_group_id_tracker_handle_remove_gid(
422 tracker_handle
, (gid_t
) one_value_int
);
424 case LTTNG_PROCESS_ATTR_VIRTUAL_GROUP_ID
:
425 status
= cmd_type
== CMD_TRACK
?
426 lttng_process_attr_virtual_group_id_tracker_handle_add_gid(
427 tracker_handle
, (gid_t
) one_value_int
) :
428 lttng_process_attr_virtual_group_id_tracker_handle_remove_gid(
429 tracker_handle
, (gid_t
) one_value_int
);
437 ret
= mi_lttng_string_process_attribute_value(
438 writer
, process_attr
, one_value_str
, true);
445 switch (process_attr
) {
446 case LTTNG_PROCESS_ATTR_USER_ID
:
447 status
= cmd_type
== CMD_TRACK
?
448 lttng_process_attr_user_id_tracker_handle_add_user_name(
449 tracker_handle
, one_value_str
) :
450 lttng_process_attr_user_id_tracker_handle_remove_user_name(
451 tracker_handle
, one_value_str
);
453 case LTTNG_PROCESS_ATTR_VIRTUAL_USER_ID
:
454 status
= cmd_type
== CMD_TRACK
?
455 lttng_process_attr_virtual_user_id_tracker_handle_add_user_name(
456 tracker_handle
, one_value_str
) :
457 lttng_process_attr_virtual_user_id_tracker_handle_remove_user_name(
458 tracker_handle
, one_value_str
);
460 case LTTNG_PROCESS_ATTR_GROUP_ID
:
461 status
= cmd_type
== CMD_TRACK
?
462 lttng_process_attr_group_id_tracker_handle_add_group_name(
463 tracker_handle
, one_value_str
) :
464 lttng_process_attr_group_id_tracker_handle_remove_group_name(
465 tracker_handle
, one_value_str
);
467 case LTTNG_PROCESS_ATTR_VIRTUAL_GROUP_ID
:
468 status
= cmd_type
== CMD_TRACK
?
469 lttng_process_attr_virtual_group_id_tracker_handle_add_group_name(
470 tracker_handle
, one_value_str
) :
471 lttng_process_attr_virtual_group_id_tracker_handle_remove_group_name(
472 tracker_handle
, one_value_str
);
475 ERR("%s is not a valid %s value; expected an integer",
477 lttng_process_attr_to_string(process_attr
));
484 &prettified_arg
, is_numerical_argument
? "%s" : "`%s`", one_value_str
);
486 PERROR("Failed to format argument `%s`", one_value_str
);
492 case LTTNG_PROCESS_ATTR_TRACKER_HANDLE_STATUS_OK
:
493 if (cmd_type
== CMD_TRACK
) {
494 MSG("Added %s to the %s tracker inclusion set",
496 lttng_process_attr_to_string(process_attr
));
498 MSG("Removed %s from the %s tracker inclusion set",
500 lttng_process_attr_to_string(process_attr
));
503 case LTTNG_PROCESS_ATTR_TRACKER_HANDLE_STATUS_SESSION_DOES_NOT_EXIST
:
504 ERR("Session `%s` not found", session_name
);
506 case LTTNG_PROCESS_ATTR_TRACKER_HANDLE_STATUS_EXISTS
:
507 WARN("%s is already in the %s inclusion set",
509 lttng_process_attr_to_string(process_attr
));
511 case LTTNG_PROCESS_ATTR_TRACKER_HANDLE_STATUS_MISSING
:
512 WARN("%s is not in the %s the inclusion set",
514 lttng_process_attr_to_string(process_attr
));
516 case LTTNG_PROCESS_ATTR_TRACKER_HANDLE_STATUS_USER_NOT_FOUND
:
517 ERR("User %s was not found", prettified_arg
);
520 case LTTNG_PROCESS_ATTR_TRACKER_HANDLE_STATUS_GROUP_NOT_FOUND
:
521 ERR("Group %s was not found", prettified_arg
);
525 ERR("Unknown error encountered while %s %s %s %s tracker's inclusion set",
526 cmd_type
== CMD_TRACK
? "adding" : "removing",
527 lttng_process_attr_to_string(process_attr
),
529 cmd_type
== CMD_TRACK
? "to" : "from");
533 free(prettified_arg
);
536 ret
= mi_lttng_writer_write_element_bool(
537 writer
, mi_lttng_element_success
, cmd_ret
== CMD_SUCCESS
);
542 ret
= mi_lttng_writer_close_element(writer
);
543 cmd_ret
= ret
== 0 ? cmd_ret
: CMD_FATAL
;
549 lttng_process_attr_tracker_handle_destroy(tracker_handle
);
553 static enum cmd_error_code
run_command(enum cmd_type cmd_type
,
554 const char *session_name
,
555 const struct process_attr_command_args
*command_args
,
556 struct mi_writer
*writer
)
558 const enum lttng_domain_type domain_type
= opt_kernel
? LTTNG_DOMAIN_KERNEL
:
560 enum cmd_error_code cmd_ret
= CMD_SUCCESS
;
562 const unsigned int string_arg_count
=
563 lttng_dynamic_pointer_array_get_count(&command_args
->string_args
);
564 enum lttng_process_attr process_attr
= command_args
->process_attr
;
568 * Check that this process attribute can be tracked
569 * in the user space domain. Backward-compatibility
570 * changes are be applied to process_attr as needed.
572 if (!ust_process_attr_supported(&process_attr
)) {
579 /* Open tracker and trackers elements */
580 const int ret
= mi_lttng_process_attribute_tracker_open(writer
, process_attr
);
587 if (command_args
->all
) {
589 run_command_all(cmd_type
, session_name
, domain_type
, process_attr
, writer
);
591 bool error_occurred
= false;
593 for (i
= 0; i
< string_arg_count
; i
++) {
594 const char *arg
= (const char *) lttng_dynamic_pointer_array_get_pointer(
595 &command_args
->string_args
, i
);
597 cmd_ret
= run_command_string(
598 cmd_type
, session_name
, domain_type
, process_attr
, arg
, writer
);
599 if (cmd_ret
!= CMD_SUCCESS
) {
600 error_occurred
= true;
601 if (cmd_ret
== CMD_FATAL
) {
607 if (error_occurred
) {
613 /* Close tracker and trackers elements */
614 const int ret
= mi_lttng_close_multi_element(writer
, 2);
625 * Add/remove tracker to/from session.
627 static int cmd_track_untrack(enum cmd_type cmd_type
,
630 const char *help_msg
__attribute__((unused
)))
633 bool sub_command_failed
= false;
634 bool opt_all
= false;
635 unsigned int selected_process_attr_tracker_count
= 0;
636 const unsigned int command_count
=
637 sizeof(process_attr_commands
) / sizeof(struct process_attr_command_args
);
638 enum cmd_error_code command_ret
= CMD_SUCCESS
;
639 static poptContext pc
;
640 char *session_name
= nullptr;
641 const char *leftover
= nullptr;
642 struct mi_writer
*writer
= nullptr;
645 for (i
= 0; i
< command_count
; i
++) {
646 process_attr_command_init(&process_attr_commands
[i
], (lttng_process_attr
) i
);
650 command_ret
= CMD_ERROR
;
654 pc
= poptGetContext(nullptr, argc
, argv
, long_options
, 0);
655 poptReadDefaultConfig(pc
, 0);
657 while ((opt
= poptGetNextOpt(pc
)) != -1) {
662 case OPT_LIST_OPTIONS
:
663 list_cmd_options(stdout
, long_options
);
673 /* See OPT_ enum declaration comment. */
675 selected_process_attr_tracker_count
++;
676 process_attr_commands
[opt
].requested
= true;
680 ret
= lttng_dynamic_pointer_array_add_pointer(
681 &process_attr_commands
[opt
].string_args
, opt_str_arg
);
683 ERR("Allocation failed while parsing command arguments");
684 command_ret
= CMD_ERROR
;
692 command_ret
= CMD_UNDEFINED
;
697 ret
= print_missing_or_multiple_domains(opt_kernel
+ opt_userspace
, false);
699 command_ret
= CMD_ERROR
;
703 if (selected_process_attr_tracker_count
== 0) {
704 ERR("At least one process attribute must be specified");
705 command_ret
= CMD_ERROR
;
710 * Only one process attribute tracker was specified; find it
711 * and set it in 'all' mode.
713 for (i
= 0; i
< command_count
; i
++) {
714 if (!process_attr_commands
[i
].requested
) {
717 process_attr_commands
[i
].all
= true;
718 if (lttng_dynamic_pointer_array_get_count(
719 &process_attr_commands
[i
].string_args
)) {
720 ERR("The --all option cannot be used with a list of process attribute values");
721 command_ret
= CMD_ERROR
;
726 for (i
= 0; i
< command_count
; i
++) {
727 if (!process_attr_commands
[i
].requested
) {
730 if (lttng_dynamic_pointer_array_get_count(
731 &process_attr_commands
[i
].string_args
) == 0) {
732 ERR("No process attribute value specified for %s tracker",
733 get_capitalized_process_attr_str(
734 process_attr_commands
[i
].process_attr
));
735 command_ret
= CMD_ERROR
;
741 if (!opt_session_name
) {
742 session_name
= get_session_name();
743 if (session_name
== nullptr) {
744 command_ret
= CMD_ERROR
;
748 session_name
= opt_session_name
;
751 leftover
= poptGetArg(pc
);
753 ERR("Unknown argument: %s", leftover
);
754 command_ret
= CMD_ERROR
;
760 writer
= mi_lttng_writer_create(fileno(stdout
), lttng_opt_mi
);
762 command_ret
= CMD_ERROR
;
768 /* Open command element */
769 ret
= mi_lttng_writer_command_open(writer
, get_mi_element_command(cmd_type
));
771 command_ret
= CMD_ERROR
;
775 /* Open output element */
776 ret
= mi_lttng_writer_open_element(writer
, mi_lttng_element_command_output
);
778 command_ret
= CMD_ERROR
;
782 ret
= mi_lttng_trackers_open(writer
);
788 /* Execute sub-commands. */
789 for (i
= 0; i
< command_count
; i
++) {
790 if (!process_attr_commands
[i
].requested
) {
794 run_command(cmd_type
, session_name
, &process_attr_commands
[i
], writer
);
795 if (command_ret
!= CMD_SUCCESS
) {
796 sub_command_failed
= true;
797 if (command_ret
== CMD_FATAL
) {
805 /* Close trackers and output elements */
806 ret
= mi_lttng_close_multi_element(writer
, 2);
808 command_ret
= CMD_ERROR
;
813 ret
= mi_lttng_writer_write_element_bool(
814 writer
, mi_lttng_element_command_success
, !sub_command_failed
);
816 command_ret
= CMD_ERROR
;
820 /* Command element close */
821 ret
= mi_lttng_writer_command_close(writer
);
823 command_ret
= CMD_ERROR
;
829 if (!opt_session_name
) {
834 if (writer
&& mi_lttng_writer_destroy(writer
)) {
835 /* Preserve original error code */
836 command_ret
= CMD_ERROR
;
839 for (i
= 0; i
< command_count
; i
++) {
840 process_attr_command_fini(&process_attr_commands
[i
]);
844 return (int) command_ret
;
847 int cmd_track(int argc
, const char **argv
)
849 static const char *help_msg
=
850 #ifdef LTTNG_EMBED_HELP
851 #include <lttng-track.1.h>
857 return cmd_track_untrack(CMD_TRACK
, argc
, argv
, help_msg
);
860 int cmd_untrack(int argc
, const char **argv
)
862 static const char *help_msg
=
863 #ifdef LTTNG_EMBED_HELP
864 #include <lttng-untrack.1.h>
870 return cmd_track_untrack(CMD_UNTRACK
, argc
, argv
, help_msg
);