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