lttng-ctl: Expose sessiond cmd_clear_session command
authorMathieu Desnoyers <mathieu.desnoyers@efficios.com>
Wed, 18 Dec 2019 17:07:16 +0000 (12:07 -0500)
committerJérémie Galarneau <jeremie.galarneau@efficios.com>
Thu, 19 Dec 2019 22:21:11 +0000 (17:21 -0500)
Co-developed-by: Jonathan Rajotte <jonathan.rajotte-julien@efficios.com>
Signed-off-by: Jonathan Rajotte <jonathan.rajotte-julien@efficios.com>
Signed-off-by: Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
Change-Id: Id9d169daaa1c20912a6c105b4473c3f9f2ba3b49
Signed-off-by: Jérémie Galarneau <jeremie.galarneau@efficios.com>
include/Makefile.am
include/lttng/clear-handle.h [new file with mode: 0644]
include/lttng/clear.h [new file with mode: 0644]
src/common/sessiond-comm/sessiond-comm.h
src/lib/lttng-ctl/Makefile.am
src/lib/lttng-ctl/clear.c [new file with mode: 0644]

index 9f8bf230105d4d3846b438c707c7b0d18c461dce..ab5aa5375794501cc5b75930b1b7e123f6f488a2 100644 (file)
@@ -113,7 +113,9 @@ lttnginclude_HEADERS = \
        lttng/location.h \
        lttng/userspace-probe.h \
        lttng/session-descriptor.h \
-       lttng/destruction-handle.h
+       lttng/destruction-handle.h \
+       lttng/clear.h \
+       lttng/clear-handle.h
 
 lttngactioninclude_HEADERS= \
        lttng/action/action.h \
diff --git a/include/lttng/clear-handle.h b/include/lttng/clear-handle.h
new file mode 100644 (file)
index 0000000..ea9edd4
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2019 - Jérémie Galarneau <jeremie.galarneau@efficios.com>
+ * Copyright (C) 2019 - Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License, version 2.1 only,
+ * as published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef LTTNG_CLEAR_HANDLE_H
+#define LTTNG_CLEAR_HANDLE_H
+
+#include <lttng/lttng-error.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Handle used to represent a specific instance of session clear
+ * operation.
+ */
+struct lttng_clear_handle;
+
+/*
+ * Negative values indicate errors. Values >= 0 indicate success.
+ */
+enum lttng_clear_handle_status {
+       LTTNG_CLEAR_HANDLE_STATUS_ERROR = -2,
+       LTTNG_CLEAR_HANDLE_STATUS_INVALID = -1,
+       LTTNG_CLEAR_HANDLE_STATUS_OK = 0,
+       LTTNG_CLEAR_HANDLE_STATUS_COMPLETED = 1,
+       LTTNG_CLEAR_HANDLE_STATUS_TIMEOUT = 2,
+};
+
+/*
+ * Destroy an lttng_clear_handle.
+ * The handle should be discarded after this call.
+ */
+extern void lttng_clear_handle_destroy(struct lttng_clear_handle *handle);
+
+/*
+ * Wait for a session clear operation to complete.
+ *
+ * A negative timeout_ms value can be used to wait indefinitely.
+ *
+ * Returns LTTNG_CLEAR_HANDLE_STATUS_COMPLETED if the session clear
+ * operation was completed. LTTNG_CLEAR_HANDLE_STATUS_TIMEOUT is returned
+ * to indicate that the wait timed out.
+ * On error, one of the negative lttng_clear_handle_status is returned.
+ *
+ * Note: This function returning a success status does not mean that
+ * the clear operation itself succeeded; it indicates that the _wait_
+ * operation completed successfully.
+ */
+extern enum lttng_clear_handle_status
+       lttng_clear_handle_wait_for_completion(
+               struct lttng_clear_handle *handle, int timeout_ms);
+
+/*
+ * Get the result of a session clear operation.
+ *
+ * This function must be used on a clear handle which was successfully waited
+ * on.
+ *
+ * Returns LTTNG_CLEAR_HANDLE_STATUS_OK if the result of the session
+ * clear operation could be obtained. Check the value of 'result' to
+ * determine if the session clear operation completed successfully or not.
+ *
+ * On error, one of the negative lttng_clear_handle_status is returned.
+ * Returns LTTNG_CLEAR_HANDLE_STATUS_INVALID if the clear operation
+ * was not waited-on using the handle or if the arguments of the function are
+ * invalid (e.g. NULL).
+ */
+extern enum lttng_clear_handle_status
+       lttng_clear_handle_get_result(
+               const struct lttng_clear_handle *handle,
+               enum lttng_error_code *result);
+
+#endif /* LTTNG_CLEAR_HANDLE_H */
diff --git a/include/lttng/clear.h b/include/lttng/clear.h
new file mode 100644 (file)
index 0000000..68c0cd4
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2019 - Jérémie Galarneau <jeremie.galarneau@efficios.com>
+ * Copyright (C) 2019 - Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License, version 2.1 only,
+ * as published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef LTTNG_CLEAR_H
+#define LTTNG_CLEAR_H
+
+#include <lttng/lttng-error.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct lttng_clear_handle;
+
+/*
+ * Clear a tracing session.
+ *
+ * Clear the data buffers and trace data.
+ *
+ * For sessions saving trace data to disk and streaming over the network to a
+ * relay daemon, the buffers content and existing stream files are cleared when
+ * the clear command is issued.
+ *
+ * For snapshot sessions (flight recorder), only the buffer content is cleared.
+ * Prior snapshots are individually recorded to disk, and are therefore
+ * untouched by this "clear" command.
+ *
+ * For live sessions streaming over network to a relay daemon, the buffers
+ * will be cleared and the files on the relay daemon side will be cleared as
+ * well. However, any active live trace viewer currently reading an existing
+ * trace packet will be able to proceed to read that packet entirely before
+ * skipping over cleared stream data.
+ *
+ * The clear command guarantees that no trace data produced before this function
+ * is called will be present in the resulting trace.
+ *
+ * Trace data produced between the moment this function is called and when it
+ * returns might be present in the resulting trace.
+ *
+ * Provides an lttng_clear_handle which can be used to wait for the completion
+ * of the session's clear.
+ *
+ * Return LTTNG_OK on success else a negative LTTng error code. The returned
+ * handle is owned by the caller and must be free'd using
+ * lttng_clear_handle_destroy().
+ *
+ * Important error codes:
+ *    LTTNG_ERR_CLEAR_RELAY_DISALLOWED
+ *    LTTNG_ERR_CLEAR_NOT_AVAILABLE_RELAY
+ *    LTTNG_ERR_CLEAR_FAIL_CONSUMER
+*/
+extern enum lttng_error_code lttng_clear_session(const char *session_name,
+               struct lttng_clear_handle **handle);
+
+#endif /* LTTNG_CLEAR_H */
index 8c2a47538535e2e190a0d72c77b0d8d9b80f9cf9..cbb79821d5f42b917c2195c48e283c36eda2bef6 100644 (file)
@@ -107,7 +107,8 @@ enum lttcomm_sessiond_command {
        LTTNG_ROTATION_GET_INFO               = 46,
        LTTNG_ROTATION_SET_SCHEDULE           = 47,
        LTTNG_SESSION_LIST_ROTATION_SCHEDULES = 48,
-       LTTNG_CREATE_SESSION_EXT              = 49
+       LTTNG_CREATE_SESSION_EXT              = 49,
+       LTTNG_CLEAR_SESSION                   = 50,
 };
 
 enum lttcomm_relayd_command {
index f79c26e04b355d26f4162a43abd31862ffb35c47..bbf3d5b1eabd6713a5bfc15afd557b23f1eb9119 100644 (file)
@@ -6,7 +6,7 @@ lib_LTLIBRARIES = liblttng-ctl.la
 
 liblttng_ctl_la_SOURCES = lttng-ctl.c snapshot.c lttng-ctl-helper.h \
                lttng-ctl-health.c save.c load.c deprecated-symbols.c \
-               channel.c rotate.c event.c destruction-handle.c
+               channel.c rotate.c event.c destruction-handle.c clear.c
 
 liblttng_ctl_la_LDFLAGS = \
                $(LT_NO_UNDEFINED)
diff --git a/src/lib/lttng-ctl/clear.c b/src/lib/lttng-ctl/clear.c
new file mode 100644 (file)
index 0000000..4219f44
--- /dev/null
@@ -0,0 +1,347 @@
+/*
+ * Copyright (C) 2019 - Jérémie Galarneau <jeremie.galarneau@efficios.com>
+ * Copyright (C) 2019 - Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License, version 2.1 only,
+ * as published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#define _LGPL_SOURCE
+#include <assert.h>
+#include <string.h>
+
+#include <lttng/lttng-error.h>
+#include <lttng/clear.h>
+#include <lttng/clear-handle.h>
+#include <common/sessiond-comm/sessiond-comm.h>
+#include <common/macros.h>
+#include <common/compat/poll.h>
+#include <common/dynamic-buffer.h>
+#include <common/buffer-view.h>
+#include <common/optional.h>
+
+#include "lttng-ctl-helper.h"
+
+enum communication_state {
+       COMMUNICATION_STATE_RECEIVE_LTTNG_MSG,
+       COMMUNICATION_STATE_RECEIVE_COMMAND_HEADER,
+       COMMUNICATION_STATE_END,
+       COMMUNICATION_STATE_ERROR,
+};
+
+struct lttng_clear_handle {
+       LTTNG_OPTIONAL(enum lttng_error_code) clear_return_code;
+       struct {
+               int socket;
+               struct lttng_poll_event events;
+               size_t bytes_left_to_receive;
+               enum communication_state state;
+               struct lttng_dynamic_buffer buffer;
+               LTTNG_OPTIONAL(size_t) data_size;
+       } communication;
+};
+
+void lttng_clear_handle_destroy(struct lttng_clear_handle *handle)
+{
+       int ret;
+
+       if (!handle) {
+               return;
+       }
+
+       if (handle->communication.socket >= 0) {
+               ret = close(handle->communication.socket);
+               if (ret) {
+                       PERROR("Failed to close lttng-sessiond command socket");
+               }
+        }
+        lttng_poll_clean(&handle->communication.events);
+       lttng_dynamic_buffer_reset(&handle->communication.buffer);
+       free(handle);
+}
+
+static
+struct lttng_clear_handle *lttng_clear_handle_create(int sessiond_socket)
+{
+       int ret;
+       struct lttng_clear_handle *handle = zmalloc(sizeof(*handle));
+
+       if (!handle) {
+               goto end;
+       }
+       lttng_dynamic_buffer_init(&handle->communication.buffer);
+       handle->communication.socket = sessiond_socket;
+       ret = lttng_poll_create(&handle->communication.events, 1, 0);
+       if (ret) {
+               goto error;
+       }
+
+       ret = lttng_poll_add(&handle->communication.events, sessiond_socket,
+                       LPOLLIN | LPOLLHUP | LPOLLRDHUP | LPOLLERR);
+        if (ret) {
+               goto error;
+        }
+
+       handle->communication.bytes_left_to_receive =
+                       sizeof(struct lttcomm_lttng_msg);
+       handle->communication.state = COMMUNICATION_STATE_RECEIVE_LTTNG_MSG;
+end:
+       return handle;
+error:
+       lttng_clear_handle_destroy(handle);
+       return NULL;
+}
+
+static
+int handle_state_transition(struct lttng_clear_handle *handle)
+{
+       int ret = 0;
+
+       assert(handle->communication.bytes_left_to_receive == 0);
+
+       switch (handle->communication.state) {
+       case COMMUNICATION_STATE_RECEIVE_LTTNG_MSG:
+       {
+               const struct lttcomm_lttng_msg *msg =
+                               (typeof(msg)) handle->communication.buffer.data;
+
+               LTTNG_OPTIONAL_SET(&handle->clear_return_code,
+                               (enum lttng_error_code) msg->ret_code);
+               if (handle->clear_return_code.value != LTTNG_OK) {
+                       handle->communication.state = COMMUNICATION_STATE_END;
+                       break;
+               } else if (msg->cmd_header_size != 0 || msg->data_size != 0) {
+                       handle->communication.state = COMMUNICATION_STATE_ERROR;
+                       ret = -1;
+                       break;
+               }
+
+               handle->communication.state = COMMUNICATION_STATE_END;
+               handle->communication.bytes_left_to_receive = 0;
+               LTTNG_OPTIONAL_SET(&handle->communication.data_size, 0);
+               ret = lttng_dynamic_buffer_set_size(
+                               &handle->communication.buffer, 0);
+               assert(!ret);
+               break;
+       }
+       default:
+               abort();
+       }
+
+       /* Clear reception buffer on state transition. */
+       if (lttng_dynamic_buffer_set_size(&handle->communication.buffer, 0)) {
+               abort();
+       }
+       return ret;
+}
+
+static
+int handle_incoming_data(struct lttng_clear_handle *handle)
+{
+       int ret;
+       ssize_t comm_ret;
+       const size_t original_buffer_size = handle->communication.buffer.size;
+
+       /* Reserve space for reception. */
+       ret = lttng_dynamic_buffer_set_size(&handle->communication.buffer,
+                       original_buffer_size + handle->communication.bytes_left_to_receive);
+       if (ret) {
+               goto end;
+       }
+
+       comm_ret = lttcomm_recv_unix_sock(handle->communication.socket,
+                       handle->communication.buffer.data + original_buffer_size,
+                       handle->communication.bytes_left_to_receive);
+       if (comm_ret <= 0) {
+               ret = -1;
+               goto end;
+       }
+
+       handle->communication.bytes_left_to_receive -= comm_ret;
+       if (handle->communication.bytes_left_to_receive == 0) {
+               ret = handle_state_transition(handle);
+       } else {
+               ret = lttng_dynamic_buffer_set_size(
+                               &handle->communication.buffer,
+                               original_buffer_size + comm_ret);
+       }
+end:
+       return ret;
+}
+
+extern enum lttng_clear_handle_status
+       lttng_clear_handle_wait_for_completion(
+               struct lttng_clear_handle *handle, int timeout_ms)
+{
+       int ret;
+       enum lttng_clear_handle_status status;
+       unsigned long time_left_ms = 0;
+       const bool has_timeout = timeout_ms > 0;
+       struct timespec initial_time;
+
+        if (handle->communication.state == COMMUNICATION_STATE_ERROR) {
+               status = LTTNG_CLEAR_HANDLE_STATUS_ERROR;
+               goto end;
+       } else if (handle->communication.state == COMMUNICATION_STATE_END) {
+               status = LTTNG_CLEAR_HANDLE_STATUS_COMPLETED;
+               goto end;
+       }
+        if (has_timeout) {
+               ret = lttng_clock_gettime(CLOCK_MONOTONIC, &initial_time);
+               if (ret) {
+                       status = LTTNG_CLEAR_HANDLE_STATUS_ERROR;
+                       goto end;
+               }
+               time_left_ms = (unsigned long) timeout_ms;
+        }
+
+        while (handle->communication.state != COMMUNICATION_STATE_END &&
+                       (time_left_ms || !has_timeout)) {
+               int ret;
+               uint32_t revents;
+                struct timespec current_time, diff;
+               unsigned long diff_ms;
+
+                ret = lttng_poll_wait(&handle->communication.events,
+                               has_timeout ? time_left_ms : -1);
+                if (ret == 0) {
+                       /* timeout */
+                       break;
+               } else if (ret < 0) {
+                       status = LTTNG_CLEAR_HANDLE_STATUS_ERROR;
+                       goto end;
+               }
+
+               /* The sessiond connection socket is the only monitored fd. */
+               revents = LTTNG_POLL_GETEV(&handle->communication.events, 0);
+               if (revents & LPOLLIN) {
+                       ret = handle_incoming_data(handle);
+                       if (ret) {
+                               handle->communication.state =
+                                               COMMUNICATION_STATE_ERROR;
+                               status = LTTNG_CLEAR_HANDLE_STATUS_ERROR;
+                               goto end;
+                       }
+               } else {
+                       handle->communication.state = COMMUNICATION_STATE_ERROR;
+                       status = LTTNG_CLEAR_HANDLE_STATUS_ERROR;
+                       goto end;
+               }
+               if (!has_timeout) {
+                       continue;
+               }
+
+               ret = lttng_clock_gettime(CLOCK_MONOTONIC, &current_time);
+               if (ret) {
+                       status = LTTNG_CLEAR_HANDLE_STATUS_ERROR;
+                       goto end;
+               }
+               diff = timespec_abs_diff(initial_time, current_time);
+               ret = timespec_to_ms(diff, &diff_ms);
+               if (ret) {
+                       ERR("Failed to compute elapsed time while waiting for completion");
+                       status = LTTNG_CLEAR_HANDLE_STATUS_ERROR;
+                       goto end;
+               }
+               DBG("%lums elapsed while waiting for session clear completion",
+                               diff_ms);
+               diff_ms = max_t(unsigned long, diff_ms, 1);
+               diff_ms = min_t(unsigned long, diff_ms, time_left_ms);
+               time_left_ms -= diff_ms;
+       }
+
+       status = handle->communication.state == COMMUNICATION_STATE_END ?
+                       LTTNG_CLEAR_HANDLE_STATUS_COMPLETED :
+                       LTTNG_CLEAR_HANDLE_STATUS_TIMEOUT;
+end:
+       return status;
+}
+
+extern enum lttng_clear_handle_status
+       lttng_clear_handle_get_result(
+               const struct lttng_clear_handle *handle,
+               enum lttng_error_code *result)
+{
+       enum lttng_clear_handle_status status =
+                       LTTNG_CLEAR_HANDLE_STATUS_OK;
+
+       if (!handle->clear_return_code.is_set) {
+               status = LTTNG_CLEAR_HANDLE_STATUS_INVALID;
+               goto end;
+       }
+       *result = handle->clear_return_code.value;
+end:
+       return status;
+}
+
+/*
+ * Clear the session
+ */
+enum lttng_error_code lttng_clear_session(const char *session_name,
+               struct lttng_clear_handle **_handle)
+{
+       enum lttng_error_code ret_code = LTTNG_OK;
+       struct lttng_clear_handle *handle = NULL;
+       struct lttcomm_session_msg lsm = {
+               .cmd_type = LTTNG_CLEAR_SESSION,
+       };
+       int sessiond_socket = -1;
+       ssize_t comm_ret;
+       int ret;
+
+       if (session_name == NULL) {
+               ret_code = LTTNG_ERR_INVALID;
+               goto error;
+       }
+       ret = lttng_strncpy(lsm.session.name, session_name,
+                       sizeof(lsm.session.name));
+       if (ret) {
+               ret_code = LTTNG_ERR_INVALID;
+               goto error;
+       }
+       ret = connect_sessiond();
+       if (ret < 0) {
+               ret_code = LTTNG_ERR_NO_SESSIOND;
+               goto error;
+       } else {
+               sessiond_socket = ret;
+       }
+       handle = lttng_clear_handle_create(sessiond_socket);
+       if (!handle) {
+               ret_code = LTTNG_ERR_NOMEM;
+               goto error;
+       }
+       comm_ret = lttcomm_send_creds_unix_sock(sessiond_socket, &lsm, sizeof(lsm));
+       if (comm_ret < 0) {
+               ret_code = LTTNG_ERR_FATAL;
+               goto error;
+       }
+       sessiond_socket = -1;
+
+error:
+       /* Transfer the handle to the caller. */
+       if (_handle) {
+               *_handle = handle;
+               handle = NULL;
+       }
+       if (sessiond_socket >= 0) {
+               ret = close(sessiond_socket);
+               if (ret < 0) {
+                       PERROR("Failed to close the LTTng session daemon connection socket");
+               }
+       }
+       if (handle) {
+               lttng_clear_handle_destroy(handle);
+       }
+       return ret_code;
+}
This page took 0.032676 seconds and 5 git commands to generate.