2 * Copyright (C) 2013 David Goulet <dgoulet@efficios.com>
4 * SPDX-License-Identifier: GPL-2.0-only
15 #include <sys/types.h>
18 #include <common/utils.hpp>
19 #include <common/mi-lttng.hpp>
20 #include <lttng/lttng.h>
22 #include "../command.hpp"
24 static const char *opt_session_name
;
25 static const char *opt_output_name
;
26 static const char *opt_data_url
;
27 static const char *opt_ctrl_url
;
28 static const char *current_session_name
;
29 static uint64_t opt_max_size
;
31 /* Stub for the cmd struct actions. */
32 static int cmd_add_output(int argc
, const char **argv
);
33 static int cmd_del_output(int argc
, const char **argv
);
34 static int cmd_list_output(int argc
, const char **argv
);
35 static int cmd_record(int argc
, const char **argv
);
37 static const char *indent4
= " ";
39 #ifdef LTTNG_EMBED_HELP
40 static const char help_msg
[] =
41 #include <lttng-snapshot.1.h>
52 static struct mi_writer
*writer
;
54 static struct poptOption snapshot_opts
[] = {
55 /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */
56 {"help", 'h', POPT_ARG_NONE
, 0, OPT_HELP
, 0, 0},
57 {"session", 's', POPT_ARG_STRING
, &opt_session_name
, 0, 0, 0},
58 {"ctrl-url", 'C', POPT_ARG_STRING
, &opt_ctrl_url
, 0, 0, 0},
59 {"data-url", 'D', POPT_ARG_STRING
, &opt_data_url
, 0, 0, 0},
60 {"name", 'n', POPT_ARG_STRING
, &opt_output_name
, 0, 0, 0},
61 {"max-size", 'm', POPT_ARG_STRING
, 0, OPT_MAX_SIZE
, 0, 0},
62 {"list-options", 0, POPT_ARG_NONE
, NULL
, OPT_LIST_OPTIONS
, NULL
, NULL
},
63 {"list-commands", 0, POPT_ARG_NONE
, NULL
, OPT_LIST_COMMANDS
, NULL
, NULL
},
67 static struct cmd_struct actions
[] = {
68 { "add-output", cmd_add_output
},
69 { "del-output", cmd_del_output
},
70 { "list-output", cmd_list_output
},
71 { "record", cmd_record
},
72 { NULL
, NULL
} /* Array closure */
76 * Count and return the number of arguments in argv.
78 static int count_arguments(const char **argv
)
84 while (argv
[i
] != NULL
) {
92 * Create a snapshot output object from arguments using the given URL.
94 * Return a newly allocated object or NULL on error.
96 static struct lttng_snapshot_output
*create_output_from_args(const char *url
)
99 struct lttng_snapshot_output
*output
= NULL
;
101 output
= lttng_snapshot_output_create();
107 ret
= lttng_snapshot_output_set_ctrl_url(url
, output
);
111 } else if (opt_ctrl_url
) {
112 ret
= lttng_snapshot_output_set_ctrl_url(opt_ctrl_url
, output
);
119 ret
= lttng_snapshot_output_set_data_url(opt_data_url
, output
);
126 ret
= lttng_snapshot_output_set_size(opt_max_size
, output
);
132 if (opt_output_name
) {
133 ret
= lttng_snapshot_output_set_name(opt_output_name
, output
);
142 lttng_snapshot_output_destroy(output
);
147 static int list_output(void)
149 int ret
, output_seen
= 0;
150 struct lttng_snapshot_output
*s_iter
;
151 struct lttng_snapshot_output_list
*list
;
153 ret
= lttng_snapshot_list_output(current_session_name
, &list
);
158 MSG("Snapshot output list for session %s", current_session_name
);
161 ret
= mi_lttng_snapshot_output_session_name(writer
,
162 current_session_name
);
169 while ((s_iter
= lttng_snapshot_output_list_get_next(list
)) != NULL
) {
170 if (lttng_snapshot_output_get_maxsize(s_iter
)) {
171 MSG("%s[%" PRIu32
"] %s: %s (max size: %" PRIu64
" bytes)", indent4
,
172 lttng_snapshot_output_get_id(s_iter
),
173 lttng_snapshot_output_get_name(s_iter
),
174 lttng_snapshot_output_get_ctrl_url(s_iter
),
175 lttng_snapshot_output_get_maxsize(s_iter
));
177 MSG("%s[%" PRIu32
"] %s: %s", indent4
,
178 lttng_snapshot_output_get_id(s_iter
),
179 lttng_snapshot_output_get_name(s_iter
),
180 lttng_snapshot_output_get_ctrl_url(s_iter
));
184 ret
= mi_lttng_snapshot_list_output(writer
, s_iter
);
193 /* Close snapshot snapshots element */
194 ret
= mi_lttng_writer_close_element(writer
);
200 /* Close snapshot session element */
201 ret
= mi_lttng_writer_close_element(writer
);
207 lttng_snapshot_output_list_destroy(list
);
210 MSG("%sNone", indent4
);
218 * Delete output by ID.
220 static int del_output(uint32_t id
, const char *name
)
223 struct lttng_snapshot_output
*output
= NULL
;
225 output
= lttng_snapshot_output_create();
232 ret
= lttng_snapshot_output_set_name(name
, output
);
233 } else if (id
!= UINT32_MAX
) {
234 ret
= lttng_snapshot_output_set_id(id
, output
);
244 ret
= lttng_snapshot_del_output(current_session_name
, output
);
249 if (id
!= UINT32_MAX
) {
250 MSG("Snapshot output id %" PRIu32
" successfully deleted for session %s",
251 id
, current_session_name
);
253 MSG("Snapshot output %s successfully deleted for session %s",
254 name
, current_session_name
);
258 ret
= mi_lttng_snapshot_del_output(writer
, id
, name
,
259 current_session_name
);
266 lttng_snapshot_output_destroy(output
);
271 * Add output from the user URL.
273 static int add_output(const char *url
)
276 struct lttng_snapshot_output
*output
= NULL
;
280 if (!url
&& (!opt_data_url
|| !opt_ctrl_url
)) {
285 output
= create_output_from_args(url
);
291 /* This call, if successful, populates the id of the output object. */
292 ret
= lttng_snapshot_add_output(current_session_name
, output
);
297 n_ptr
= lttng_snapshot_output_get_name(output
);
298 if (*n_ptr
== '\0') {
300 pret
= snprintf(name
, sizeof(name
), DEFAULT_SNAPSHOT_NAME
"-%" PRIu32
,
301 lttng_snapshot_output_get_id(output
));
303 PERROR("snprintf add output name");
308 MSG("Snapshot output successfully added for session %s",
309 current_session_name
);
311 MSG(" [%" PRIu32
"] %s: %s (max size: %" PRIu64
" bytes)",
312 lttng_snapshot_output_get_id(output
), n_ptr
,
313 lttng_snapshot_output_get_ctrl_url(output
),
314 lttng_snapshot_output_get_maxsize(output
));
316 MSG(" [%" PRIu32
"] %s: %s",
317 lttng_snapshot_output_get_id(output
), n_ptr
,
318 lttng_snapshot_output_get_ctrl_url(output
));
321 ret
= mi_lttng_snapshot_add_output(writer
, current_session_name
,
328 lttng_snapshot_output_destroy(output
);
332 static int cmd_add_output(int argc
, const char **argv
)
336 if (argc
< 2 && (!opt_data_url
|| !opt_ctrl_url
)) {
337 ERR("An output destination must be specified to add a snapshot output.");
342 ret
= add_output(argv
[1]);
345 case LTTNG_ERR_SNAPSHOT_UNSUPPORTED
:
346 ERR("Session \"%s\" contains a channel that is incompatible with the snapshot functionality.\nMake sure all channels are configured in 'mmap' output mode.",
347 current_session_name
);
359 static int cmd_del_output(int argc
, const char **argv
)
366 ERR("A snapshot output name or id must be provided to delete a snapshot output.");
372 id
= strtol(argv
[1], &name
, 10);
373 if (id
== 0 && (errno
== 0 || errno
== EINVAL
)) {
374 ret
= del_output(UINT32_MAX
, name
);
375 } else if (errno
== 0 && *name
== '\0') {
376 ret
= del_output(id
, NULL
);
378 ERR("Argument %s not recognized", argv
[1]);
387 static int cmd_list_output(int argc
__attribute__((unused
)),
388 const char **argv
__attribute__((unused
)))
398 * Do a snapshot record with the URL if one is given.
400 static int record(const char *url
)
403 struct lttng_snapshot_output
*output
= NULL
;
405 output
= create_output_from_args(url
);
411 ret
= lttng_snapshot_record(current_session_name
, output
, 0);
413 if (ret
== -LTTNG_ERR_MAX_SIZE_INVALID
) {
414 ERR("Invalid snapshot size. Cannot fit at least one packet per stream.");
419 MSG("Snapshot recorded successfully for session %s", current_session_name
);
422 MSG("Snapshot written at: %s", url
);
423 } else if (opt_ctrl_url
) {
424 MSG("Snapshot written to ctrl: %s, data: %s", opt_ctrl_url
,
429 ret
= mi_lttng_snapshot_record(writer
, url
, opt_ctrl_url
,
437 lttng_snapshot_output_destroy(output
);
441 static int cmd_record(int argc
, const char **argv
)
446 ret
= record(argv
[1]);
454 static enum cmd_error_code
handle_command(const char **argv
)
456 int mi_ret
, i
= 0, argc
;
457 enum cmd_error_code cmd_ret
;
458 struct cmd_struct
*cmd
;
461 ERR("No action specified for snapshot command.");
466 if ((!opt_ctrl_url
&& opt_data_url
) ||
467 (opt_ctrl_url
&& !opt_data_url
)) {
468 ERR("URLs must be specified for both data and control");
473 argc
= count_arguments(argv
);
474 /* popt should have passed NULL if no arguments are present. */
475 LTTNG_ASSERT(argc
> 0);
478 while (cmd
->func
!= NULL
) {
480 if (strcmp(argv
[0], cmd
->name
) == 0) {
485 mi_ret
= mi_lttng_writer_open_element(writer
,
486 mi_lttng_element_command_action
);
492 /* Name of the action */
493 mi_ret
= mi_lttng_writer_write_element_string(writer
,
494 config_element_name
, argv
[0]);
500 /* Open output element */
501 mi_ret
= mi_lttng_writer_open_element(writer
,
502 mi_lttng_element_command_output
);
509 result
= cmd
->func(argc
, argv
);
516 case CMD_UNSUPPORTED
:
518 * Sub-commands mix lttng_error_codes
519 * and cmd_error_codes. This should be
520 * cleaned-up, but in the meantime this
521 * hack works since the values of the
522 * two enums do not intersect.
524 cmd_ret
= (cmd_error_code
) result
;
526 case -LTTNG_ERR_SNAPSHOT_NODATA
:
527 WARN("%s", lttng_strerror(result
));
529 /* A warning is fine since the user has no control on
530 * whether or not applications (or the kernel) have
531 * produced any event between the start of the tracing
532 * session and the recording of the snapshot. MI wise
533 * the command is not a success since nothing was
536 cmd_ret
= CMD_SUCCESS
;
539 ERR("%s", lttng_strerror(result
));
544 cmd_ret
= CMD_SUCCESS
;
549 /* Close output and action element */
550 mi_ret
= mi_lttng_close_multi_element(writer
, 2);
562 cmd_ret
= CMD_UNDEFINED
;
568 * The 'snapshot <cmd> <options>' first level command
570 int cmd_snapshot(int argc
, const char **argv
)
574 enum cmd_error_code cmd_ret
= CMD_SUCCESS
;
575 char *session_name
= NULL
;
576 static poptContext pc
;
578 pc
= poptGetContext(NULL
, argc
, argv
, snapshot_opts
, 0);
579 poptReadDefaultConfig(pc
, 0);
583 writer
= mi_lttng_writer_create(fileno(stdout
), lttng_opt_mi
);
589 /* Open command element */
590 mi_ret
= mi_lttng_writer_command_open(writer
,
591 mi_lttng_element_command_snapshot
);
597 /* Open output element */
598 mi_ret
= mi_lttng_writer_open_element(writer
,
599 mi_lttng_element_command_output
);
606 while ((opt
= poptGetNextOpt(pc
)) != -1) {
612 /* SHOW_HELP assigns to ret. */
614 cmd_ret
= (cmd_error_code
) ret
;
617 case OPT_LIST_OPTIONS
:
618 list_cmd_options(stdout
, snapshot_opts
);
620 case OPT_LIST_COMMANDS
:
621 list_commands(actions
, stdout
);
626 const char *max_size_arg
= poptGetOptArg(pc
);
628 if (utils_parse_size_suffix((char *) max_size_arg
, &val
) < 0) {
629 ERR("Unable to handle max-size value %s",
640 cmd_ret
= CMD_UNDEFINED
;
645 if (!opt_session_name
) {
646 session_name
= get_session_name();
647 if (session_name
== NULL
) {
651 current_session_name
= session_name
;
653 current_session_name
= opt_session_name
;
656 cmd_ret
= handle_command(poptGetArgs(pc
));
659 /* Close output element */
660 mi_ret
= mi_lttng_writer_close_element(writer
);
667 mi_ret
= mi_lttng_writer_write_element_bool(writer
,
668 mi_lttng_element_command_success
,
669 cmd_ret
== CMD_SUCCESS
);
675 /* Command element close */
676 mi_ret
= mi_lttng_writer_command_close(writer
);
685 if (writer
&& mi_lttng_writer_destroy(writer
)) {
689 if (!opt_session_name
) {