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