Commit | Line | Data |
---|---|---|
f3ed775e | 1 | /* |
21cf9b6b | 2 | * Copyright (C) 2011 EfficiOS Inc. |
f3ed775e | 3 | * |
ab5be9fa | 4 | * SPDX-License-Identifier: GPL-2.0-only |
f3ed775e | 5 | * |
f3ed775e DG |
6 | */ |
7 | ||
6c1c0768 | 8 | #define _LGPL_SOURCE |
28ab034a | 9 | #include "../command.hpp" |
0729ea55 | 10 | #include "../exception.hpp" |
28ab034a | 11 | |
aabf6773 | 12 | #include <common/exception.hpp> |
b50fbe86 | 13 | #include <common/make-unique-wrapper.hpp> |
28ab034a | 14 | #include <common/mi-lttng.hpp> |
b50fbe86 | 15 | #include <common/scope-exit.hpp> |
28ab034a JG |
16 | #include <common/sessiond-comm/sessiond-comm.hpp> |
17 | #include <common/utils.hpp> | |
18 | ||
19 | #include <lttng/lttng.h> | |
20 | ||
f3ed775e | 21 | #include <popt.h> |
28ab034a | 22 | #include <stdbool.h> |
f3ed775e DG |
23 | #include <stdio.h> |
24 | #include <stdlib.h> | |
25 | #include <string.h> | |
26 | #include <sys/stat.h> | |
27 | #include <sys/types.h> | |
28 | #include <unistd.h> | |
42224349 | 29 | |
544349a3 JG |
30 | enum { |
31 | OPT_HELP = 1, | |
32 | OPT_LIST_OPTIONS, | |
33 | OPT_ALL, | |
34 | OPT_ENABLE_GLOB, | |
35 | }; | |
f3ed775e | 36 | |
544349a3 | 37 | namespace { |
4fc83d94 | 38 | #ifdef LTTNG_EMBED_HELP |
544349a3 | 39 | const char help_msg[] = |
4fc83d94 | 40 | #include <lttng-destroy.1.h> |
28ab034a | 41 | ; |
4fc83d94 PP |
42 | #endif |
43 | ||
544349a3 | 44 | int opt_no_wait; |
65f25c66 | 45 | |
544349a3 JG |
46 | /* Mi writer */ |
47 | struct mi_writer *writer; | |
f3ed775e | 48 | |
544349a3 | 49 | struct poptOption long_options[] = { |
f3ed775e | 50 | /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */ |
cd9adb8b | 51 | { "help", 'h', POPT_ARG_NONE, nullptr, OPT_HELP, nullptr, nullptr }, |
aabf6773 OD |
52 | { "all", 'a', POPT_ARG_NONE, nullptr, OPT_ALL, nullptr, nullptr }, |
53 | { "glob", 'g', POPT_ARG_NONE, nullptr, OPT_ENABLE_GLOB, nullptr, nullptr }, | |
cd9adb8b JG |
54 | { "list-options", 0, POPT_ARG_NONE, nullptr, OPT_LIST_OPTIONS, nullptr, nullptr }, |
55 | { "no-wait", 'n', POPT_ARG_VAL, &opt_no_wait, 1, nullptr, nullptr }, | |
56 | { nullptr, 0, 0, nullptr, 0, nullptr, nullptr } | |
f3ed775e DG |
57 | }; |
58 | ||
f3ed775e | 59 | /* |
b09ee5ba FG |
60 | * destroy_session |
61 | * | |
62 | * Unregister the provided session to the session daemon. On success, removes | |
63 | * the default configuration. | |
f3ed775e | 64 | */ |
b50fbe86 | 65 | cmd_error_code destroy_session(const lttng_session& session) |
f3ed775e DG |
66 | { |
67 | int ret; | |
58f237ca | 68 | bool newline_needed = false, printed_destroy_msg = false; |
b50fbe86 JG |
69 | |
70 | const auto print_trailing_new_line = lttng::make_scope_exit([&newline_needed]() noexcept { | |
71 | if (newline_needed) { | |
72 | MSG(""); | |
73 | } | |
74 | }); | |
f3ed775e | 75 | |
aabf6773 | 76 | ret = lttng_stop_tracing_no_wait(session.name); |
e20ee7c2 | 77 | if (ret < 0 && ret != -LTTNG_ERR_TRACE_ALREADY_STOPPED) { |
f9a41357 | 78 | LTTNG_THROW_CTL(lttng::format("Failed to stop session `{}`", session.name), |
b50fbe86 | 79 | static_cast<lttng_error_code>(-ret)); |
e20ee7c2 | 80 | } |
58f237ca | 81 | |
b50fbe86 | 82 | const auto session_was_already_stopped = ret == -LTTNG_ERR_TRACE_ALREADY_STOPPED; |
e20ee7c2 | 83 | if (!opt_no_wait) { |
e20ee7c2 | 84 | do { |
aabf6773 | 85 | ret = lttng_data_pending(session.name); |
e20ee7c2 JD |
86 | if (ret < 0) { |
87 | /* Return the data available call error. */ | |
b50fbe86 JG |
88 | ERR_FMT("Failed to check pending data for session `{}` ({})", |
89 | session.name, | |
90 | lttng_strerror(ret)); | |
91 | return CMD_ERROR; | |
e20ee7c2 JD |
92 | } |
93 | ||
94 | /* | |
58f237ca JG |
95 | * Data sleep time before retrying (in usec). Don't |
96 | * sleep if the call returned value indicates | |
97 | * availability. | |
e20ee7c2 JD |
98 | */ |
99 | if (ret) { | |
dec2c8e1 | 100 | if (!printed_destroy_msg) { |
b50fbe86 | 101 | _MSG("Destroying session `%s`", session.name); |
dec2c8e1 JG |
102 | newline_needed = true; |
103 | printed_destroy_msg = true; | |
104 | fflush(stdout); | |
105 | } | |
01f55be4 | 106 | |
c8f61fd4 | 107 | usleep(DEFAULT_DATA_AVAILABILITY_WAIT_TIME_US); |
e20ee7c2 JD |
108 | _MSG("."); |
109 | fflush(stdout); | |
110 | } | |
111 | } while (ret != 0); | |
e20ee7c2 | 112 | } |
58f237ca | 113 | |
303ac4ed JG |
114 | std::unique_ptr<char, |
115 | lttng::memory::create_deleter_class<char, lttng::memory::free>::deleter> | |
b50fbe86 | 116 | stats_str; |
58f237ca | 117 | if (!session_was_already_stopped) { |
b50fbe86 JG |
118 | char *raw_stats_str = nullptr; |
119 | ||
20fb9e02 JD |
120 | /* |
121 | * Don't print the event and packet loss warnings since the user | |
122 | * already saw them when stopping the trace. | |
123 | */ | |
b50fbe86 | 124 | ret = get_session_stats_str(session.name, &raw_stats_str); |
58f237ca | 125 | if (ret < 0) { |
b50fbe86 | 126 | return CMD_ERROR; |
58f237ca | 127 | } |
e20ee7c2 | 128 | |
b50fbe86 JG |
129 | /* May still be null if there are no stats to print. */ |
130 | stats_str.reset(raw_stats_str); | |
f3ed775e DG |
131 | } |
132 | ||
b50fbe86 JG |
133 | const auto destruction_handle = [&session]() { |
134 | struct lttng_destruction_handle *raw_destruction_handle = nullptr; | |
135 | ||
136 | auto ctl_ret_code = | |
137 | lttng_destroy_session_ext(session.name, &raw_destruction_handle); | |
138 | if (ctl_ret_code != LTTNG_OK) { | |
f9a41357 JG |
139 | LTTNG_THROW_CTL(lttng::format("Failed to destroy session `{}`", |
140 | session.name), | |
b50fbe86 JG |
141 | ctl_ret_code); |
142 | } | |
bbbfd849 | 143 | |
b50fbe86 JG |
144 | return lttng::make_unique_wrapper<lttng_destruction_handle, |
145 | lttng_destruction_handle_destroy>( | |
146 | raw_destruction_handle); | |
147 | }(); | |
148 | ||
149 | if (!opt_no_wait) { | |
150 | enum lttng_destruction_handle_status status; | |
151 | ||
152 | do { | |
153 | status = lttng_destruction_handle_wait_for_completion( | |
154 | destruction_handle.get(), | |
155 | DEFAULT_DATA_AVAILABILITY_WAIT_TIME_US / USEC_PER_MSEC); | |
156 | switch (status) { | |
157 | case LTTNG_DESTRUCTION_HANDLE_STATUS_TIMEOUT: | |
158 | if (!printed_destroy_msg) { | |
159 | _MSG("Destroying session `%s`", session.name); | |
160 | newline_needed = true; | |
161 | printed_destroy_msg = true; | |
162 | } | |
163 | _MSG("."); | |
164 | fflush(stdout); | |
165 | break; | |
166 | case LTTNG_DESTRUCTION_HANDLE_STATUS_COMPLETED: | |
167 | break; | |
168 | default: | |
169 | ERR_FMT("{}An error occurred during the destruction of session `{}`", | |
170 | newline_needed ? "\n" : "", | |
171 | session.name); | |
172 | newline_needed = false; | |
173 | return CMD_ERROR; | |
bbbfd849 | 174 | } |
b50fbe86 JG |
175 | } while (status == LTTNG_DESTRUCTION_HANDLE_STATUS_TIMEOUT); |
176 | ||
177 | enum lttng_error_code ctl_ret_code; | |
178 | status = lttng_destruction_handle_get_result(destruction_handle.get(), | |
179 | &ctl_ret_code); | |
180 | if (status != LTTNG_DESTRUCTION_HANDLE_STATUS_OK) { | |
181 | ERR_FMT("{}Failed to query the result of the destruction of session `{}`", | |
182 | newline_needed ? "\n" : "", | |
183 | session.name); | |
184 | ||
58f237ca | 185 | newline_needed = false; |
b50fbe86 | 186 | return CMD_ERROR; |
bbbfd849 | 187 | } |
bbbfd849 | 188 | |
b50fbe86 | 189 | if (ctl_ret_code != LTTNG_OK) { |
f9a41357 JG |
190 | LTTNG_THROW_CTL(lttng::format("Failed to destroy session `{}`", |
191 | session.name), | |
b50fbe86 JG |
192 | ctl_ret_code); |
193 | } | |
bbbfd849 | 194 | |
b50fbe86 JG |
195 | enum lttng_rotation_state rotation_state; |
196 | status = lttng_destruction_handle_get_rotation_state(destruction_handle.get(), | |
197 | &rotation_state); | |
198 | if (status != LTTNG_DESTRUCTION_HANDLE_STATUS_OK) { | |
199 | ERR_FMT("{}Failed to query the rotation state from the destruction handle of session `{}`", | |
200 | newline_needed ? "\n" : "", | |
201 | session.name); | |
202 | newline_needed = false; | |
203 | } else { | |
204 | switch (rotation_state) { | |
205 | case LTTNG_ROTATION_STATE_NO_ROTATION: | |
206 | break; | |
207 | case LTTNG_ROTATION_STATE_COMPLETED: | |
208 | { | |
209 | const struct lttng_trace_archive_location *location; | |
210 | ||
211 | status = lttng_destruction_handle_get_archive_location( | |
212 | destruction_handle.get(), &location); | |
213 | if (status == LTTNG_DESTRUCTION_HANDLE_STATUS_OK) { | |
214 | ret = print_trace_archive_location(location, session.name); | |
215 | if (ret) { | |
216 | ERR_FMT("{}Failed to print the location of the latest trace archive of session `{}`", | |
217 | newline_needed ? "\n" : "", | |
218 | session.name); | |
219 | newline_needed = false; | |
220 | } | |
221 | ||
222 | break; | |
223 | } | |
224 | } | |
225 | /* fall-through. */ | |
226 | default: | |
227 | ERR_FMT("{}Failed to get the location of the rotation performed during the destruction of `{}`", | |
228 | newline_needed ? "\n" : "", | |
229 | session.name); | |
58f237ca | 230 | newline_needed = false; |
b50fbe86 | 231 | break; |
bbbfd849 | 232 | } |
bbbfd849 | 233 | } |
58f237ca | 234 | } |
b50fbe86 JG |
235 | |
236 | MSG("%sSession `%s` destroyed", newline_needed ? "\n" : "", session.name); | |
58f237ca JG |
237 | newline_needed = false; |
238 | if (stats_str) { | |
b50fbe86 | 239 | MSG("%s", stats_str.get()); |
58f237ca | 240 | } |
1dac0189 | 241 | |
b50fbe86 JG |
242 | /* |
243 | * If the session being destroy is the "default" session as defined in the .lttngrc file, | |
244 | * destroy the file. | |
245 | */ | |
246 | const auto session_name = | |
303ac4ed | 247 | lttng::make_unique_wrapper<char, lttng::memory::free>(get_session_name_quiet()); |
b50fbe86 | 248 | if (session_name && !strncmp(session.name, session_name.get(), NAME_MAX)) { |
1dac0189 PPM |
249 | config_destroy_default(); |
250 | } | |
65f25c66 JRJ |
251 | |
252 | if (lttng_opt_mi) { | |
aabf6773 | 253 | ret = mi_lttng_session(writer, &session, 0); |
65f25c66 | 254 | if (ret) { |
b50fbe86 | 255 | return CMD_ERROR; |
65f25c66 JRJ |
256 | } |
257 | } | |
258 | ||
b50fbe86 | 259 | return CMD_SUCCESS; |
b09ee5ba | 260 | } |
f3ed775e | 261 | |
6e11909e | 262 | cmd_error_code destroy_sessions(const lttng::cli::session_spec& spec) |
b09ee5ba | 263 | { |
b50fbe86 JG |
264 | bool had_warning = false; |
265 | bool had_error = false; | |
544349a3 JG |
266 | bool listing_failed = false; |
267 | ||
6e11909e | 268 | const auto sessions = [&listing_failed, &spec]() -> lttng::cli::session_list { |
544349a3 JG |
269 | try { |
270 | return list_sessions(spec); | |
271 | } catch (const lttng::ctl::error& ctl_exception) { | |
272 | ERR_FMT("Failed to list sessions ({})", | |
273 | lttng_strerror(-ctl_exception.code())); | |
274 | listing_failed = true; | |
275 | return {}; | |
0729ea55 JG |
276 | } catch (const lttng::cli::no_default_session_error& cli_exception) { |
277 | /* | |
278 | * The retrieval of the default session name already logs | |
279 | * an error when it fails. There is no value in printing | |
280 | * anything about this exception. | |
281 | */ | |
282 | listing_failed = true; | |
283 | return {}; | |
aabf6773 | 284 | } |
544349a3 JG |
285 | }(); |
286 | ||
6e11909e JG |
287 | if (!listing_failed && sessions.size() == 0 && |
288 | spec.type_ == lttng::cli::session_spec::type::NAME) { | |
b50fbe86 JG |
289 | ERR_FMT("Session `{}` not found", spec.value); |
290 | return CMD_ERROR; | |
291 | } | |
292 | ||
293 | if (listing_failed) { | |
294 | return CMD_FATAL; | |
544349a3 | 295 | } |
aabf6773 | 296 | |
544349a3 | 297 | for (const auto& session : sessions) { |
b50fbe86 | 298 | cmd_error_code sub_ret; |
aabf6773 | 299 | |
b50fbe86 JG |
300 | try { |
301 | sub_ret = destroy_session(session); | |
302 | } catch (const lttng::ctl::error& ctl_exception) { | |
303 | switch (ctl_exception.code()) { | |
304 | case LTTNG_ERR_NO_SESSION: | |
6e11909e | 305 | if (spec.type_ != lttng::cli::session_spec::type::NAME) { |
b50fbe86 JG |
306 | /* Session destroyed during command, ignore and carry-on. */ |
307 | sub_ret = CMD_SUCCESS; | |
308 | break; | |
309 | } else { | |
310 | sub_ret = CMD_ERROR; | |
311 | break; | |
312 | } | |
313 | case LTTNG_ERR_NO_SESSIOND: | |
314 | /* Don't keep going on a fatal error. */ | |
315 | return CMD_FATAL; | |
316 | default: | |
317 | /* Generic error. */ | |
318 | sub_ret = CMD_ERROR; | |
319 | ERR_FMT("Failed to destroy session `{}` ({})", | |
320 | session.name, | |
321 | lttng_strerror(-ctl_exception.code())); | |
322 | break; | |
323 | } | |
544349a3 | 324 | } |
b50fbe86 JG |
325 | |
326 | /* Keep going, but report the most serious state. */ | |
327 | had_warning |= sub_ret == CMD_WARNING; | |
328 | had_error |= sub_ret == CMD_ERROR; | |
b73d0b29 | 329 | } |
3285a971 | 330 | |
b50fbe86 JG |
331 | if (had_error) { |
332 | return CMD_ERROR; | |
333 | } else if (had_warning) { | |
334 | return CMD_WARNING; | |
335 | } else { | |
336 | return CMD_SUCCESS; | |
337 | } | |
f3ed775e | 338 | } |
544349a3 | 339 | } /* namespace */ |
f3ed775e DG |
340 | |
341 | /* | |
843f5df9 | 342 | * The 'destroy <options>' first level command |
f3ed775e DG |
343 | */ |
344 | int cmd_destroy(int argc, const char **argv) | |
345 | { | |
b09ee5ba | 346 | int opt; |
544349a3 | 347 | cmd_error_code command_ret = CMD_SUCCESS; |
aabf6773 | 348 | bool success; |
f3ed775e | 349 | static poptContext pc; |
cd9adb8b | 350 | const char *leftover = nullptr; |
6e11909e JG |
351 | lttng::cli::session_spec spec(lttng::cli::session_spec::type::NAME); |
352 | lttng::cli::session_list const sessions; | |
65f25c66 | 353 | |
cd9adb8b | 354 | pc = poptGetContext(nullptr, argc, argv, long_options, 0); |
f3ed775e DG |
355 | poptReadDefaultConfig(pc, 0); |
356 | ||
357 | while ((opt = poptGetNextOpt(pc)) != -1) { | |
358 | switch (opt) { | |
359 | case OPT_HELP: | |
544349a3 JG |
360 | { |
361 | int ret; | |
362 | ||
4ba92f18 | 363 | SHOW_HELP(); |
544349a3 | 364 | command_ret = static_cast<cmd_error_code>(ret); |
aabf6773 | 365 | goto end; |
544349a3 | 366 | } |
679b4943 SM |
367 | case OPT_LIST_OPTIONS: |
368 | list_cmd_options(stdout, long_options); | |
aabf6773 OD |
369 | goto end; |
370 | case OPT_ALL: | |
6e11909e | 371 | spec.type_ = lttng::cli::session_spec::type::ALL; |
aabf6773 OD |
372 | break; |
373 | case OPT_ENABLE_GLOB: | |
6e11909e | 374 | spec.type_ = lttng::cli::session_spec::type::GLOB_PATTERN; |
b09ee5ba | 375 | break; |
f3ed775e | 376 | default: |
544349a3 | 377 | command_ret = CMD_UNDEFINED; |
aabf6773 | 378 | goto end; |
f3ed775e DG |
379 | } |
380 | } | |
381 | ||
65f25c66 | 382 | /* Mi preparation */ |
c7e35b03 | 383 | if (lttng_opt_mi) { |
65f25c66 JRJ |
384 | writer = mi_lttng_writer_create(fileno(stdout), lttng_opt_mi); |
385 | if (!writer) { | |
544349a3 | 386 | command_ret = CMD_ERROR; |
65f25c66 JRJ |
387 | goto end; |
388 | } | |
389 | ||
390 | /* Open command element */ | |
544349a3 JG |
391 | if (mi_lttng_writer_command_open(writer, mi_lttng_element_command_destroy)) { |
392 | command_ret = CMD_ERROR; | |
65f25c66 JRJ |
393 | goto end; |
394 | } | |
395 | ||
396 | /* Open output element */ | |
544349a3 JG |
397 | if (mi_lttng_writer_open_element(writer, mi_lttng_element_command_output)) { |
398 | command_ret = CMD_ERROR; | |
65f25c66 JRJ |
399 | goto end; |
400 | } | |
401 | ||
402 | /* For validation and semantic purpose we open a sessions element */ | |
544349a3 JG |
403 | if (mi_lttng_sessions_open(writer)) { |
404 | command_ret = CMD_ERROR; | |
65f25c66 JRJ |
405 | goto end; |
406 | } | |
407 | } | |
408 | ||
aabf6773 | 409 | spec.value = poptGetArg(pc); |
fd076c09 | 410 | |
aabf6773 | 411 | command_ret = destroy_sessions(spec); |
65f25c66 | 412 | |
aabf6773 | 413 | success = command_ret == CMD_SUCCESS; |
65f25c66 | 414 | |
68c7f6e5 JD |
415 | leftover = poptGetArg(pc); |
416 | if (leftover) { | |
417 | ERR("Unknown argument: %s", leftover); | |
544349a3 | 418 | command_ret = CMD_ERROR; |
aabf6773 | 419 | success = false; |
68c7f6e5 JD |
420 | } |
421 | ||
65f25c66 JRJ |
422 | /* Mi closing */ |
423 | if (lttng_opt_mi) { | |
424 | /* Close sessions and output element element */ | |
544349a3 | 425 | if (mi_lttng_close_multi_element(writer, 2)) { |
b50fbe86 | 426 | command_ret = CMD_ERROR; |
b09ee5ba FG |
427 | goto end; |
428 | } | |
fd076c09 | 429 | |
65f25c66 | 430 | /* Success ? */ |
544349a3 | 431 | if (mi_lttng_writer_write_element_bool( |
b50fbe86 JG |
432 | writer, mi_lttng_element_command_success, success)) { |
433 | command_ret = CMD_ERROR; | |
65f25c66 JRJ |
434 | goto end; |
435 | } | |
f3ed775e | 436 | |
65f25c66 | 437 | /* Command element close */ |
544349a3 | 438 | if (mi_lttng_writer_command_close(writer)) { |
b50fbe86 | 439 | command_ret = CMD_ERROR; |
65f25c66 JRJ |
440 | goto end; |
441 | } | |
442 | } | |
f3ed775e | 443 | end: |
65f25c66 JRJ |
444 | /* Mi clean-up */ |
445 | if (writer && mi_lttng_writer_destroy(writer)) { | |
544349a3 | 446 | command_ret = CMD_ERROR; |
65f25c66 JRJ |
447 | } |
448 | ||
fd076c09 | 449 | poptFreeContext(pc); |
544349a3 | 450 | return command_ret; |
f3ed775e | 451 | } |