From 3e3665b89947ebdd812bcfaca1090d22b34d5778 Mon Sep 17 00:00:00 2001 From: =?utf8?q?J=C3=A9r=C3=A9mie=20Galarneau?= Date: Tue, 18 Jun 2019 14:05:46 -0400 Subject: [PATCH] Fix: wait for the completion of implicit session rotations MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit A session that has been rotated within its lifetime will be rotated during its destruction to rename the last trace chunk. Currently, the liblttng-ctl library's session destruction function only waits for the data pending to indicate that all data has been consumed. This used to be sufficient, but it is now necessary to wait for the implicit session rotation to complete. The "wait" variant of the session destruction function will wait for any implicit session rotation to compete. A new lttng_destruction_handle class is introduced to allow a client to wait for the destruction of a session and obtain the location of the last trace archive produced by the implicit session rotation. Signed-off-by: Jérémie Galarneau --- include/Makefile.am | 3 +- include/lttng/destruction-handle.h | 60 ++++ include/lttng/lttng-error.h | 1 + include/lttng/rotate-internal.h | 4 +- include/lttng/session.h | 19 +- src/bin/lttng-sessiond/client.c | 75 ++-- src/bin/lttng-sessiond/cmd.c | 137 +++++++- src/bin/lttng-sessiond/cmd.h | 3 +- src/bin/lttng-sessiond/main.c | 4 +- src/bin/lttng-sessiond/session.c | 40 ++- src/bin/lttng-sessiond/session.h | 9 +- src/bin/lttng/commands/destroy.c | 2 +- src/common/defaults.h | 3 + src/common/error.c | 1 + src/common/sessiond-comm/sessiond-comm.h | 8 + src/lib/lttng-ctl/Makefile.am | 2 +- src/lib/lttng-ctl/destruction-handle.c | 429 +++++++++++++++++++++++ src/lib/lttng-ctl/lttng-ctl-helper.h | 2 + src/lib/lttng-ctl/lttng-ctl.c | 96 +++-- 19 files changed, 798 insertions(+), 100 deletions(-) create mode 100644 include/lttng/destruction-handle.h create mode 100644 src/lib/lttng-ctl/destruction-handle.c diff --git a/include/Makefile.am b/include/Makefile.am index 7f27cc82f..c86a1c5e0 100644 --- a/include/Makefile.am +++ b/include/Makefile.am @@ -84,7 +84,8 @@ lttnginclude_HEADERS = \ 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 \ diff --git a/include/lttng/destruction-handle.h b/include/lttng/destruction-handle.h new file mode 100644 index 000000000..3447314ed --- /dev/null +++ b/include/lttng/destruction-handle.h @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2019 - Jérémie Galarneau + * + * 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 +#include + +#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 */ diff --git a/include/lttng/lttng-error.h b/include/lttng/lttng-error.h index efbd06625..d6e72df98 100644 --- a/include/lttng/lttng-error.h +++ b/include/lttng/lttng-error.h @@ -172,6 +172,7 @@ enum lttng_error_code { 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 */ diff --git a/include/lttng/rotate-internal.h b/include/lttng/rotate-internal.h index c44178079..17a08dd4f 100644 --- a/include/lttng/rotate-internal.h +++ b/include/lttng/rotate-internal.h @@ -89,7 +89,9 @@ struct lttng_rotate_session_return { 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 { diff --git a/include/lttng/session.h b/include/lttng/session.h index 6c54d7e4f..8ba817913 100644 --- a/include/lttng/session.h +++ b/include/lttng/session.h @@ -1,5 +1,6 @@ /* * Copyright (C) 2014 - David Goulet + * Copyright (C) 2019 - Jérémie Galarneau * * 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, @@ -23,6 +24,7 @@ extern "C" { #endif struct lttng_session_descriptor; +struct lttng_destruction_handle; /* * Basic session information. @@ -128,10 +130,25 @@ extern int lttng_create_session_live(const char *name, const char *url, * * 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. diff --git a/src/bin/lttng-sessiond/client.c b/src/bin/lttng-sessiond/client.c index 4565a18c3..aea667cd1 100644 --- a/src/bin/lttng-sessiond/client.c +++ b/src/bin/lttng-sessiond/client.c @@ -706,10 +706,12 @@ static int send_unix_sock(int sock, void *buf, size_t len) * 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; @@ -1108,13 +1110,13 @@ skip_domain: 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; @@ -1167,7 +1169,7 @@ 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; @@ -1225,7 +1227,7 @@ error_add_context: 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"); @@ -1256,7 +1258,7 @@ error_add_context: /* 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"); @@ -1289,7 +1291,7 @@ error_add_context: /* 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; @@ -1323,7 +1325,7 @@ error_add_context: 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); @@ -1476,7 +1478,7 @@ error_add_context: /* 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; @@ -1521,7 +1523,8 @@ error_add_context: case LTTNG_DESTROY_SESSION: { ret = cmd_destroy_session(cmd_ctx->session, - notification_thread_handle); + notification_thread_handle, + sock); break; } case LTTNG_LIST_DOMAINS: @@ -1760,7 +1763,7 @@ error_add_context: 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; } @@ -1809,13 +1812,13 @@ error_add_context: } 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; } @@ -1934,6 +1937,7 @@ setup_error: 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(); @@ -2178,14 +2182,16 @@ static void *thread_manage_clients(void *data) * 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 @@ -2211,21 +2217,24 @@ static void *thread_manage_clients(void *data) 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); diff --git a/src/bin/lttng-sessiond/cmd.c b/src/bin/lttng-sessiond/cmd.c index b5bde4a9d..84d6fd9b3 100644 --- a/src/bin/lttng-sessiond/cmd.c +++ b/src/bin/lttng-sessiond/cmd.c @@ -34,6 +34,7 @@ #include #include #include +#include #include #include #include @@ -69,6 +70,11 @@ /* 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); /* @@ -3003,20 +3009,127 @@ error: 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( @@ -3039,7 +3152,10 @@ int cmd_destroy_session(struct ltt_session *session, 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]) { /* @@ -3101,8 +3217,19 @@ int cmd_destroy_session(struct ltt_session *session, * _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; } diff --git a/src/bin/lttng-sessiond/cmd.h b/src/bin/lttng-sessiond/cmd.h index 0821fb33d..21a12d51f 100644 --- a/src/bin/lttng-sessiond/cmd.h +++ b/src/bin/lttng-sessiond/cmd.h @@ -47,7 +47,8 @@ void cmd_init(void); 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, diff --git a/src/bin/lttng-sessiond/main.c b/src/bin/lttng-sessiond/main.c index dd5cee4bd..ac7f7c910 100644 --- a/src/bin/lttng-sessiond/main.c +++ b/src/bin/lttng-sessiond/main.c @@ -1335,8 +1335,8 @@ static void destroy_all_sessions_and_wait(void) 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); diff --git a/src/bin/lttng-sessiond/session.c b/src/bin/lttng-sessiond/session.c index abac2404f..4a15c9a9a 100644 --- a/src/bin/lttng-sessiond/session.c +++ b/src/bin/lttng-sessiond/session.c @@ -40,6 +40,11 @@ #include "trace-ust.h" #include "timer.h" +struct ltt_session_destroy_notifier_element { + ltt_session_destroy_notifier notifier; + void *user_data; +}; + /* * NOTES: * @@ -245,7 +250,7 @@ void session_get_net_consumer_ports(const struct ltt_session *session, * 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; @@ -737,6 +742,22 @@ int session_set_trace_chunk(struct ltt_session *session, 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) { @@ -749,6 +770,9 @@ 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) { @@ -852,6 +876,18 @@ void session_destroy(struct ltt_session *session) 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 @@ -940,6 +976,8 @@ enum lttng_error_code session_create(const char *name, uid_t uid, gid_t gid, 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); diff --git a/src/bin/lttng-sessiond/session.h b/src/bin/lttng-sessiond/session.h index 1cea52a61..0b4746cb5 100644 --- a/src/bin/lttng-sessiond/session.h +++ b/src/bin/lttng-sessiond/session.h @@ -23,6 +23,7 @@ #include #include +#include #include #include #include @@ -33,6 +34,9 @@ struct ltt_ust_session; +typedef void (*ltt_session_destroy_notifier)(const struct ltt_session *session, + void *user_data); + /* * Tracing session list * @@ -178,6 +182,7 @@ struct ltt_session { 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 */ @@ -190,6 +195,8 @@ void session_unlock(struct ltt_session *session); 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); @@ -202,7 +209,7 @@ void session_get_net_consumer_ports( 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); diff --git a/src/bin/lttng/commands/destroy.c b/src/bin/lttng/commands/destroy.c index 6878aaa0d..77acb180f 100644 --- a/src/bin/lttng/commands/destroy.c +++ b/src/bin/lttng/commands/destroy.c @@ -113,7 +113,7 @@ static int destroy_session(struct lttng_session *session) print_session_stats(session->name); } - ret = lttng_destroy_session_no_wait(session->name); + ret = lttng_destroy_session(session->name); if (ret < 0) { goto error; } diff --git a/src/common/defaults.h b/src/common/defaults.h index 5df5127b3..08bbb64b9 100644 --- a/src/common/defaults.h +++ b/src/common/defaults.h @@ -333,6 +333,9 @@ /* 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 diff --git a/src/common/error.c b/src/common/error.c index 2ba62d7be..a87c53a89 100644 --- a/src/common/error.c +++ b/src/common/error.c @@ -217,6 +217,7 @@ static const char *error_string_array[] = { [ 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" diff --git a/src/common/sessiond-comm/sessiond-comm.h b/src/common/sessiond-comm/sessiond-comm.h index d673c6e19..3b0093bfb 100644 --- a/src/common/sessiond-comm/sessiond-comm.h +++ b/src/common/sessiond-comm/sessiond-comm.h @@ -445,6 +445,14 @@ struct lttcomm_event_extended_header { 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. */ diff --git a/src/lib/lttng-ctl/Makefile.am b/src/lib/lttng-ctl/Makefile.am index d18328e4b..32d0aff54 100644 --- a/src/lib/lttng-ctl/Makefile.am +++ b/src/lib/lttng-ctl/Makefile.am @@ -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 + channel.c rotate.c event.c destruction-handle.c liblttng_ctl_la_LDFLAGS = \ $(LT_NO_UNDEFINED) diff --git a/src/lib/lttng-ctl/destruction-handle.c b/src/lib/lttng-ctl/destruction-handle.c new file mode 100644 index 000000000..e22deaa93 --- /dev/null +++ b/src/lib/lttng-ctl/destruction-handle.c @@ -0,0 +1,429 @@ +/* + * Copyright (C) 2019 - Jérémie Galarneau + * + * 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 +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "lttng-ctl-helper.h" + +#include + +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; +} diff --git a/src/lib/lttng-ctl/lttng-ctl-helper.h b/src/lib/lttng-ctl/lttng-ctl-helper.h index 9b50aacf7..4341ee6b5 100644 --- a/src/lib/lttng-ctl/lttng-ctl-helper.h +++ b/src/lib/lttng-ctl/lttng-ctl-helper.h @@ -79,4 +79,6 @@ int lttng_ctl_ask_sessiond(struct lttcomm_session_msg *lsm, void **buf) int lttng_check_tracing_group(void); +int connect_sessiond(void); + #endif /* LTTNG_CTL_HELPER_H */ diff --git a/src/lib/lttng-ctl/lttng-ctl.c b/src/lib/lttng-ctl/lttng-ctl.c index 20dca8132..8c52c456d 100644 --- a/src/lib/lttng-ctl/lttng-ctl.c +++ b/src/lib/lttng-ctl/lttng-ctl.c @@ -45,6 +45,7 @@ #include #include #include +#include #include "filter/filter-ast.h" #include "filter/filter-parser.h" @@ -68,7 +69,7 @@ do { \ /* Socket to session daemon for communication */ -static int sessiond_socket; +static int sessiond_socket = -1; static char sessiond_sock_path[PATH_MAX]; /* Variables */ @@ -423,17 +424,12 @@ error: /* * 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; @@ -445,15 +441,18 @@ static int connect_sessiond(void) 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. * @@ -465,8 +464,7 @@ static int disconnect_sessiond(void) if (connected) { ret = lttcomm_close_unix_sock(sessiond_socket); - sessiond_socket = 0; - connected = 0; + reset_global_sessiond_connection_state(); } return ret; @@ -541,6 +539,9 @@ int lttng_ctl_ask_sessiond_fds_varlen(struct lttcomm_session_msg *lsm, if (ret < 0) { ret = -LTTNG_ERR_NO_SESSIOND; goto end; + } else { + sessiond_socket = ret; + connected = 1; } /* Send command to session daemon */ @@ -1993,45 +1994,47 @@ end: 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; } @@ -2040,21 +2043,10 @@ end: */ 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; } /* -- 2.34.1