Fix: notification: assert on len > 0 for dropped notification message
[lttng-tools.git] / src / lib / lttng-ctl / destruction-handle.cpp
1 /*
2 * Copyright (C) 2019 Jérémie Galarneau <jeremie.galarneau@efficios.com>
3 *
4 * SPDX-License-Identifier: LGPL-2.1-only
5 *
6 */
7
8 #include <lttng/destruction-handle.h>
9 #include <lttng/rotation.h>
10
11 #include <common/optional.hpp>
12 #include <common/compat/poll.hpp>
13 #include <common/compat/time.hpp>
14 #include <common/macros.hpp>
15 #include <common/compat/poll.hpp>
16 #include <common/dynamic-buffer.hpp>
17 #include <common/buffer-view.hpp>
18 #include <common/sessiond-comm/sessiond-comm.hpp>
19 #include <lttng/location-internal.hpp>
20 #include "lttng-ctl-helper.hpp"
21
22 #include <algorithm>
23 #include <stdbool.h>
24
25 enum communication_state {
26 COMMUNICATION_STATE_RECEIVE_LTTNG_MSG,
27 COMMUNICATION_STATE_RECEIVE_COMMAND_HEADER,
28 COMMUNICATION_STATE_RECEIVE_PAYLOAD,
29 COMMUNICATION_STATE_END,
30 COMMUNICATION_STATE_ERROR,
31 };
32
33 struct lttng_destruction_handle {
34 LTTNG_OPTIONAL(enum lttng_error_code) destruction_return_code;
35 LTTNG_OPTIONAL(enum lttng_rotation_state) rotation_state;
36 struct lttng_trace_archive_location *location;
37 struct {
38 int socket;
39 struct lttng_poll_event events;
40 size_t bytes_left_to_receive;
41 enum communication_state state;
42 struct lttng_dynamic_buffer buffer;
43 LTTNG_OPTIONAL(size_t) data_size;
44 } communication;
45 };
46
47 void lttng_destruction_handle_destroy(struct lttng_destruction_handle *handle)
48 {
49 int ret;
50
51 if (!handle) {
52 return;
53 }
54
55 if (handle->communication.socket >= 0) {
56 ret = close(handle->communication.socket);
57 if (ret) {
58 PERROR("Failed to close lttng-sessiond command socket");
59 }
60 }
61 lttng_poll_clean(&handle->communication.events);
62 lttng_dynamic_buffer_reset(&handle->communication.buffer);
63 lttng_trace_archive_location_put(handle->location);
64 free(handle);
65 }
66
67 static
68 struct lttng_destruction_handle *lttng_destruction_handle_create(
69 int sessiond_socket)
70 {
71 int ret;
72 struct lttng_destruction_handle *handle = zmalloc<lttng_destruction_handle>();
73
74 if (!handle) {
75 goto end;
76 }
77 lttng_dynamic_buffer_init(&handle->communication.buffer);
78 handle->communication.socket = sessiond_socket;
79 ret = lttng_poll_create(&handle->communication.events, 1, 0);
80 if (ret) {
81 goto error;
82 }
83
84 ret = lttng_poll_add(&handle->communication.events, sessiond_socket,
85 LPOLLIN | LPOLLHUP | LPOLLRDHUP | LPOLLERR);
86 if (ret) {
87 goto error;
88 }
89
90 handle->communication.bytes_left_to_receive =
91 sizeof(struct lttcomm_lttng_msg);
92 handle->communication.state = COMMUNICATION_STATE_RECEIVE_LTTNG_MSG;
93 end:
94 return handle;
95 error:
96 lttng_destruction_handle_destroy(handle);
97 return NULL;
98 }
99
100 static
101 int handle_state_transition(struct lttng_destruction_handle *handle)
102 {
103 int ret = 0;
104
105 LTTNG_ASSERT(handle->communication.bytes_left_to_receive == 0);
106
107 switch (handle->communication.state) {
108 case COMMUNICATION_STATE_RECEIVE_LTTNG_MSG:
109 {
110 const struct lttcomm_lttng_msg *msg =
111 (typeof(msg)) handle->communication.buffer.data;
112
113 LTTNG_OPTIONAL_SET(&handle->destruction_return_code,
114 (enum lttng_error_code) msg->ret_code);
115 if (handle->destruction_return_code.value != LTTNG_OK) {
116 handle->communication.state = COMMUNICATION_STATE_END;
117 break;
118 } else if (msg->cmd_header_size != sizeof(struct lttcomm_session_destroy_command_header) ||
119 msg->data_size > DEFAULT_MAX_TRACE_ARCHIVE_LOCATION_PAYLOAD_SIZE) {
120 handle->communication.state = COMMUNICATION_STATE_ERROR;
121 ret = -1;
122 break;
123 }
124
125 handle->communication.state =
126 COMMUNICATION_STATE_RECEIVE_COMMAND_HEADER;
127 handle->communication.bytes_left_to_receive =
128 msg->cmd_header_size;
129 LTTNG_OPTIONAL_SET(&handle->communication.data_size,
130 msg->data_size);
131 ret = lttng_dynamic_buffer_set_size(
132 &handle->communication.buffer, 0);
133 LTTNG_ASSERT(!ret);
134 break;
135 }
136 case COMMUNICATION_STATE_RECEIVE_COMMAND_HEADER:
137 {
138 const struct lttcomm_session_destroy_command_header *hdr =
139 (typeof(hdr)) handle->communication.buffer.data;
140
141 LTTNG_OPTIONAL_SET(&handle->rotation_state,
142 (enum lttng_rotation_state) hdr->rotation_state);
143 switch (handle->rotation_state.value) {
144 case LTTNG_ROTATION_STATE_COMPLETED:
145 handle->communication.state =
146 COMMUNICATION_STATE_RECEIVE_PAYLOAD;
147 handle->communication.bytes_left_to_receive =
148 LTTNG_OPTIONAL_GET(handle->communication.data_size);
149 break;
150 case LTTNG_ROTATION_STATE_ERROR:
151 case LTTNG_ROTATION_STATE_NO_ROTATION:
152 handle->communication.state = COMMUNICATION_STATE_END;
153 break;
154 default:
155 handle->communication.state = COMMUNICATION_STATE_ERROR;
156 ret = -1;
157 break;
158 }
159 break;
160 }
161 case COMMUNICATION_STATE_RECEIVE_PAYLOAD:
162 {
163 ssize_t location_ret;
164 struct lttng_trace_archive_location *location;
165 const struct lttng_buffer_view view =
166 lttng_buffer_view_from_dynamic_buffer(
167 &handle->communication.buffer, 0, -1);
168
169 location_ret = lttng_trace_archive_location_create_from_buffer(
170 &view, &location);
171 if (location_ret < 0) {
172 ERR("Failed to deserialize trace archive location");
173 handle->communication.state = COMMUNICATION_STATE_ERROR;
174 ret = -1;
175 break;
176 } else {
177 /* Ownership is transferred to the destruction handle. */
178 handle->location = location;
179 handle->communication.state = COMMUNICATION_STATE_END;
180 }
181 break;
182 }
183 default:
184 abort();
185 }
186
187 /* Clear reception buffer on state transition. */
188 if (lttng_dynamic_buffer_set_size(&handle->communication.buffer, 0)) {
189 abort();
190 }
191 return ret;
192 }
193
194 static
195 int handle_incoming_data(struct lttng_destruction_handle *handle)
196 {
197 int ret;
198 ssize_t comm_ret;
199 const size_t original_buffer_size = handle->communication.buffer.size;
200
201 /* Reserve space for reception. */
202 ret = lttng_dynamic_buffer_set_size(&handle->communication.buffer,
203 original_buffer_size + handle->communication.bytes_left_to_receive);
204 if (ret) {
205 goto end;
206 }
207
208 comm_ret = lttcomm_recv_unix_sock(handle->communication.socket,
209 handle->communication.buffer.data + original_buffer_size,
210 handle->communication.bytes_left_to_receive);
211 if (comm_ret <= 0) {
212 ret = -1;
213 goto end;
214 }
215
216 handle->communication.bytes_left_to_receive -= comm_ret;
217 if (handle->communication.bytes_left_to_receive == 0) {
218 ret = handle_state_transition(handle);
219 } else {
220 ret = lttng_dynamic_buffer_set_size(
221 &handle->communication.buffer,
222 original_buffer_size + comm_ret);
223 }
224 end:
225 return ret;
226 }
227
228 enum lttng_destruction_handle_status
229 lttng_destruction_handle_wait_for_completion(
230 struct lttng_destruction_handle *handle, int timeout_ms)
231 {
232 enum lttng_destruction_handle_status status;
233 unsigned long time_left_ms = 0;
234 const bool has_timeout = timeout_ms > 0;
235 struct timespec initial_time;
236
237 if (!handle) {
238 status = LTTNG_DESTRUCTION_HANDLE_STATUS_INVALID;
239 goto end;
240 }
241
242 if (handle->communication.state == COMMUNICATION_STATE_ERROR) {
243 status = LTTNG_DESTRUCTION_HANDLE_STATUS_ERROR;
244 goto end;
245 } else if (handle->communication.state == COMMUNICATION_STATE_END) {
246 status = LTTNG_DESTRUCTION_HANDLE_STATUS_COMPLETED;
247 goto end;
248 }
249 if (has_timeout) {
250 int ret = lttng_clock_gettime(CLOCK_MONOTONIC, &initial_time);
251 if (ret) {
252 status = LTTNG_DESTRUCTION_HANDLE_STATUS_ERROR;
253 goto end;
254 }
255 time_left_ms = (unsigned long) timeout_ms;
256 }
257
258 while (handle->communication.state != COMMUNICATION_STATE_END &&
259 (time_left_ms || !has_timeout)) {
260 int ret;
261 uint32_t revents;
262 struct timespec current_time, diff;
263 unsigned long diff_ms;
264
265 ret = lttng_poll_wait(&handle->communication.events,
266 has_timeout ? time_left_ms : -1);
267 if (ret == 0) {
268 /* timeout */
269 break;
270 } else if (ret < 0) {
271 status = LTTNG_DESTRUCTION_HANDLE_STATUS_ERROR;
272 goto end;
273 }
274
275 /* The sessiond connection socket is the only monitored fd. */
276 revents = LTTNG_POLL_GETEV(&handle->communication.events, 0);
277 if (revents & LPOLLIN) {
278 ret = handle_incoming_data(handle);
279 if (ret) {
280 handle->communication.state =
281 COMMUNICATION_STATE_ERROR;
282 status = LTTNG_DESTRUCTION_HANDLE_STATUS_ERROR;
283 goto end;
284 }
285 } else {
286 handle->communication.state = COMMUNICATION_STATE_ERROR;
287 status = LTTNG_DESTRUCTION_HANDLE_STATUS_ERROR;
288 goto end;
289 }
290 if (!has_timeout) {
291 continue;
292 }
293
294 ret = lttng_clock_gettime(CLOCK_MONOTONIC, &current_time);
295 if (ret) {
296 status = LTTNG_DESTRUCTION_HANDLE_STATUS_ERROR;
297 goto end;
298 }
299 diff = timespec_abs_diff(initial_time, current_time);
300 ret = timespec_to_ms(diff, &diff_ms);
301 if (ret) {
302 ERR("Failed to compute elapsed time while waiting for completion");
303 status = LTTNG_DESTRUCTION_HANDLE_STATUS_ERROR;
304 goto end;
305 }
306 DBG("%lums elapsed while waiting for session destruction completion",
307 diff_ms);
308 diff_ms = std::max(diff_ms, 1UL);
309 diff_ms = std::min(diff_ms, time_left_ms);
310 time_left_ms -= diff_ms;
311 }
312
313 status = handle->communication.state == COMMUNICATION_STATE_END ?
314 LTTNG_DESTRUCTION_HANDLE_STATUS_COMPLETED :
315 LTTNG_DESTRUCTION_HANDLE_STATUS_TIMEOUT;
316 end:
317 return status;
318 }
319
320 enum lttng_destruction_handle_status
321 lttng_destruction_handle_get_rotation_state(
322 const struct lttng_destruction_handle *handle,
323 enum lttng_rotation_state *rotation_state)
324 {
325 enum lttng_destruction_handle_status status =
326 LTTNG_DESTRUCTION_HANDLE_STATUS_OK;
327
328 if (!handle || !rotation_state) {
329 status = LTTNG_DESTRUCTION_HANDLE_STATUS_INVALID;
330 goto end;
331 }
332
333 if (!handle->rotation_state.is_set) {
334 status = LTTNG_DESTRUCTION_HANDLE_STATUS_INVALID;
335 goto end;
336 }
337 *rotation_state = handle->rotation_state.value;
338 end:
339 return status;
340 }
341
342 enum lttng_destruction_handle_status
343 lttng_destruction_handle_get_archive_location(
344 const struct lttng_destruction_handle *handle,
345 const struct lttng_trace_archive_location **location)
346 {
347 enum lttng_destruction_handle_status status =
348 LTTNG_DESTRUCTION_HANDLE_STATUS_OK;
349
350 if (!handle || !location) {
351 status = LTTNG_DESTRUCTION_HANDLE_STATUS_INVALID;
352 goto end;
353 }
354
355 if (!handle->location) {
356 status = LTTNG_DESTRUCTION_HANDLE_STATUS_INVALID;
357 goto end;
358 }
359 *location = handle->location;
360 end:
361 return status;
362 }
363
364 enum lttng_destruction_handle_status
365 lttng_destruction_handle_get_result(
366 const struct lttng_destruction_handle *handle,
367 enum lttng_error_code *result)
368 {
369 enum lttng_destruction_handle_status status =
370 LTTNG_DESTRUCTION_HANDLE_STATUS_OK;
371
372 if (!handle || !result) {
373 status = LTTNG_DESTRUCTION_HANDLE_STATUS_INVALID;
374 goto end;
375 }
376
377 if (!handle->destruction_return_code.is_set) {
378 status = LTTNG_DESTRUCTION_HANDLE_STATUS_INVALID;
379 goto end;
380 }
381 *result = handle->destruction_return_code.value;
382 end:
383 return status;
384 }
385
386 enum lttng_error_code lttng_destroy_session_ext(const char *session_name,
387 struct lttng_destruction_handle **_handle)
388 {
389 int ret;
390 ssize_t comm_ret;
391 enum lttng_error_code ret_code = LTTNG_OK;
392 struct lttcomm_session_msg lsm = {
393 .cmd_type = LTTNG_DESTROY_SESSION,
394 .session = {},
395 .domain = {},
396 .u = {},
397 .fd_count = 0,
398 };
399 int sessiond_socket = -1;
400 struct lttng_destruction_handle *handle = NULL;
401
402 if (!session_name) {
403 ret_code = LTTNG_ERR_INVALID;
404 goto error;
405 }
406
407 ret = lttng_strncpy(lsm.session.name, session_name,
408 sizeof(lsm.session.name));
409 if (ret) {
410 ret_code = LTTNG_ERR_INVALID;
411 goto error;
412 }
413
414 ret = connect_sessiond();
415 if (ret < 0) {
416 ret_code = LTTNG_ERR_NO_SESSIOND;
417 goto error;
418 } else {
419 sessiond_socket = ret;
420 }
421
422 handle = lttng_destruction_handle_create(sessiond_socket);
423 if (!handle) {
424 ret_code = LTTNG_ERR_NOMEM;
425 goto error;
426 }
427
428 comm_ret = lttcomm_send_creds_unix_sock(sessiond_socket, &lsm, sizeof(lsm));
429 if (comm_ret < 0) {
430 ret_code = LTTNG_ERR_FATAL;
431 goto error;
432 }
433 sessiond_socket = -1;
434
435 /* Transfer the handle to the caller. */
436 if (_handle) {
437 *_handle = handle;
438 handle = NULL;
439 }
440 error:
441 if (sessiond_socket >= 0) {
442 ret = close(sessiond_socket);
443 if (ret < 0) {
444 PERROR("Failed to close the LTTng session daemon connection socket");
445 }
446 }
447 if (handle) {
448 lttng_destruction_handle_destroy(handle);
449 }
450 return ret_code;
451 }
This page took 0.040798 seconds and 4 git commands to generate.