lttng/rotation.h \
lttng/location.h \
lttng/userspace-probe.h \
- lttng/session-descriptor.h
+ lttng/session-descriptor.h \
+ lttng/destruction-handle.h
lttngactioninclude_HEADERS= \
lttng/action/action.h \
--- /dev/null
+/*
+ * Copyright (C) 2019 - Jérémie Galarneau <jeremie.galarneau@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_DESTRUCTION_HANDLE_H
+#define LTTNG_DESTRUCTION_HANDLE_H
+
+#include <lttng/rotation.h>
+#include <lttng/lttng-error.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct lttng_destruction_handle;
+
+enum lttng_destruction_handle_status {
+ LTTNG_DESTRUCTION_HANDLE_STATUS_ERROR = -2,
+ LTTNG_DESTRUCTION_HANDLE_STATUS_INVALID = -1,
+ LTTNG_DESTRUCTION_HANDLE_STATUS_OK = 0,
+ LTTNG_DESTRUCTION_HANDLE_STATUS_COMPLETED = 1,
+ LTTNG_DESTRUCTION_HANDLE_STATUS_TIMEOUT = 2,
+};
+
+extern void lttng_destruction_handle_destroy(
+ struct lttng_destruction_handle *handle);
+
+extern enum lttng_destruction_handle_status
+lttng_destruction_handle_wait_for_completion(
+ struct lttng_destruction_handle *handle, int timeout_ms);
+
+extern enum lttng_destruction_handle_status
+lttng_destruction_handle_get_result(
+ const struct lttng_destruction_handle *handle,
+ enum lttng_error_code *result);
+
+extern enum lttng_destruction_handle_status
+lttng_destruction_handle_get_rotation_state(
+ const struct lttng_destruction_handle *handle,
+ enum lttng_rotation_state *rotation_state);
+
+extern enum lttng_destruction_handle_status
+lttng_destruction_handle_get_archive_location(
+ const struct lttng_destruction_handle *handle,
+ const struct lttng_trace_archive_location **location);
+
+#endif /* LTTNG_DESTRUCTION_HANDLE_H */
LTTNG_ERR_CREATE_TRACE_CHUNK_FAIL_CONSUMER = 149, /* trace chunk creation failure on consumer */
LTTNG_ERR_CLOSE_TRACE_CHUNK_FAIL_CONSUMER = 150, /* trace chunk close failure on consumer */
LTTNG_ERR_TRACE_CHUNK_EXISTS_FAIL_CONSUMER = 151, /* failed to query consumer for trace chunk existence */
+ LTTNG_ERR_INVALID_PROTOCOL = 152, /* a protocol error occurred */
/* MUST be last element */
LTTNG_ERR_NR, /* Last element */
struct lttng_rotation_get_info_return {
/* Represents values defined in enum lttng_rotation_state. */
int32_t status;
- /* Represents values defined in enum lttng_rotation_state. */
+ /*
+ * Represents values defined in enum lttng_trace_archive_location_type.
+ */
int8_t location_type;
union {
struct {
/*
* Copyright (C) 2014 - David Goulet <dgoulet@efficios.com>
+ * Copyright (C) 2019 - Jérémie Galarneau <jeremie.galarneau@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,
#endif
struct lttng_session_descriptor;
+struct lttng_destruction_handle;
/*
* Basic session information.
*
* The name can't be NULL here.
*
- * Return 0 on success else a negative LTTng error code.
+ * Returns LTTNG_OK on success, else a negative LTTng error code.
*/
extern int lttng_destroy_session(const char *name);
+/*
+ * Destroy a tracing session.
+ *
+ * Performs the same function as lttng_destroy_session(), but provides
+ * an lttng_destruction_handle which can be used to wait for the completion
+ * of the session's destruction. The lttng_destroy_handle can also be used
+ * obtain the status and archive location of any implicit session
+ * rotation that may have occured during the session's destruction.
+ *
+ * Returns LTTNG_OK on success. The returned handle is owned by the caller
+ * and must be free'd using lttng_destruction_handle_destroy().
+ */
+extern enum lttng_error_code lttng_destroy_session_ext(const char *session_name,
+ struct lttng_destruction_handle **handle);
+
/*
* Behaves exactly like lttng_destroy_session but does not wait for data
* availability.
* Return any error encountered or 0 for success.
*
* "sock" is only used for special-case var. len data.
+ * A command may assume the ownership of the socket, in which case its value
+ * should be set to -1.
*
* Should *NOT* be called with RCU read-side lock held.
*/
-static int process_client_msg(struct command_ctx *cmd_ctx, int sock,
+static int process_client_msg(struct command_ctx *cmd_ctx, int *sock,
int *sock_error)
{
int ret = LTTNG_OK;
cmd_ctx->lsm->u.context.ctx.u.app_ctx.ctx_name =
context_name;
- ret = lttcomm_recv_unix_sock(sock, provider_name,
+ ret = lttcomm_recv_unix_sock(*sock, provider_name,
provider_name_len);
if (ret < 0) {
goto error_add_context;
}
- ret = lttcomm_recv_unix_sock(sock, context_name,
+ ret = lttcomm_recv_unix_sock(*sock, context_name,
context_name_len);
if (ret < 0) {
goto error_add_context;
DBG("Discarding disable event command payload of size %zu", count);
while (count) {
- ret = lttcomm_recv_unix_sock(sock, data,
+ ret = lttcomm_recv_unix_sock(*sock, data,
count > sizeof(data) ? sizeof(data) : count);
if (ret < 0) {
goto error;
DBG("Receiving var len exclusion event list from client ...");
exclusion->count = count;
- ret = lttcomm_recv_unix_sock(sock, exclusion->names,
+ ret = lttcomm_recv_unix_sock(*sock, exclusion->names,
count * LTTNG_SYMBOL_NAME_LEN);
if (ret <= 0) {
DBG("Nothing recv() from client var len data... continuing");
/* Receive var. len. data */
DBG("Receiving var len filter's expression from client ...");
- ret = lttcomm_recv_unix_sock(sock, filter_expression,
+ ret = lttcomm_recv_unix_sock(*sock, filter_expression,
expression_len);
if (ret <= 0) {
DBG("Nothing recv() from client var len data... continuing");
/* Receive var. len. data */
DBG("Receiving var len filter's bytecode from client ...");
- ret = lttcomm_recv_unix_sock(sock, bytecode, bytecode_len);
+ ret = lttcomm_recv_unix_sock(*sock, bytecode, bytecode_len);
if (ret <= 0) {
DBG("Nothing recv() from client var len data... continuing");
*sock_error = 1;
if (cmd_ctx->lsm->u.enable.userspace_probe_location_len > 0) {
/* Expect a userspace probe description. */
- ret = receive_userspace_probe(cmd_ctx, sock, sock_error, ev);
+ ret = receive_userspace_probe(cmd_ctx, *sock, sock_error, ev);
if (ret) {
free(filter_expression);
free(bytecode);
/* Receive variable len data */
DBG("Receiving %zu URI(s) from client ...", nb_uri);
- ret = lttcomm_recv_unix_sock(sock, uris, len);
+ ret = lttcomm_recv_unix_sock(*sock, uris, len);
if (ret <= 0) {
DBG("No URIs received from client... continuing");
*sock_error = 1;
case LTTNG_DESTROY_SESSION:
{
ret = cmd_destroy_session(cmd_ctx->session,
- notification_thread_handle);
+ notification_thread_handle,
+ sock);
break;
}
case LTTNG_LIST_DOMAINS:
struct lttng_session_descriptor *return_descriptor = NULL;
lttng_dynamic_buffer_init(&payload);
- ret = cmd_create_session(cmd_ctx, sock, &return_descriptor);
+ ret = cmd_create_session(cmd_ctx, *sock, &return_descriptor);
if (ret != LTTNG_OK) {
goto error;
}
}
case LTTNG_REGISTER_TRIGGER:
{
- ret = cmd_register_trigger(cmd_ctx, sock,
+ ret = cmd_register_trigger(cmd_ctx, *sock,
notification_thread_handle);
break;
}
case LTTNG_UNREGISTER_TRIGGER:
{
- ret = cmd_unregister_trigger(cmd_ctx, sock,
+ ret = cmd_unregister_trigger(cmd_ctx, *sock,
notification_thread_handle);
break;
}
if (cmd_ctx->session) {
session_unlock(cmd_ctx->session);
session_put(cmd_ctx->session);
+ cmd_ctx->session = NULL;
}
if (need_tracing_session) {
session_unlock_list();
* informations for the client. The command context struct contains
* everything this function may needs.
*/
- ret = process_client_msg(cmd_ctx, sock, &sock_error);
+ ret = process_client_msg(cmd_ctx, &sock, &sock_error);
rcu_thread_offline();
if (ret < 0) {
- ret = close(sock);
- if (ret) {
- PERROR("close");
- }
- sock = -1;
+ if (sock >= 0) {
+ ret = close(sock);
+ if (ret) {
+ PERROR("close");
+ }
+ }
+ sock = -1;
/*
* TODO: Inform client somehow of the fatal error. At
* this point, ret < 0 means that a zmalloc failed
health_code_update();
- DBG("Sending response (size: %d, retcode: %s (%d))",
- cmd_ctx->lttng_msg_size,
- lttng_strerror(-cmd_ctx->llm->ret_code),
- cmd_ctx->llm->ret_code);
- ret = send_unix_sock(sock, cmd_ctx->llm, cmd_ctx->lttng_msg_size);
- if (ret < 0) {
- ERR("Failed to send data back to client");
- }
+ if (sock >= 0) {
+ DBG("Sending response (size: %d, retcode: %s (%d))",
+ cmd_ctx->lttng_msg_size,
+ lttng_strerror(-cmd_ctx->llm->ret_code),
+ cmd_ctx->llm->ret_code);
+ ret = send_unix_sock(sock, cmd_ctx->llm,
+ cmd_ctx->lttng_msg_size);
+ if (ret < 0) {
+ ERR("Failed to send data back to client");
+ }
- /* End of transmission */
- ret = close(sock);
- if (ret) {
- PERROR("close");
- }
- sock = -1;
+ /* End of transmission */
+ ret = close(sock);
+ if (ret) {
+ PERROR("close");
+ }
+ }
+ sock = -1;
clean_command_ctx(&cmd_ctx);
#include <common/dynamic-buffer.h>
#include <common/buffer-view.h>
#include <common/trace-chunk.h>
+#include <lttng/location-internal.h>
#include <lttng/trigger/trigger-internal.h>
#include <lttng/condition/condition.h>
#include <lttng/action/action.h>
/* Sleep for 100ms between each check for the shm path's deletion. */
#define SESSION_DESTROY_SHM_PATH_CHECK_DELAY_US 100000
+struct cmd_destroy_session_reply_context {
+ int reply_sock_fd;
+ bool implicit_rotation_on_destroy;
+};
+
static enum lttng_error_code wait_on_path(void *path);
/*
return ret_code;
}
+static
+void cmd_destroy_session_reply(const struct ltt_session *session,
+ void *_reply_context)
+{
+ int ret;
+ ssize_t comm_ret;
+ const struct cmd_destroy_session_reply_context *reply_context =
+ _reply_context;
+ struct lttng_dynamic_buffer payload;
+ struct lttcomm_session_destroy_command_header cmd_header;
+ struct lttng_trace_archive_location *location = NULL;
+ struct lttcomm_lttng_msg llm = {
+ .cmd_type = LTTNG_DESTROY_SESSION,
+ .ret_code = LTTNG_OK,
+ .pid = UINT32_MAX,
+ .cmd_header_size =
+ sizeof(struct lttcomm_session_destroy_command_header),
+ .data_size = 0,
+ };
+ size_t payload_size_before_location;
+
+ lttng_dynamic_buffer_init(&payload);
+
+ ret = lttng_dynamic_buffer_append(&payload, &llm, sizeof(llm));
+ if (ret) {
+ ERR("Failed to append session destruction message");
+ goto error;
+ }
+
+ cmd_header.rotation_state =
+ (int32_t) (reply_context->implicit_rotation_on_destroy ?
+ session->rotation_state :
+ LTTNG_ROTATION_STATE_NO_ROTATION);
+ ret = lttng_dynamic_buffer_append(&payload, &cmd_header,
+ sizeof(cmd_header));
+ if (ret) {
+ ERR("Failed to append session destruction command header");
+ goto error;
+ }
+
+ if (!reply_context->implicit_rotation_on_destroy) {
+ DBG("No implicit rotation performed during the destruction of session \"%s\", sending reply",
+ session->name);
+ goto send_reply;
+ }
+ if (session->rotation_state != LTTNG_ROTATION_STATE_COMPLETED) {
+ DBG("Rotation state of session \"%s\" is not \"completed\", sending session destruction reply",
+ session->name);
+ goto send_reply;
+ }
+
+ location = session_get_trace_archive_location(session);
+ if (!location) {
+ ERR("Failed to get the location of the trace archive produced during the destruction of session \"%s\"",
+ session->name);
+ goto error;
+ }
+
+ payload_size_before_location = payload.size;
+ comm_ret = lttng_trace_archive_location_serialize(location,
+ &payload);
+ if (comm_ret < 0) {
+ ERR("Failed to serialize the location of the trace archive produced during the destruction of session \"%s\"",
+ session->name);
+ goto error;
+ }
+ /* Update the message to indicate the location's length. */
+ ((struct lttcomm_lttng_msg *) payload.data)->data_size =
+ payload.size - payload_size_before_location;
+send_reply:
+ comm_ret = lttcomm_send_unix_sock(reply_context->reply_sock_fd,
+ payload.data, payload.size);
+ if (comm_ret != (ssize_t) payload.size) {
+ ERR("Failed to send result of the destruction of session \"%s\" to client",
+ session->name);
+ }
+error:
+ ret = close(reply_context->reply_sock_fd);
+ if (ret) {
+ PERROR("Failed to close client socket in deferred session destroy reply");
+ }
+ lttng_dynamic_buffer_reset(&payload);
+ free(_reply_context);
+}
+
/*
* Command LTTNG_DESTROY_SESSION processed by the client thread.
*
* Called with session lock held.
*/
int cmd_destroy_session(struct ltt_session *session,
- struct notification_thread_handle *notification_thread_handle)
+ struct notification_thread_handle *notification_thread_handle,
+ int *sock_fd)
{
int ret;
+ struct cmd_destroy_session_reply_context *reply_context = NULL;
+
+ if (sock_fd) {
+ reply_context = zmalloc(sizeof(*reply_context));
+ if (!reply_context) {
+ ret = LTTNG_ERR_NOMEM;
+ goto end;
+ }
+ reply_context->reply_sock_fd = *sock_fd;
+ }
/* Safety net */
assert(session);
- DBG("Begin destroy session %s (id %" PRIu64 ")", session->name, session->id);
+ DBG("Begin destroy session %s (id %" PRIu64 ")", session->name,
+ session->id);
+ if (session->active) {
+ DBG("Session \"%s\" is active, attempting to stop it before destroying it",
+ session->name);
+ ret = cmd_stop_trace(session);
+ if (ret != LTTNG_OK && ret != LTTNG_ERR_TRACE_ALREADY_STOPPED) {
+ /* Carry on with the destruction of the session. */
+ ERR("Failed to stop session \"%s\" as part of its destruction: %s",
+ session->name, lttng_strerror(-ret));
+ }
+ }
if (session->rotation_schedule_timer_enabled) {
if (timer_session_rotation_schedule_timer_stop(
ERR("Failed to perform an implicit rotation as part of the destruction of session \"%s\": %s",
session->name, lttng_strerror(-ret));
}
- }
+ if (reply_context) {
+ reply_context->implicit_rotation_on_destroy = true;
+ }
+ }
if (session->shm_path[0]) {
/*
* _at least_ up to the point when that reference is released.
*/
session_destroy(session);
- ret = LTTNG_OK;
-
+ if (reply_context) {
+ ret = session_add_destroy_notifier(session,
+ cmd_destroy_session_reply,
+ (void *) reply_context);
+ if (ret) {
+ ret = LTTNG_ERR_FATAL;
+ goto end;
+ } else {
+ *sock_fd = -1;
+ }
+ }
+ ret = LTTNG_OK;
+end:
return ret;
}
enum lttng_error_code cmd_create_session(struct command_ctx *cmd_ctx, int sock,
struct lttng_session_descriptor **return_descriptor);
int cmd_destroy_session(struct ltt_session *session,
- struct notification_thread_handle *notification_thread_handle);
+ struct notification_thread_handle *notification_thread_handle,
+ int *sock_fd);
/* Channel commands */
int cmd_disable_channel(struct ltt_session *session,
goto unlock_session;
}
(void) cmd_stop_trace(session);
- (void) cmd_destroy_session(session,
- notification_thread_handle);
+ (void) cmd_destroy_session(session, notification_thread_handle,
+ NULL);
unlock_session:
session_unlock(session);
session_put(session);
#include "trace-ust.h"
#include "timer.h"
+struct ltt_session_destroy_notifier_element {
+ ltt_session_destroy_notifier notifier;
+ void *user_data;
+};
+
/*
* NOTES:
*
* The caller must hold the session lock.
*/
struct lttng_trace_archive_location *session_get_trace_archive_location(
- struct ltt_session *session)
+ const struct ltt_session *session)
{
int ret;
struct lttng_trace_archive_location *location = NULL;
current_trace_chunk);
}
+static
+void session_notify_destruction(const struct ltt_session *session)
+{
+ size_t i;
+ const size_t count = lttng_dynamic_array_get_count(
+ &session->destroy_notifiers);
+
+ for (i = 0; i < count; i++) {
+ const struct ltt_session_destroy_notifier_element *element =
+ lttng_dynamic_array_get_element(
+ &session->destroy_notifiers, i);
+
+ element->notifier(session, element->user_data);
+ }
+}
+
static
void session_release(struct urcu_ref *ref)
{
usess = session->ust_session;
ksess = session->kernel_session;
+
+ session_notify_destruction(session);
+ lttng_dynamic_array_reset(&session->destroy_notifiers, NULL);
if (session->current_trace_chunk) {
ret = session_close_trace_chunk(session, session->current_trace_chunk);
if (ret) {
session_put(session);
}
+int session_add_destroy_notifier(struct ltt_session *session,
+ ltt_session_destroy_notifier notifier, void *user_data)
+{
+ const struct ltt_session_destroy_notifier_element element = {
+ .notifier = notifier,
+ .user_data = user_data
+ };
+
+ return lttng_dynamic_array_add_element(&session->destroy_notifiers,
+ &element);
+}
+
/*
* Return a ltt_session structure ptr that matches name. If no session found,
* NULL is returned. This must be called with the session list lock held using
goto error;
}
+ lttng_dynamic_array_init(&new_session->destroy_notifiers,
+ sizeof(struct ltt_session_destroy_notifier_element));
urcu_ref_init(&new_session->ref);
pthread_mutex_init(&new_session->lock, NULL);
#include <urcu/list.h>
#include <common/hashtable/hashtable.h>
+#include <common/dynamic-array.h>
#include <lttng/rotation.h>
#include <lttng/location.h>
#include <lttng/lttng-error.h>
struct ltt_ust_session;
+typedef void (*ltt_session_destroy_notifier)(const struct ltt_session *session,
+ void *user_data);
+
/*
* Tracing session list
*
enum lttng_rotation_state rotation_state;
char *last_archived_chunk_name;
LTTNG_OPTIONAL(uint64_t) last_archived_chunk_id;
+ struct lttng_dynamic_array destroy_notifiers;
};
/* Prototypes */
void session_unlock_list(void);
void session_destroy(struct ltt_session *session);
+int session_add_destroy_notifier(struct ltt_session *session,
+ ltt_session_destroy_notifier notifier, void *user_data);
bool session_get(struct ltt_session *session);
void session_put(struct ltt_session *session);
const struct ltt_session *session,
uint16_t *control_port, uint16_t *data_port);
struct lttng_trace_archive_location *session_get_trace_archive_location(
- struct ltt_session *session);
+ const struct ltt_session *session);
struct ltt_session *session_find_by_name(const char *name);
struct ltt_session *session_find_by_id(uint64_t id);
print_session_stats(session->name);
}
- ret = lttng_destroy_session_no_wait(session->name);
+ ret = lttng_destroy_session(session->name);
if (ret < 0) {
goto error;
}
/* Default maximal size of message notification channel message payloads. */
#define DEFAULT_MAX_NOTIFICATION_CLIENT_MESSAGE_PAYLOAD_SIZE 65536
+/* Default maximal size of trace archive location. */
+#define DEFAULT_MAX_TRACE_ARCHIVE_LOCATION_PAYLOAD_SIZE 65536
+
/* Default maximal size of message notification channel message payloads. */
#define DEFAULT_CLIENT_MAX_QUEUED_NOTIFICATIONS_COUNT 100
[ ERROR_INDEX(LTTNG_ERR_CREATE_TRACE_CHUNK_FAIL_CONSUMER) ] = "Trace chunk creation failed on consumer",
[ ERROR_INDEX(LTTNG_ERR_CLOSE_TRACE_CHUNK_FAIL_CONSUMER) ] = "Trace chunk close failed on consumer",
[ ERROR_INDEX(LTTNG_ERR_TRACE_CHUNK_EXISTS_FAIL_CONSUMER) ] = "Failed to query consumer for trace chunk existence",
+ [ ERROR_INDEX(LTTNG_ERR_INVALID_PROTOCOL) ] = "Protocol error occurred",
/* Last element */
[ ERROR_INDEX(LTTNG_ERR_NR) ] = "Unknown error code"
uint32_t userspace_probe_location_len;
} LTTNG_PACKED;
+/*
+ * Command header of the reply to an LTTNG_DESTROY_SESSION command.
+ */
+struct lttcomm_session_destroy_command_header {
+ /* enum lttng_session */
+ int32_t rotation_state;
+};
+
/*
* Data structure for the response from sessiond to the lttng client.
*/
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
+ channel.c rotate.c event.c destruction-handle.c
liblttng_ctl_la_LDFLAGS = \
$(LT_NO_UNDEFINED)
--- /dev/null
+/*
+ * Copyright (C) 2019 - Jérémie Galarneau <jeremie.galarneau@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
+ */
+
+#include <lttng/destruction-handle.h>
+#include <lttng/rotation.h>
+
+#include <common/optional.h>
+#include <common/compat/poll.h>
+#include <common/compat/time.h>
+#include <common/macros.h>
+#include <common/compat/poll.h>
+#include <common/dynamic-buffer.h>
+#include <common/buffer-view.h>
+#include <common/sessiond-comm/sessiond-comm.h>
+#include <lttng/location-internal.h>
+#include "lttng-ctl-helper.h"
+
+#include <stdbool.h>
+
+enum communication_state {
+ COMMUNICATION_STATE_RECEIVE_LTTNG_MSG,
+ COMMUNICATION_STATE_RECEIVE_COMMAND_HEADER,
+ COMMUNICATION_STATE_RECEIVE_PAYLOAD,
+ COMMUNICATION_STATE_END,
+ COMMUNICATION_STATE_ERROR,
+};
+
+struct lttng_destruction_handle {
+ LTTNG_OPTIONAL(enum lttng_error_code) destruction_return_code;
+ LTTNG_OPTIONAL(enum lttng_rotation_state) rotation_state;
+ struct lttng_trace_archive_location *location;
+ 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_destruction_handle_destroy(struct lttng_destruction_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);
+ lttng_trace_archive_location_destroy(handle->location);
+ free(handle);
+}
+
+static
+struct lttng_destruction_handle *lttng_destruction_handle_create(
+ int sessiond_socket)
+{
+ int ret;
+ struct lttng_destruction_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_destruction_handle_destroy(handle);
+ return NULL;
+}
+
+static
+int handle_state_transition(struct lttng_destruction_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->destruction_return_code,
+ (enum lttng_error_code) msg->ret_code);
+ if (handle->destruction_return_code.value != LTTNG_OK) {
+ handle->communication.state = COMMUNICATION_STATE_END;
+ break;
+ } else if (msg->cmd_header_size != sizeof(struct lttcomm_session_destroy_command_header) ||
+ msg->data_size > DEFAULT_MAX_TRACE_ARCHIVE_LOCATION_PAYLOAD_SIZE) {
+ handle->communication.state = COMMUNICATION_STATE_ERROR;
+ ret = -1;
+ break;
+ }
+
+ handle->communication.state =
+ COMMUNICATION_STATE_RECEIVE_COMMAND_HEADER;
+ handle->communication.bytes_left_to_receive =
+ msg->cmd_header_size;
+ LTTNG_OPTIONAL_SET(&handle->communication.data_size,
+ msg->data_size);
+ ret = lttng_dynamic_buffer_set_size(
+ &handle->communication.buffer, 0);
+ assert(!ret);
+ break;
+ }
+ case COMMUNICATION_STATE_RECEIVE_COMMAND_HEADER:
+ {
+ const struct lttcomm_session_destroy_command_header *hdr =
+ (typeof(hdr)) handle->communication.buffer.data;
+
+ LTTNG_OPTIONAL_SET(&handle->rotation_state,
+ (enum lttng_rotation_state) hdr->rotation_state);
+ switch (handle->rotation_state.value) {
+ case LTTNG_ROTATION_STATE_COMPLETED:
+ handle->communication.state =
+ COMMUNICATION_STATE_RECEIVE_PAYLOAD;
+ handle->communication.bytes_left_to_receive =
+ LTTNG_OPTIONAL_GET(handle->communication.data_size);
+ break;
+ case LTTNG_ROTATION_STATE_ERROR:
+ case LTTNG_ROTATION_STATE_NO_ROTATION:
+ handle->communication.state = COMMUNICATION_STATE_END;
+ break;
+ default:
+ handle->communication.state = COMMUNICATION_STATE_ERROR;
+ ret = -1;
+ break;
+ }
+ break;
+ }
+ case COMMUNICATION_STATE_RECEIVE_PAYLOAD:
+ {
+ ssize_t location_ret;
+ struct lttng_trace_archive_location *location;
+ const struct lttng_buffer_view view =
+ lttng_buffer_view_from_dynamic_buffer(
+ &handle->communication.buffer, 0, -1);
+
+ location_ret = lttng_trace_archive_location_create_from_buffer(
+ &view, &location);
+ if (location_ret < 0) {
+ ERR("Failed to deserialize trace archive location");
+ handle->communication.state = COMMUNICATION_STATE_ERROR;
+ ret = -1;
+ break;
+ } else {
+ handle->location = location;
+ handle->communication.state = COMMUNICATION_STATE_END;
+ }
+ 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_destruction_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;
+}
+
+enum lttng_destruction_handle_status
+lttng_destruction_handle_wait_for_completion(
+ struct lttng_destruction_handle *handle, int timeout_ms)
+{
+ int ret;
+ enum lttng_destruction_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_DESTRUCTION_HANDLE_STATUS_ERROR;
+ goto end;
+ } else if (handle->communication.state == COMMUNICATION_STATE_END) {
+ status = LTTNG_DESTRUCTION_HANDLE_STATUS_COMPLETED;
+ goto end;
+ }
+ if (has_timeout) {
+ ret = lttng_clock_gettime(CLOCK_MONOTONIC, &initial_time);
+ if (ret) {
+ status = LTTNG_DESTRUCTION_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_DESTRUCTION_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_DESTRUCTION_HANDLE_STATUS_ERROR;
+ goto end;
+ }
+ } else {
+ handle->communication.state = COMMUNICATION_STATE_ERROR;
+ status = LTTNG_DESTRUCTION_HANDLE_STATUS_ERROR;
+ goto end;
+ }
+ if (!has_timeout) {
+ continue;
+ }
+
+ ret = lttng_clock_gettime(CLOCK_MONOTONIC, ¤t_time);
+ if (ret) {
+ status = LTTNG_DESTRUCTION_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_DESTRUCTION_HANDLE_STATUS_ERROR;
+ goto end;
+ }
+ DBG("%lums elapsed while waiting for session destruction 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_DESTRUCTION_HANDLE_STATUS_COMPLETED :
+ LTTNG_DESTRUCTION_HANDLE_STATUS_TIMEOUT;
+end:
+ return status;
+}
+
+enum lttng_destruction_handle_status
+lttng_destruction_handle_get_rotation_state(
+ const struct lttng_destruction_handle *handle,
+ enum lttng_rotation_state *rotation_state)
+{
+ enum lttng_destruction_handle_status status =
+ LTTNG_DESTRUCTION_HANDLE_STATUS_OK;
+
+ if (!handle->rotation_state.is_set) {
+ status = LTTNG_DESTRUCTION_HANDLE_STATUS_INVALID;
+ goto end;
+ }
+ *rotation_state = handle->rotation_state.value;
+end:
+ return status;
+}
+
+enum lttng_destruction_handle_status
+lttng_destruction_handle_get_archive_location(
+ const struct lttng_destruction_handle *handle,
+ const struct lttng_trace_archive_location **location)
+{
+ enum lttng_destruction_handle_status status =
+ LTTNG_DESTRUCTION_HANDLE_STATUS_OK;
+
+ if (!handle->location) {
+ status = LTTNG_DESTRUCTION_HANDLE_STATUS_INVALID;
+ goto end;
+ }
+ *location = handle->location;
+end:
+ return status;
+}
+
+enum lttng_destruction_handle_status
+lttng_destruction_handle_get_result(
+ const struct lttng_destruction_handle *handle,
+ enum lttng_error_code *result)
+{
+ enum lttng_destruction_handle_status status =
+ LTTNG_DESTRUCTION_HANDLE_STATUS_OK;
+
+ if (!handle->destruction_return_code.is_set) {
+ status = LTTNG_DESTRUCTION_HANDLE_STATUS_INVALID;
+ goto end;
+ }
+ *result = handle->destruction_return_code.value;
+end:
+ return status;
+}
+
+enum lttng_error_code lttng_destroy_session_ext(const char *session_name,
+ struct lttng_destruction_handle **_handle)
+{
+ int ret;
+ ssize_t comm_ret;
+ enum lttng_error_code ret_code = LTTNG_OK;
+ struct lttcomm_session_msg lsm = {
+ .cmd_type = LTTNG_DESTROY_SESSION,
+ };
+ int sessiond_socket = -1;
+ struct lttng_destruction_handle *handle = NULL;
+
+ 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_destruction_handle_create(sessiond_socket);
+ if (!handle) {
+ ret_code = LTTNG_ERR_NOMEM;
+ goto error;
+ }
+
+ comm_ret = lttcomm_send_unix_sock(sessiond_socket, &lsm, sizeof(lsm));
+ if (comm_ret < 0) {
+ ret_code = LTTNG_ERR_FATAL;
+ goto error;
+ }
+ sessiond_socket = -1;
+
+ /* Transfer the handle to the caller. */
+ if (_handle) {
+ *_handle = handle;
+ handle = NULL;
+ }
+error:
+ if (sessiond_socket >= 0) {
+ ret = close(sessiond_socket);
+ PERROR("Failed to close the LTTng session daemon connection socket");
+ }
+ if (handle) {
+ lttng_destruction_handle_destroy(handle);
+ }
+ return ret_code;
+}
int lttng_check_tracing_group(void);
+int connect_sessiond(void);
+
#endif /* LTTNG_CTL_HELPER_H */
#include <lttng/userspace-probe-internal.h>
#include <lttng/session-internal.h>
#include <lttng/session-descriptor-internal.h>
+#include <lttng/destruction-handle.h>
#include "filter/filter-ast.h"
#include "filter/filter-parser.h"
/* Socket to session daemon for communication */
-static int sessiond_socket;
+static int sessiond_socket = -1;
static char sessiond_sock_path[PATH_MAX];
/* Variables */
/*
* Connect to the LTTng session daemon.
*
- * On success, return 0. On error, return -1.
+ * On success, return the socket's file descriptor. On error, return -1.
*/
-static int connect_sessiond(void)
+LTTNG_HIDDEN int connect_sessiond(void)
{
int ret;
- /* Don't try to connect if already connected. */
- if (connected) {
- return 0;
- }
-
ret = set_session_daemon_path();
if (ret < 0) {
goto error;
goto error;
}
- sessiond_socket = ret;
- connected = 1;
-
- return 0;
+ return ret;
error:
return -1;
}
+static void reset_global_sessiond_connection_state(void)
+{
+ sessiond_socket = -1;
+ connected = 0;
+}
+
/*
* Clean disconnect from the session daemon.
*
if (connected) {
ret = lttcomm_close_unix_sock(sessiond_socket);
- sessiond_socket = 0;
- connected = 0;
+ reset_global_sessiond_connection_state();
}
return ret;
if (ret < 0) {
ret = -LTTNG_ERR_NO_SESSIOND;
goto end;
+ } else {
+ sessiond_socket = ret;
+ connected = 1;
}
/* Send command to session daemon */
return ret;
}
-/*
- * Destroy session using name.
- * Returns size of returned session payload data or a negative error code.
- */
-static
-int _lttng_destroy_session(const char *session_name)
-{
- struct lttcomm_session_msg lsm;
-
- if (session_name == NULL) {
- return -LTTNG_ERR_INVALID;
- }
-
- memset(&lsm, 0, sizeof(lsm));
- lsm.cmd_type = LTTNG_DESTROY_SESSION;
-
- lttng_ctl_copy_string(lsm.session.name, session_name,
- sizeof(lsm.session.name));
-
- return lttng_ctl_ask_sessiond(&lsm, NULL);
-}
-
/*
* Stop the session and wait for the data before destroying it
*/
int lttng_destroy_session(const char *session_name)
{
int ret;
+ enum lttng_error_code ret_code;
+ enum lttng_destruction_handle_status status;
+ struct lttng_destruction_handle *handle = NULL;
/*
- * Stop the tracing and wait for the data.
+ * Stop the tracing and wait for the data to be
+ * consumed.
*/
ret = _lttng_stop_tracing(session_name, 1);
if (ret && ret != -LTTNG_ERR_TRACE_ALREADY_STOPPED) {
goto end;
}
- ret = _lttng_destroy_session(session_name);
+ ret_code = lttng_destroy_session_ext(session_name, &handle);
+ if (ret_code != LTTNG_OK) {
+ ret = (int) -ret_code;
+ goto end;
+ }
+ assert(handle);
+
+ /* Block until the completion of the destruction of the session. */
+ status = lttng_destruction_handle_wait_for_completion(handle, -1);
+ if (status != LTTNG_DESTRUCTION_HANDLE_STATUS_COMPLETED) {
+ ret = -LTTNG_ERR_UNK;
+ goto end;
+ }
+
+ status = lttng_destruction_handle_get_result(handle, &ret_code);
+ if (status != LTTNG_DESTRUCTION_HANDLE_STATUS_OK) {
+ ret = -LTTNG_ERR_UNK;
+ goto end;
+ }
+ ret = ret_code == LTTNG_OK ? LTTNG_OK : -ret_code;
end:
+ lttng_destruction_handle_destroy(handle);
return ret;
}
*/
int lttng_destroy_session_no_wait(const char *session_name)
{
- int ret;
-
- /*
- * Stop the tracing without waiting for the data.
- * The session might already have been stopped, so just
- * skip this error.
- */
- ret = _lttng_stop_tracing(session_name, 0);
- if (ret && ret != -LTTNG_ERR_TRACE_ALREADY_STOPPED) {
- goto end;
- }
+ enum lttng_error_code ret_code;
- ret = _lttng_destroy_session(session_name);
-end:
- return ret;
+ ret_code = lttng_destroy_session_ext(session_name, NULL);
+ return ret_code == LTTNG_OK ? ret_code : -ret_code;
}
/*