2 * Copyright (C) 2013 - David Goulet <dgoulet@efficios.com>
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License, version 2 only, as
6 * published by the Free Software Foundation.
8 * This program is distributed in the hope that it will be useful, but WITHOUT
9 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
13 * You should have received a copy of the GNU General Public License along with
14 * this program; if not, write to the Free Software Foundation, Inc., 51
15 * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
26 #include <sys/types.h>
30 #include <common/utils.h>
31 #include <common/mi-lttng.h>
32 #include <lttng/snapshot.h>
34 #include "../command.h"
36 static const char *opt_session_name
;
37 static const char *opt_output_name
;
38 static const char *opt_data_url
;
39 static const char *opt_ctrl_url
;
40 static const char *current_session_name
;
41 static uint64_t opt_max_size
;
43 /* Stub for the cmd struct actions. */
44 static int cmd_add_output(int argc
, const char **argv
);
45 static int cmd_del_output(int argc
, const char **argv
);
46 static int cmd_list_output(int argc
, const char **argv
);
47 static int cmd_record(int argc
, const char **argv
);
49 static const char *indent4
= " ";
58 static struct mi_writer
*writer
;
60 static struct poptOption snapshot_opts
[] = {
61 /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */
62 {"help", 'h', POPT_ARG_NONE
, 0, OPT_HELP
, 0, 0},
63 {"session", 's', POPT_ARG_STRING
, &opt_session_name
, 0, 0, 0},
64 {"ctrl-url", 'C', POPT_ARG_STRING
, &opt_ctrl_url
, 0, 0, 0},
65 {"data-url", 'D', POPT_ARG_STRING
, &opt_data_url
, 0, 0, 0},
66 {"name", 'n', POPT_ARG_STRING
, &opt_output_name
, 0, 0, 0},
67 {"max-size", 'm', POPT_ARG_STRING
, 0, OPT_MAX_SIZE
, 0, 0},
68 {"list-options", 0, POPT_ARG_NONE
, NULL
, OPT_LIST_OPTIONS
, NULL
, NULL
},
69 {"list-commands", 0, POPT_ARG_NONE
, NULL
, OPT_LIST_COMMANDS
},
73 static struct cmd_struct actions
[] = {
74 { "add-output", cmd_add_output
},
75 { "del-output", cmd_del_output
},
76 { "list-output", cmd_list_output
},
77 { "record", cmd_record
},
78 { NULL
, NULL
} /* Array closure */
82 * Count and return the number of arguments in argv.
84 static int count_arguments(const char **argv
)
90 while (argv
[i
] != NULL
) {
98 * Create a snapshot output object from arguments using the given URL.
100 * Return a newly allocated object or NULL on error.
102 static struct lttng_snapshot_output
*create_output_from_args(const char *url
)
105 struct lttng_snapshot_output
*output
= NULL
;
107 output
= lttng_snapshot_output_create();
113 ret
= lttng_snapshot_output_set_ctrl_url(url
, output
);
117 } else if (opt_ctrl_url
) {
118 ret
= lttng_snapshot_output_set_ctrl_url(opt_ctrl_url
, output
);
125 ret
= lttng_snapshot_output_set_data_url(opt_data_url
, output
);
132 ret
= lttng_snapshot_output_set_size(opt_max_size
, output
);
138 if (opt_output_name
) {
139 ret
= lttng_snapshot_output_set_name(opt_output_name
, output
);
148 lttng_snapshot_output_destroy(output
);
153 static int mi_list_output(void)
156 struct lttng_snapshot_output
*s_iter
;
157 struct lttng_snapshot_output_list
*list
;
161 ret
= lttng_snapshot_list_output(current_session_name
, &list
);
166 ret
= mi_lttng_snapshot_output_session_name(writer
, current_session_name
);
172 while ((s_iter
= lttng_snapshot_output_list_get_next(list
)) != NULL
) {
173 ret
= mi_lttng_snapshot_list_output(writer
, s_iter
);
181 /* Close snapshot snapshots element */
182 ret
= mi_lttng_writer_close_element(writer
);
188 /* Close snapshot session element */
189 ret
= mi_lttng_writer_close_element(writer
);
194 lttng_snapshot_output_list_destroy(list
);
199 static int list_output(void)
201 int ret
, output_seen
= 0;
202 struct lttng_snapshot_output
*s_iter
;
203 struct lttng_snapshot_output_list
*list
;
205 ret
= lttng_snapshot_list_output(current_session_name
, &list
);
210 MSG("Snapshot output list for session %s", current_session_name
);
212 while ((s_iter
= lttng_snapshot_output_list_get_next(list
)) != NULL
) {
213 MSG("%s[%" PRIu32
"] %s: %s (max-size: %" PRId64
")", indent4
,
214 lttng_snapshot_output_get_id(s_iter
),
215 lttng_snapshot_output_get_name(s_iter
),
216 lttng_snapshot_output_get_ctrl_url(s_iter
),
217 lttng_snapshot_output_get_maxsize(s_iter
));
221 lttng_snapshot_output_list_destroy(list
);
224 MSG("%sNone", indent4
);
232 * Delete output by ID (machine interface version).
234 static int mi_del_output(uint32_t id
, const char *name
)
237 struct lttng_snapshot_output
*output
= NULL
;
241 output
= lttng_snapshot_output_create();
248 ret
= lttng_snapshot_output_set_name(name
, output
);
249 } else if (id
!= UINT32_MAX
) {
250 ret
= lttng_snapshot_output_set_id(id
, output
);
260 ret
= lttng_snapshot_del_output(current_session_name
, output
);
265 ret
= mi_lttng_snapshot_del_output(writer
, id
, name
, current_session_name
);
271 lttng_snapshot_output_destroy(output
);
276 * Delete output by ID.
278 static int del_output(uint32_t id
, const char *name
)
281 struct lttng_snapshot_output
*output
= NULL
;
283 output
= lttng_snapshot_output_create();
290 ret
= lttng_snapshot_output_set_name(name
, output
);
291 } else if (id
!= UINT32_MAX
) {
292 ret
= lttng_snapshot_output_set_id(id
, output
);
302 ret
= lttng_snapshot_del_output(current_session_name
, output
);
307 if (id
!= UINT32_MAX
) {
308 MSG("Snapshot output id %" PRIu32
" successfully deleted for session %s",
309 id
, current_session_name
);
311 MSG("Snapshot output %s successfully deleted for session %s",
312 name
, current_session_name
);
316 lttng_snapshot_output_destroy(output
);
321 * Add output from the user URL (machine interface).
323 static int mi_add_output(const char *url
)
326 struct lttng_snapshot_output
*output
= NULL
;
330 if (!url
&& (!opt_data_url
|| !opt_ctrl_url
)) {
335 output
= create_output_from_args(url
);
341 /* This call, if successful, populates the id of the output object. */
342 ret
= lttng_snapshot_add_output(current_session_name
, output
);
347 n_ptr
= lttng_snapshot_output_get_name(output
);
348 if (*n_ptr
== '\0') {
350 pret
= snprintf(name
, sizeof(name
), DEFAULT_SNAPSHOT_NAME
"-%" PRIu32
,
351 lttng_snapshot_output_get_id(output
));
353 PERROR("snprintf add output name");
358 ret
= mi_lttng_snapshot_add_output(writer
, current_session_name
, n_ptr
,
365 lttng_snapshot_output_destroy(output
);
370 * Add output from the user URL.
372 static int add_output(const char *url
)
375 struct lttng_snapshot_output
*output
= NULL
;
379 if (!url
&& (!opt_data_url
|| !opt_ctrl_url
)) {
384 output
= create_output_from_args(url
);
390 /* This call, if successful, populates the id of the output object. */
391 ret
= lttng_snapshot_add_output(current_session_name
, output
);
396 n_ptr
= lttng_snapshot_output_get_name(output
);
397 if (*n_ptr
== '\0') {
399 pret
= snprintf(name
, sizeof(name
), DEFAULT_SNAPSHOT_NAME
"-%" PRIu32
,
400 lttng_snapshot_output_get_id(output
));
402 PERROR("snprintf add output name");
407 MSG("Snapshot output successfully added for session %s",
408 current_session_name
);
409 MSG(" [%" PRIu32
"] %s: %s (max-size: %" PRId64
")",
410 lttng_snapshot_output_get_id(output
), n_ptr
,
411 lttng_snapshot_output_get_ctrl_url(output
),
412 lttng_snapshot_output_get_maxsize(output
));
414 lttng_snapshot_output_destroy(output
);
418 static int cmd_add_output(int argc
, const char **argv
)
422 if (argc
< 2 && (!opt_data_url
|| !opt_ctrl_url
)) {
428 ret
= mi_add_output(argv
[1]);
430 ret
= add_output(argv
[1]);
437 static int cmd_del_output(int argc
, const char **argv
)
449 id
= strtol(argv
[1], &name
, 10);
450 if (id
== 0 && errno
== 0) {
452 ret
= mi_del_output(UINT32_MAX
, name
);
454 ret
= del_output(UINT32_MAX
, name
);
456 } else if (errno
== 0 && *name
== '\0') {
458 ret
= mi_del_output(id
, NULL
);
460 ret
= del_output(id
, NULL
);
463 ERR("Argument %s not recognized", argv
[1]);
472 static int cmd_list_output(int argc
, const char **argv
)
477 ret
= mi_list_output();
486 * Do a snapshot record with the URL if one is given (machine interface).
488 static int mi_record(const char *url
)
491 struct lttng_snapshot_output
*output
= NULL
;
493 output
= create_output_from_args(url
);
499 ret
= lttng_snapshot_record(current_session_name
, output
, 0);
505 ret
= mi_lttng_snapshot_record(writer
, current_session_name
, url
,
506 opt_ctrl_url
, opt_data_url
);
512 lttng_snapshot_output_destroy(output
);
517 * Do a snapshot record with the URL if one is given.
519 static int record(const char *url
)
522 struct lttng_snapshot_output
*output
= NULL
;
524 output
= create_output_from_args(url
);
530 ret
= lttng_snapshot_record(current_session_name
, output
, 0);
532 if (ret
== -LTTNG_ERR_MAX_SIZE_INVALID
) {
533 ERR("Invalid snapshot size. Cannot fit at least one packet per stream.");
538 MSG("Snapshot recorded successfully for session %s", current_session_name
);
541 MSG("Snapshot written at: %s", url
);
542 } else if (opt_ctrl_url
) {
543 MSG("Snapshot written to ctrl: %s, data: %s", opt_ctrl_url
,
548 lttng_snapshot_output_destroy(output
);
552 static int cmd_record(int argc
, const char **argv
)
557 /* With a given URL */
559 ret
= mi_record(argv
[1]);
561 ret
= record(argv
[1]);
565 ret
= mi_record(NULL
);
574 static int handle_command(const char **argv
)
576 int ret
= CMD_SUCCESS
, i
= 0, argc
, command_ret
= CMD_SUCCESS
;
577 struct cmd_struct
*cmd
;
579 if (argv
== NULL
|| (!opt_ctrl_url
&& opt_data_url
) ||
580 (opt_ctrl_url
&& !opt_data_url
)) {
581 command_ret
= CMD_ERROR
;
585 argc
= count_arguments(argv
);
588 while (cmd
->func
!= NULL
) {
590 if (strcmp(argv
[0], cmd
->name
) == 0) {
593 ret
= mi_lttng_writer_open_element(writer
,
594 mi_lttng_element_command_action
);
600 /* Name of the action */
601 ret
= mi_lttng_writer_write_element_string(writer
,
602 config_element_name
, argv
[0]);
608 /* Open output element */
609 ret
= mi_lttng_writer_open_element(writer
,
610 mi_lttng_element_command_output
);
617 command_ret
= cmd
->func(argc
, argv
);
620 /* Close output and action element */
621 ret
= mi_lttng_close_multi_element(writer
, 2);
636 /* Overwrite ret if an error occurred in cmd->func() */
637 ret
= command_ret
? command_ret
: ret
;
641 * The 'snapshot <cmd> <options>' first level command
643 int cmd_snapshot(int argc
, const char **argv
)
645 int opt
, ret
= CMD_SUCCESS
, command_ret
= CMD_SUCCESS
, success
= 1;
646 char *session_name
= NULL
;
647 static poptContext pc
;
649 pc
= poptGetContext(NULL
, argc
, argv
, snapshot_opts
, 0);
650 poptReadDefaultConfig(pc
, 0);
654 writer
= mi_lttng_writer_create(fileno(stdout
), lttng_opt_mi
);
656 ret
= -LTTNG_ERR_NOMEM
;
660 /* Open command element */
661 ret
= mi_lttng_writer_command_open(writer
,
662 mi_lttng_element_command_snapshot
);
668 /* Open output element */
669 ret
= mi_lttng_writer_open_element(writer
,
670 mi_lttng_element_command_output
);
677 while ((opt
= poptGetNextOpt(pc
)) != -1) {
682 case OPT_LIST_OPTIONS
:
683 list_cmd_options(stdout
, snapshot_opts
);
685 case OPT_LIST_COMMANDS
:
686 list_commands(actions
, stdout
);
691 const char *opt
= poptGetOptArg(pc
);
693 if (utils_parse_size_suffix((char *) opt
, &val
) < 0) {
694 ERR("Unable to handle max-size value %s", opt
);
709 if (!opt_session_name
) {
710 session_name
= get_session_name();
711 if (session_name
== NULL
) {
715 current_session_name
= session_name
;
717 current_session_name
= opt_session_name
;
720 command_ret
= handle_command(poptGetArgs(pc
));
722 switch (-command_ret
) {
723 case LTTNG_ERR_EPERM
:
724 ERR("The session needs to be set in no output mode (--no-output)");
726 case LTTNG_ERR_SNAPSHOT_NODATA
:
727 WARN("%s", lttng_strerror(command_ret
));
730 ERR("%s", lttng_strerror(command_ret
));
737 /* Close output element */
738 ret
= mi_lttng_writer_close_element(writer
);
745 ret
= mi_lttng_writer_write_element_bool(writer
,
746 mi_lttng_element_command_success
, success
);
752 /* Command element close */
753 ret
= mi_lttng_writer_command_close(writer
);
762 if (writer
&& mi_lttng_writer_destroy(writer
)) {
763 /* Preserve original error code */
764 ret
= ret
? ret
: -LTTNG_ERR_MI_IO_FAIL
;
767 if (!opt_session_name
) {
771 /* Overwrite ret if an error occured during handle_command */
772 ret
= command_ret
? command_ret
: ret
;