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) { |
b50fbe86 JG |
77 | LTTNG_THROW_CTL(fmt::format("Failed to stop session `{}`", session.name), |
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) { | |
137 | LTTNG_THROW_CTL(fmt::format("Failed to destroy session `{}`", session.name), | |
138 | ctl_ret_code); | |
139 | } | |
bbbfd849 | 140 | |
b50fbe86 JG |
141 | return lttng::make_unique_wrapper<lttng_destruction_handle, |
142 | lttng_destruction_handle_destroy>( | |
143 | raw_destruction_handle); | |
144 | }(); | |
145 | ||
146 | if (!opt_no_wait) { | |
147 | enum lttng_destruction_handle_status status; | |
148 | ||
149 | do { | |
150 | status = lttng_destruction_handle_wait_for_completion( | |
151 | destruction_handle.get(), | |
152 | DEFAULT_DATA_AVAILABILITY_WAIT_TIME_US / USEC_PER_MSEC); | |
153 | switch (status) { | |
154 | case LTTNG_DESTRUCTION_HANDLE_STATUS_TIMEOUT: | |
155 | if (!printed_destroy_msg) { | |
156 | _MSG("Destroying session `%s`", session.name); | |
157 | newline_needed = true; | |
158 | printed_destroy_msg = true; | |
159 | } | |
160 | _MSG("."); | |
161 | fflush(stdout); | |
162 | break; | |
163 | case LTTNG_DESTRUCTION_HANDLE_STATUS_COMPLETED: | |
164 | break; | |
165 | default: | |
166 | ERR_FMT("{}An error occurred during the destruction of session `{}`", | |
167 | newline_needed ? "\n" : "", | |
168 | session.name); | |
169 | newline_needed = false; | |
170 | return CMD_ERROR; | |
bbbfd849 | 171 | } |
b50fbe86 JG |
172 | } while (status == LTTNG_DESTRUCTION_HANDLE_STATUS_TIMEOUT); |
173 | ||
174 | enum lttng_error_code ctl_ret_code; | |
175 | status = lttng_destruction_handle_get_result(destruction_handle.get(), | |
176 | &ctl_ret_code); | |
177 | if (status != LTTNG_DESTRUCTION_HANDLE_STATUS_OK) { | |
178 | ERR_FMT("{}Failed to query the result of the destruction of session `{}`", | |
179 | newline_needed ? "\n" : "", | |
180 | session.name); | |
181 | ||
58f237ca | 182 | newline_needed = false; |
b50fbe86 | 183 | return CMD_ERROR; |
bbbfd849 | 184 | } |
bbbfd849 | 185 | |
b50fbe86 JG |
186 | if (ctl_ret_code != LTTNG_OK) { |
187 | LTTNG_THROW_CTL(fmt::format("Failed to destroy session `{}`", session.name), | |
188 | ctl_ret_code); | |
189 | } | |
bbbfd849 | 190 | |
b50fbe86 JG |
191 | enum lttng_rotation_state rotation_state; |
192 | status = lttng_destruction_handle_get_rotation_state(destruction_handle.get(), | |
193 | &rotation_state); | |
194 | if (status != LTTNG_DESTRUCTION_HANDLE_STATUS_OK) { | |
195 | ERR_FMT("{}Failed to query the rotation state from the destruction handle of session `{}`", | |
196 | newline_needed ? "\n" : "", | |
197 | session.name); | |
198 | newline_needed = false; | |
199 | } else { | |
200 | switch (rotation_state) { | |
201 | case LTTNG_ROTATION_STATE_NO_ROTATION: | |
202 | break; | |
203 | case LTTNG_ROTATION_STATE_COMPLETED: | |
204 | { | |
205 | const struct lttng_trace_archive_location *location; | |
206 | ||
207 | status = lttng_destruction_handle_get_archive_location( | |
208 | destruction_handle.get(), &location); | |
209 | if (status == LTTNG_DESTRUCTION_HANDLE_STATUS_OK) { | |
210 | ret = print_trace_archive_location(location, session.name); | |
211 | if (ret) { | |
212 | ERR_FMT("{}Failed to print the location of the latest trace archive of session `{}`", | |
213 | newline_needed ? "\n" : "", | |
214 | session.name); | |
215 | newline_needed = false; | |
216 | } | |
217 | ||
218 | break; | |
219 | } | |
220 | } | |
221 | /* fall-through. */ | |
222 | default: | |
223 | ERR_FMT("{}Failed to get the location of the rotation performed during the destruction of `{}`", | |
224 | newline_needed ? "\n" : "", | |
225 | session.name); | |
58f237ca | 226 | newline_needed = false; |
b50fbe86 | 227 | break; |
bbbfd849 | 228 | } |
bbbfd849 | 229 | } |
58f237ca | 230 | } |
b50fbe86 JG |
231 | |
232 | MSG("%sSession `%s` destroyed", newline_needed ? "\n" : "", session.name); | |
58f237ca JG |
233 | newline_needed = false; |
234 | if (stats_str) { | |
b50fbe86 | 235 | MSG("%s", stats_str.get()); |
58f237ca | 236 | } |
1dac0189 | 237 | |
b50fbe86 JG |
238 | /* |
239 | * If the session being destroy is the "default" session as defined in the .lttngrc file, | |
240 | * destroy the file. | |
241 | */ | |
242 | const auto session_name = | |
243 | lttng::make_unique_wrapper<char, lttng::free>(get_session_name_quiet()); | |
244 | if (session_name && !strncmp(session.name, session_name.get(), NAME_MAX)) { | |
1dac0189 PPM |
245 | config_destroy_default(); |
246 | } | |
65f25c66 JRJ |
247 | |
248 | if (lttng_opt_mi) { | |
aabf6773 | 249 | ret = mi_lttng_session(writer, &session, 0); |
65f25c66 | 250 | if (ret) { |
b50fbe86 | 251 | return CMD_ERROR; |
65f25c66 JRJ |
252 | } |
253 | } | |
254 | ||
b50fbe86 | 255 | return CMD_SUCCESS; |
b09ee5ba | 256 | } |
f3ed775e | 257 | |
b50fbe86 | 258 | cmd_error_code destroy_sessions(const session_spec& spec) |
b09ee5ba | 259 | { |
b50fbe86 JG |
260 | bool had_warning = false; |
261 | bool had_error = false; | |
544349a3 JG |
262 | bool listing_failed = false; |
263 | ||
264 | const auto sessions = [&listing_failed, &spec]() -> session_list { | |
265 | try { | |
266 | return list_sessions(spec); | |
267 | } catch (const lttng::ctl::error& ctl_exception) { | |
268 | ERR_FMT("Failed to list sessions ({})", | |
269 | lttng_strerror(-ctl_exception.code())); | |
270 | listing_failed = true; | |
271 | return {}; | |
aabf6773 | 272 | } |
544349a3 JG |
273 | }(); |
274 | ||
42a11b8f | 275 | if (!listing_failed && sessions.size() == 0 && spec.type_ == session_spec::type::NAME) { |
b50fbe86 JG |
276 | ERR_FMT("Session `{}` not found", spec.value); |
277 | return CMD_ERROR; | |
278 | } | |
279 | ||
280 | if (listing_failed) { | |
281 | return CMD_FATAL; | |
544349a3 | 282 | } |
aabf6773 | 283 | |
544349a3 | 284 | for (const auto& session : sessions) { |
b50fbe86 | 285 | cmd_error_code sub_ret; |
aabf6773 | 286 | |
b50fbe86 JG |
287 | try { |
288 | sub_ret = destroy_session(session); | |
289 | } catch (const lttng::ctl::error& ctl_exception) { | |
290 | switch (ctl_exception.code()) { | |
291 | case LTTNG_ERR_NO_SESSION: | |
42a11b8f | 292 | if (spec.type_ != session_spec::type::NAME) { |
b50fbe86 JG |
293 | /* Session destroyed during command, ignore and carry-on. */ |
294 | sub_ret = CMD_SUCCESS; | |
295 | break; | |
296 | } else { | |
297 | sub_ret = CMD_ERROR; | |
298 | break; | |
299 | } | |
300 | case LTTNG_ERR_NO_SESSIOND: | |
301 | /* Don't keep going on a fatal error. */ | |
302 | return CMD_FATAL; | |
303 | default: | |
304 | /* Generic error. */ | |
305 | sub_ret = CMD_ERROR; | |
306 | ERR_FMT("Failed to destroy session `{}` ({})", | |
307 | session.name, | |
308 | lttng_strerror(-ctl_exception.code())); | |
309 | break; | |
310 | } | |
544349a3 | 311 | } |
b50fbe86 JG |
312 | |
313 | /* Keep going, but report the most serious state. */ | |
314 | had_warning |= sub_ret == CMD_WARNING; | |
315 | had_error |= sub_ret == CMD_ERROR; | |
b73d0b29 | 316 | } |
3285a971 | 317 | |
b50fbe86 JG |
318 | if (had_error) { |
319 | return CMD_ERROR; | |
320 | } else if (had_warning) { | |
321 | return CMD_WARNING; | |
322 | } else { | |
323 | return CMD_SUCCESS; | |
324 | } | |
f3ed775e | 325 | } |
544349a3 | 326 | } /* namespace */ |
f3ed775e DG |
327 | |
328 | /* | |
843f5df9 | 329 | * The 'destroy <options>' first level command |
f3ed775e DG |
330 | */ |
331 | int cmd_destroy(int argc, const char **argv) | |
332 | { | |
b09ee5ba | 333 | int opt; |
544349a3 | 334 | cmd_error_code command_ret = CMD_SUCCESS; |
aabf6773 | 335 | bool success; |
f3ed775e | 336 | static poptContext pc; |
cd9adb8b | 337 | const char *leftover = nullptr; |
42a11b8f | 338 | struct session_spec spec(session_spec::type::NAME); |
aabf6773 | 339 | session_list const sessions; |
65f25c66 | 340 | |
cd9adb8b | 341 | pc = poptGetContext(nullptr, argc, argv, long_options, 0); |
f3ed775e DG |
342 | poptReadDefaultConfig(pc, 0); |
343 | ||
344 | while ((opt = poptGetNextOpt(pc)) != -1) { | |
345 | switch (opt) { | |
346 | case OPT_HELP: | |
544349a3 JG |
347 | { |
348 | int ret; | |
349 | ||
4ba92f18 | 350 | SHOW_HELP(); |
544349a3 | 351 | command_ret = static_cast<cmd_error_code>(ret); |
aabf6773 | 352 | goto end; |
544349a3 | 353 | } |
679b4943 SM |
354 | case OPT_LIST_OPTIONS: |
355 | list_cmd_options(stdout, long_options); | |
aabf6773 OD |
356 | goto end; |
357 | case OPT_ALL: | |
42a11b8f | 358 | spec.type_ = session_spec::type::ALL; |
aabf6773 OD |
359 | break; |
360 | case OPT_ENABLE_GLOB: | |
42a11b8f | 361 | spec.type_ = session_spec::type::GLOB_PATTERN; |
b09ee5ba | 362 | break; |
f3ed775e | 363 | default: |
544349a3 | 364 | command_ret = CMD_UNDEFINED; |
aabf6773 | 365 | goto end; |
f3ed775e DG |
366 | } |
367 | } | |
368 | ||
65f25c66 | 369 | /* Mi preparation */ |
c7e35b03 | 370 | if (lttng_opt_mi) { |
65f25c66 JRJ |
371 | writer = mi_lttng_writer_create(fileno(stdout), lttng_opt_mi); |
372 | if (!writer) { | |
544349a3 | 373 | command_ret = CMD_ERROR; |
65f25c66 JRJ |
374 | goto end; |
375 | } | |
376 | ||
377 | /* Open command element */ | |
544349a3 JG |
378 | if (mi_lttng_writer_command_open(writer, mi_lttng_element_command_destroy)) { |
379 | command_ret = CMD_ERROR; | |
65f25c66 JRJ |
380 | goto end; |
381 | } | |
382 | ||
383 | /* Open output element */ | |
544349a3 JG |
384 | if (mi_lttng_writer_open_element(writer, mi_lttng_element_command_output)) { |
385 | command_ret = CMD_ERROR; | |
65f25c66 JRJ |
386 | goto end; |
387 | } | |
388 | ||
389 | /* For validation and semantic purpose we open a sessions element */ | |
544349a3 JG |
390 | if (mi_lttng_sessions_open(writer)) { |
391 | command_ret = CMD_ERROR; | |
65f25c66 JRJ |
392 | goto end; |
393 | } | |
394 | } | |
395 | ||
aabf6773 | 396 | spec.value = poptGetArg(pc); |
fd076c09 | 397 | |
aabf6773 | 398 | command_ret = destroy_sessions(spec); |
65f25c66 | 399 | |
aabf6773 | 400 | success = command_ret == CMD_SUCCESS; |
65f25c66 | 401 | |
68c7f6e5 JD |
402 | leftover = poptGetArg(pc); |
403 | if (leftover) { | |
404 | ERR("Unknown argument: %s", leftover); | |
544349a3 | 405 | command_ret = CMD_ERROR; |
aabf6773 | 406 | success = false; |
68c7f6e5 JD |
407 | } |
408 | ||
65f25c66 JRJ |
409 | /* Mi closing */ |
410 | if (lttng_opt_mi) { | |
411 | /* Close sessions and output element element */ | |
544349a3 | 412 | if (mi_lttng_close_multi_element(writer, 2)) { |
b50fbe86 | 413 | command_ret = CMD_ERROR; |
b09ee5ba FG |
414 | goto end; |
415 | } | |
fd076c09 | 416 | |
65f25c66 | 417 | /* Success ? */ |
544349a3 | 418 | if (mi_lttng_writer_write_element_bool( |
b50fbe86 JG |
419 | writer, mi_lttng_element_command_success, success)) { |
420 | command_ret = CMD_ERROR; | |
65f25c66 JRJ |
421 | goto end; |
422 | } | |
f3ed775e | 423 | |
65f25c66 | 424 | /* Command element close */ |
544349a3 | 425 | if (mi_lttng_writer_command_close(writer)) { |
b50fbe86 | 426 | command_ret = CMD_ERROR; |
65f25c66 JRJ |
427 | goto end; |
428 | } | |
429 | } | |
f3ed775e | 430 | end: |
65f25c66 JRJ |
431 | /* Mi clean-up */ |
432 | if (writer && mi_lttng_writer_destroy(writer)) { | |
544349a3 | 433 | command_ret = CMD_ERROR; |
65f25c66 JRJ |
434 | } |
435 | ||
fd076c09 | 436 | poptFreeContext(pc); |
544349a3 | 437 | return command_ret; |
f3ed775e | 438 | } |